Giter Site home page Giter Site logo

puget's Introduction

Puget

Build Status Coverage Status cljdoc

Puget is a Clojure library for printing Clojure and EDN values. Under the hood, Puget formats data into print documents and uses fipp to render them.

output example

Puget offers several features which set it apart from FIPP and Clojure's native pretty-printing functions. Syntax coloring is the most widely used, followed by canonical printing. Custom value rendering is supported using type dispatch to select print handlers.

Installation

Puget releases are published on Clojars. To use the latest version with Leiningen, add the following dependency to your project definition:

Clojars Project

See Whidbey for nREPL and Leiningen integration.

Usage

Puget's printing is controlled by a map of options which configure things like print width, sorting mode, color scheme and style, whether to print metadata, and so on. The default options are held in the dynamic var puget.printer/*options*, which can be bound using with-options. See the puget.printer namespace documentation for the full set of options.

These options are used to construct a printer to render values with. pprint and pprint-str will automatically create a PrettyPrinter record from the current and passed options, or you can use pretty-printer or canonical-printer to construct one manually. render-out and render-str take a printer and a value if you need maximum control over the printing.

Syntax Coloring

Puget's first major feature is colorizing the printed data by rendering it with embedded markup. Different syntax elements are given different colors to make the printed output much easier for humans to parse. This is similar to syntax highlighting, but much easier since the code works directly with the data instead of parsing it from text!

Elements are mapped to color codes by the :color-scheme option. The :print-color option can be set to enable colorization using the with-color macro - alternately, the cprint function always prints with colored output enabled.

Puget supports three different kinds of color markup:

  • :ansi (the default) adds ANSI color escapes for terminal outputs.
  • :html-inline adds HTML span elements with inline style attributes.
  • :html-classes adds span elements with semantic class attributes.

See the puget.color.ansi namespace for the available ANSI color styles which can be applied to syntax elements.

Canonical Representation

Puget also provides canonical serialization of data. In most cases, if two data values are equal, they should be printed identically. This is important for when the printed data is hashed, but it also makes it easier to process maps and other structures with similar contents.

Puget uses the arrangement library to sort the values in sets and the keys in maps so they are always printed the same way. This can be disabled with the :sort-keys option, or enabled only for collections under a certain size.

Most printing is done with the PrettyPrinter class, but the library also offers the CanonicalPrinter for serializing data in a stricter (and more compact) fashion.

=> (require '[puget.printer :as puget])

=> (puget/pprint #{'x :a :z 3 1.0})
#{1.0 3 :a :z x}

=> (def usd (java.util.Currency/getInstance "USD"))
#'user/usd

=> (puget/pprint usd)
#<java.util.Currency@4cc4ee24 USD>

=> (puget/render-out (puget/canonical-printer) usd)
; IllegalArgumentException: No defined representation for class java.util.Currency: USD

Type Extensions

All of Clojure's primitive types are given their standard print representations. To handle non-standard data types, Puget supports a mechanism to dispatch to custom print handlers. These take precedence over the normal rendering mechanisms.

This can be used to provide an EDN tagged-literal representation for certain types, or just avoid trying to pretty-print types which the engine struggles with (such as Datomic database values).

Before rendering a value, the printer checks for a :print-handlers function. If available, it is called with the type of the value to be printed. If the lookup returns a handler, that function is called with the value and the result is used as the rendered format of the value.

The puget.dispatch namespace has functions to help build handler lookup functions. The inheritance-lookup constructor provides semantics similar to Clojure's multimethod dispatch.

As an example, extending #inst formatting to clj-time's DateTime:

=> (require '[clj-time.core :as t]
            '[clj-time.format :as f])

=> (puget/pprint (t/now))
#<org.joda.time.DateTime 2014-05-14T00:58:40.922Z>

=> (def time-handlers
     {org.joda.time.DateTime
      (puget/tagged-handler
        'inst
        (partial f/unparse (f/formatters :date-time)))})
#'user/time-handlers

=> (puget/pprint (t/now) {:print-handlers time-handlers})
#inst "2014-05-14T01:05:53.885Z"

If no handler is specified for a given type and it's not a built-in EDN type, Puget refers to the :print-fallback option, which must be one of:

  • :pretty (the default) prints a colored representation of the unknown value (not valid EDN!).
  • :print falls back to the standard pr-str representation.
  • :error throws an exception for types with no defined representation.
  • A function which will be called with the printer and the unknown value to render, returning the formatted value.

License

This is free and unencumbered software released into the public domain. See the UNLICENSE file for more information.

puget's People

Contributors

brandonbloom avatar danielcompton avatar dhruvbhatia avatar duck1123 avatar gitter-badger avatar greglook avatar ivan avatar lread avatar niclasnilsson avatar philomates avatar scgilardi avatar tirkarthi avatar tobyclemson avatar vemv avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

puget's Issues

Any mechanism for dispatching on :type metadata?

In a few different contexts I've taken advantage of the fact that clojure's print-method is a multimethod that allows me to customize any IObj by simply setting its :type metadata. E.g., I use it here to cause a var to print with pertinent information.

Using puget at my repl disrupts this behavior, and since TaggedValue is a protocol I can't use it to extend objects this way.

So I have two questions:

  1. Is there some other way for getting metadata-level custom printing that I'm missing? Or is converting TaggedValue to a multimethod the only sensible way to do this?
  2. Is the change in the previous point something you would be interested in, or should I just use a personal fork of the project?

Unidiomatic representation of namespaced maps

Puget represents namespaced maps as:

#:foo {:x 1, :y 2}

But Clojure's printer omits the space between the namespace and the map:

#:foo{:x 1, :y 2}

Puget should probably follow the convention that's used by the Clojure printer.

:seq-limit for vectors?

I really like this library and use it to color code REPL output. However, I have one problem that I'd be interested to hear the reasoning behind.

:seq-limit only works for seqs (obvious from the name), but that means that vectors, maps and other things are still fully printed, even if they are huge. If I set print-length in a standard clojure REPL, if works for all sorts of collections (see below). Is there a specific reason that puget doesn't have a similar thing, or that :seq-limit could be applied to other collections (or to have be :coll-limit or similar if needed)? Is it something you'd be interested in a pull request for, and if so, do you have any preference of how to solve it?

user=> (set! *print-length* 3)
3
user=> (range 10)
(0 1 2 ...)
user=> [1 2 3 4 5 6 7 8 9]
[1 2 3 ...]
user=> {:a 1 :b 2 :c 3 :d 4 :e 5}
{:a 1, :b 2, :c 3, ...}```

#bin and #uri violate edn and Clojure specs for tags

This probably sounds really nitpicky, but it was actually causing some confusion on our dev team; several of our devs thought that #bin and #uri were just standard Clojure, not realizing they came from puget.

The edn spec says:

rules for tags

Tag symbols without a prefix are reserved by edn for built-ins defined using the tag system.

User tags must contain a prefix component, which must be owned by the user (e.g. trademark or domain) or known unique in the communication context.

The [Clojure reference documentation](http://clojure.org/reader#The Reader--Tagged Literals) seems to back this up:

Reader tags without namespace qualifiers are reserved for Clojure.

Granted, no one else seems to take this very seriously either. spyscope, datomic, and flatland.ordered use "spy", "db" (mostly), and "ordered" respectively; to say that these are "known unique" seems questionable to me at best. But for the most part (with one exception on datomic's part), they at least have a prefix which gives some clue as to where it came from and that it's not standard Clojure.

cprint-str throws exception for high values of :width on certain inputs

I'm occasionally encountering an exception when calling cprint-str with (somewhat) complex data structures and high values of :width.

Some observations:

  • The values of the data itself doesn't matter โ€” I've replaced the actual values in the example below.
  • The problem only happens once the input data reaches a certain size. If i remove elements from the example below, the exception goes away.
  • The exception is thrown in clojure.core.rrb-vector, after Puget calls out to fipp.
  • The bug is present in puget 1.0.2, but I've reproduced it with puget, fipp, and clojure.core.rrb-vector checked out to their respective latest commits on master.

I suspect that this is either a problem in the data structure puget sends to fipp, or with fipp or rrb-vector themselves. A good first step might be to verify that the data sent to fipp is in fact correct.

Example:

(def bad-data {:a 0, :b {}, :c {}, :d 0, :e {:a 0}, :f [0 0], :g 0, :h 0, :i {:a 0, :b 0, :c [{:a 0, :b 0, :c {:a [{:a 0, :b 0} {:a 0, :b 0} {:a 0, :b 0}], :b 0, :c 0, :d [{:a 0, :b 0}]}, :d 0, :e 0, :f 0, :g 0, :h 0, :i 0, :j 0, :k 0, :l 0} {:a 0, :b 0, :c 0, :d 0, :e 0, :f 0, :g 0, :h {:a [{:a 0, :b 0} {:a 0, :b 0} {:a 0, :b 0} {:a 0, :b 0}], :b 0, :c 0, :d [{:a 0, :b 0}]}, :i 0, :j 0, :k 0, :l 0} {:a {:a [{:a 0, :b 0} {:a 0, :b 0} {:a 0, :b 0} {:a 0, :b 0}], :b 0, :c 0, :d [{:a 0, :b 0}]}, :b 0, :c 0, :d 0, :e 0, :f 0, :g 0, :h 0, :i 0, :j 0, :k 0, :l 0} {:a 0, :b 0, :c 0, :d 0, :e 0, :f 0, :g 0, :h {:a [{:a 0, :b 0} {:a 0, :b 0} {:a 0, :b 0} {:a 0, :b 0}], :b 0, :c 0, :d [{:a 0, :b 0}]}, :i 0, :j 0, :k 0, :l 0} {:a 0, :b 0, :c 0, :d 0, :e 0, :f 0, :g {:a [{:a 0, :b 0} {:a 0, :b 0} {:a 0, :b 0} {:a 0, :b 0}], :b 0, :c 0, :d [{:a 0, :b 0}]}, :h 0, :i 0, :j 0, :k 0, :l 0} {:a 0, :b 0, :c 0, :d 0, :e 0, :f {:a [{:a 0, :b 0} {:a 0, :b 0} {:a 0, :b 0}], :b 0, :c 0, :d [{:a 0, :b 0}]}, :g 0, :h 0, :i 0, :j 0, :k 0, :l 0} {:a 0, :b 0} {:a 0, :b {:a {:a 0, :b 0, :c 0, :d 0}}, :c {}, :d 0}]}, :j 0, :k 0, :l 0, :m 0})
(puget.printer/pprint-str bad-data)               ; works
(puget.printer/cprint-str bad-data)               ; works
(puget.printer/pprint-str bad-data {:width 2000}) ; works
(puget.printer/cprint-str bad-data {:width 2000}) ; throws ClassCastException: clojure.lang.PersistentVector$Node cannot be cast to [I

The exception thrown:

#error {
 :cause clojure.lang.PersistentVector$Node cannot be cast to [I
 :via
 [{:type java.lang.ClassCastException
   :message clojure.lang.PersistentVector$Node cannot be cast to [I
   :at [clojure.lang.Numbers ints Numbers.java 1396]}]
 :trace
 [[clojure.lang.Numbers ints Numbers.java 1396]
  [clojure.core.rrb_vector.rrbt$slot_count invokeStatic rrbt.clj 1084]
  [clojure.core.rrb_vector.rrbt$slot_count invoke rrbt.clj 1078]
  [clojure.core.rrb_vector.rrbt$subtree_branch_count invokeStatic rrbt.clj 1103]
  [clojure.core.rrb_vector.rrbt$subtree_branch_count invoke rrbt.clj 1087]
  [clojure.core.rrb_vector.rrbt$rebalance invokeStatic rrbt.clj 1190]
  [clojure.core.rrb_vector.rrbt$rebalance invoke rrbt.clj 1182]
  [clojure.core.rrb_vector.rrbt$zippath invokeStatic rrbt.clj 1290]
  [clojure.core.rrb_vector.rrbt$zippath invoke rrbt.clj 1267]
  [clojure.core.rrb_vector.rrbt$splice_rrbts invokeStatic rrbt.clj 1364]
  [clojure.core.rrb_vector.rrbt$splice_rrbts invoke rrbt.clj 1334]
  [clojure.core.rrb_vector.rrbt.Vector splicev rrbt.clj 928]
  [clojure.core.rrb_vector.interop$eval1038$fn__1039 invoke interop.clj 30]
  [clojure.core.rrb_vector.protocols$eval471$fn__472$G__462__479 invoke protocols.clj 3]
  [clojure.core.rrb_vector$catvec invokeStatic rrb_vector.clj 54]
  [clojure.core.rrb_vector$catvec invoke rrb_vector.clj 45]
  [fipp.deque$conjlr invokeStatic deque.cljc 16]
  [fipp.deque$conjlr invoke deque.cljc 15]
  [fipp.engine$annotate_begins$fn__1166$fn__1168 invoke engine.cljc 119]
  [fipp.engine$annotate_rights$fn__1159 invoke engine.cljc 83]
  [clojure.lang.ArrayChunk reduce ArrayChunk.java 58]
  [clojure.core.protocols$fn__6750 invokeStatic protocols.clj 136]
  [clojure.core.protocols$fn__6750 invoke protocols.clj 124]
  [clojure.core.protocols$fn__6710$G__6705__6719 invoke protocols.clj 19]
  [clojure.core.protocols$seq_reduce invokeStatic protocols.clj 31]
  [clojure.core.protocols$fn__6738 invokeStatic protocols.clj 75]
  [clojure.core.protocols$fn__6738 invoke protocols.clj 75]
  [clojure.core.protocols$fn__6684$G__6679__6697 invoke protocols.clj 13]
  [clojure.core$transduce invokeStatic core.clj 6601]
  [clojure.core.Eduction reduce core.clj 7360]
  [clojure.core$reduce invokeStatic core.clj 6544]
  [clojure.core$run_BANG_ invokeStatic core.clj 7388]
  [clojure.core$run_BANG_ invoke core.clj 7388]
  [fipp.engine$pprint_document invokeStatic engine.cljc 239]
  [fipp.engine$pprint_document invoke engine.cljc 232]
  [puget.printer$render_out invokeStatic printer.clj 622]
  [puget.printer$render_out invoke printer.clj 618]
  [puget.printer$render_str$fn__1743 invoke printer.clj 633]
  [puget.printer$render_str invokeStatic printer.clj 632]
  [puget.printer$render_str invoke printer.clj 627]
  [puget.printer$pprint_str invokeStatic printer.clj 650]
  [puget.printer$pprint_str invoke printer.clj 645]
  [puget.printer$cprint_str invokeStatic printer.clj 666]
  [puget.printer$cprint_str invoke printer.clj 661]
  ...]}

Allow always-multiline maps

Would it be difficult to add an option that forces map keys to always be on different lines?

This sort of behavior is the default for some tools, such as jq:

$ echo '{"foo":1,"bar":2}' | jq .
{
  "bar": 2,
  "foo": 1
}

bounded lenient canonicalization

The total ordering is pretty nice, but it has a cost: Printing larger structures is much slower. Although Fipp promises linear time & bounded space, Puget's sorting cannot. Occasionally, I print a pretty large structure with Puget/Whidbey and have a little flashback to a dark time before I wrote fipp.

Incomplete design thought: There should be an option for lenient canonicalization, which recovers the performance promises.

For all counted? collections, we could check if (> (count coll) limit) and, if so, don't sort bother that structure. A reasonable default limit would be some scalar factor (0.5 to 2 or so) of Fipp's print width, which is also used internally for bounding the measurement queue.

Sorted collections are re-sorted

This collection prints as #{1 2 3} but seqs as (3 2 1): (sorted-set-by > 1 2 3).

I have twice in my life lost fifteen minutes trying to figure out why my sorted collection wasn't behaving the way I thought it should.

The documentation states that all collections get sorted when that option is turned on, but I feel like it would be better if only applied to unsorted collections.

GraalVM binary

Are you interested in creating a GraalVM binary much like zprint has done, so you can use this lib from the command line?

I would be happy to do it myself if you're not interested in it. Not sure if I could use the name puget for that binary?

The use case for this would be to pipe any program that outputs EDN or Clojure code into that binary to get colorized output.

Also see babashka/babashka#620

Metadata doesn't respect current `:align`

I was playing around with writing a custom printer for debug logging. My document looks something like

[:group
 (when ns-symb
   [:span
    ns-symb
    [:text " "]])
 (into [:align] forms)]

If I print this with :print-meta false things align the way I'd like:

honeysql2 Compiling Honey SQL 2 with options
          {:dialect :toucan2.test/current-db.dialect, :quoted true, :quoted-snake true}
honeysql2 => ["SELECT * FROM \"PEOPLE\" WHERE \"NAME\" = ?" "Cam"]
pipeline In
         toucan2.pipeline/transduce-execute with dispatch value
         [:toucan.query-type/select.instances :toucan2.test/people :toucan.map-backend/honeysql2]
pipeline Execute ["SELECT * FROM \"PEOPLE\" WHERE \"NAME\" = ?" "Cam"]

However if I enable :print-meta anything with metadata is printed at the beginning of the line, not respecting the :indent:

honeysql2 Compiling Honey SQL 2 with options
          {:dialect :toucan2.test/current-db.dialect, :quoted true, :quoted-snake true}
honeysql2 => ["SELECT * FROM \"PEOPLE\" WHERE \"NAME\" = ?" "Cam"]
pipeline In
         toucan2.pipeline/transduce-execute with dispatch value
         [:toucan.query-type/select.instances :toucan2.test/people :toucan.map-backend/honeysql2]
pipeline Execute
^{:type :toucan.map-backend/honeysql2, :toucan2.pipeline/parsed-args {}}
["SELECT * FROM \"PEOPLE\" WHERE \"NAME\" = ?" "Cam"]

Not sure if it makes a difference but I was using the colorized printer.

cannot work with core.cache

puget.print/pprint failes when tried to print core.cache object.
It because you cannot sort Cache type.

You can reproduce with below.

(require '[leiningen.exec :refer [deps])
(deps '[[org.clojure/core.cache "0.6.4"]
        [mvxcvi/puget "0.8.1"]])
(require '[clojure.core.cache :as cache])
(require '[puget.printer :as puget])
(def c (cache/ttl-cache-factory {:a 1}))
(prn c)
(puget/cprint c)

Migrate references to java.util.TimeZone to java.time.*

@greglook I'm looking into making fipp and puget compatible with babashka (running them as libraries in the interpreter). The reason is that developer tooling like hashp makes use of fipp and puget and it would be cool if we could use that in babsshka script development.

Babashka prefers the usage of java.time above any other Date/Time classes and to keep the binary lean, I try to avoid including other classes.

I made one commit here in fipp (which @brandonbloom accepted gracefully) as a first step towards bb compatibility:

brandonbloom/fipp@efdf87f

A similar commit is possible using the following logic, replacing this:

   java.util.Date
   (tagged-handler
     'inst
     #(-> "yyyy-MM-dd'T'HH:mm:ss.SSS-00:00"
          (java.text.SimpleDateFormat.)
          (doto (.setTimeZone (java.util.TimeZone/getTimeZone "GMT")))
          (.format ^java.util.Date %)))

with the ingredients from:

(def date-pattern (DateTimeFormatter/ofPattern "yyyy-MM-dd'T'HH:mm:ss.SSS-00:00"))

Date
  (-edn [x]
    (let [dt (-> x .toInstant (.atZone (ZoneId/of "GMT")))
          s (.format dt date-pattern)]
      (tagged-literal 'inst s))))

I've yet to make a proper PR but will do so when you express interest in receiving that.

Puget doesn't respect overriden clojure.core/print-method for records

In several of my libraries, I have created custom print behavior for my data structures. I do this by implementing clojure.core/print-method for that data structure, which takes care of pr/print and then I add a method for clojure.core/pprint.

For example:

(defrecord Not [literal])

(defmethod clojure.core/print-method Not [x writer]
  (binding [*out* writer]
    (print "(! ")
    (pr (:literal x))
    (print ")")))

(. clojure.pprint/simple-dispatch addMethod Not #(clojure.core/print-method % *out*))

With the rise in popularity of puget as the printer of choice for cider, I'm finding it inconvenient that puget ignores my hooks into clojure's printing system, and continues to print records in its own default way.

Would you consider a pull-request that changes visit-record to first check whether print-method is implemented for the record, and when it is, use pr-handler instead?

Is there another good way to handle this?

colors start at beginning of line instead of beginning of element

Hello! First, thanks so much for puget, it is a wonderful library!

While experimenting with using background colors with puget, I noticed an interesting behavior.

If I cprint with the default :color-scheme,

(require '[puget.printer :as puget])

(def t [nil true \space "string"
        {:omega 123N :alpha '(func x y) :gamma 3.14159}
        #{\a "heterogeneous" :set}
        (java.util.Currency/getInstance "USD")
        (java.util.Date.)
        (java.util.UUID/randomUUID)])

(puget/cprint t)

I get similar results to the README screenshot:
image

But if I try background colors:

(defn bg-cprint [ expr ]
  (puget/cprint expr
                {:color-scheme
                 {:delimiter [:black :bold :bg-red]
                  :tag       [:black :bold :bg-red]
                  :nil       [:white :bold :bg-black]
                  :boolean   [:black :bg-green]
                  :number    [:black :bg-cyan]
                  :string    [:black :bold :bg-magenta]
                  :character [:black :bold :bg-magenta]
                  :keyword   [:black :bold :bg-yellow]
                  :symbol    nil
                  :function-symbol [:black :bold :bg-blue]
                  :class-delimiter [:black :bg-blue]
                  :class-name      [:black :bold :bg-blue]}}))
(bg-cprint t)

notice that the color starts at the beginning of the line rather than the beginning of the element:
image

A nested expression:

(bg-cprint ["item1"
            ["item2"
             ["item 3"
              ["item 4"
               ["item5"
                ["item6"
                 ["item7"
                  ["item8"
                   ["item9"
                    ["item10"
                     ["item11"
                      ["item12"
                       ["item13"
                        ["item14"
                         ["item15"]]]]]]]]]]]]]]])

Illustrates the behavior more clearly:
image

I don't expect it would be terribly hard to start coloring at the beginning of an element rather than the beginning of the line. I am happy to take a crack at a PR if that would be helpful.

I noticed this while exploring coloring schemes here lambdaisland/deep-diff2#14

Java object cprint does not match README screenshot

Hey there!

While working on unit tests for #44, I noticed that:

(require '[puget.printer :as printer])
(printer/cprint (java.util.Currency/getInstance "USD"))

I get the following result:
image

Which does not match the format shown in the README screenshot:
image

I don't know what the expected/desired behavior is, perhaps the README screenshot just needs an update?

Print namespaced maps using #:

In Clojure 1.9, Clojure's default pretty printing automatically uses the new #:prefix{:key ...} syntax when all keys in a map are keywords and share a namespace.

user=> (pr-str {:a/b 1})
"#:a{:b 1}"
user=> (pr-str {:a/b 1 :b/c 2})
"{:a/b 1, :b/c 2}"
user=> (puget/render-str (puget/canonical-printer) {:a/b 1})
"{:a/b 1}"

It would be cool if puget printers supported this because it would more compactly express the maps in some cases. Making this behavior "opt-in" would probably be best, to ensure that the canonical printer (by default) does not start emitting data differently unless you explicitly tell it to. Something like:

(puget/canonical-printer {:namespaced-maps true})

Error on load

I get this on load, in Clojure 1.9.0-alpha13:

java.lang.IllegalArgumentException: No implementation of method: :visit-nil of protocol: #'fipp.visit/IVisitor found for class: puget.printer.PrettyPrinter

I haven't found any reference to this around; is it something you've seen or know how to work around?

tagged-literal warnings with Clojure 1.7.0-beta1

I use ultra. I get these warnings with [mvxcvi/puget "0.7.1"] with [org.clojure/clojure "1.7.0-beta1"]:

WARNING: tagged-literal already refers to: #'clojure.core/tagged-literal in namespace: puget.data, being replaced by: #'puget.data/tagged-literal
WARNING: tagged-literal? already refers to: #'clojure.core/tagged-literal? in namespace: puget.data, being replaced by: #'puget.data/tagged-literal?

Support Clojure 1.7

Hi Greg,

Clojure 1.7 has some cool new stuff, like tagged literal objects, which affect printing. I've done some major refactors to Fipp's built in Edn printer to make it 1.7-friendly. Work in progress is over on this branch here: https://github.com/brandonbloom/fipp/tree/clj17

I'd love for Puget and Whidbey to support Clojure 1.7 as well. I haven't taken a deep look at Pudget since my Fipp refactors, but I figured I should give you a heads up that I will soon. If I have enough time this weekend, I'll send you a pull request. If I don't find the time, or you just want to make the changes yourself, I can point you in the right direction.

There's a new EdnPrinter type, which implements an IVisitor interface. The visitor will convert the open set of host objects in to the closed set of edn-serializable (plus extra) data types. Any non-printable data will be shoved in to one of the shiny new clojure.lang.TaggedLiteral types. I'd love for Pudget to leverage this visitor, so that its code can focus on value-add, rather than every printer having to recreate the various print-method instances in Clojure 1.7.

Cheers,
Brandon

sort-keys missing from 0.9.0

Looks like the latest upgrades for Clojure 1.7 and the new Fipp broke this.

sort-keys was originally discussed here: #12

In the past, the default was "always sort", but for interactive use, I think something around the height of one terminal screen (100 to 200) is a better default.

ClojureScript support?

Hello guys,
I am trying to include the new puget 0.9.2 in a ClojureScript project with no luck.
I tried to downgrade to 0.8.1 but still I am not able to see puget in my classpath.
It always throws the classic no namespace found printer.puger...bla bla .cjlc ...bla

My project.clj:

:dependencies [[org.clojure/clojure "1.7.0"]
                 [org.clojure/clojurescript "1.7.145"]
                 [reagent "0.5.1"]]

  :plugins [[lein-cljsbuild "1.1.0"]
            [lein-codox "0.9.0"]
            [lein-simpleton "1.4.0-SNAPSHOT"]]

The profile.clj is empty.

Very weird, what can it be? Does puget modify the classpath at all?

consl does not exist, compiling:(fipp/printer.clj:1:1)

On calling :require [fipp.printer] in the puget.printer namespace declaration, an IllegalAccessError is thrown, saying: consl does not exist, compiling:(fipp/printer.clj:1:1).

This is because clojure.data.finger-tree's consl function is now conjl, with the same argument order.

I realize this is really a fipp issue, but it affects puget as well, obviously.

Puget requires a large amount of memory when printing large collections

Minimal reproduction: (puget.printer/pprint (repeatedly 500000 #(rand-int 1000000000)))

When I run this with -Xmx200m and watch GC logs, I can see the memory usage gradually growing, and the program slows down as it approaches the maximum usage; with -Xmx500m it has enough headroom to finish in a more reasonable amount of time.

I'm fairly confident this is due to how this line interacts with the four-element vector produced here. In particular, because the vector is chunked, the map inside the mapcat will retain a reference to the third element (the large collection).

As evidence for this explanation, this change to fipp (unchunking the sequence) seems to fix it:

diff --git a/src/fipp/engine.cljc b/src/fipp/engine.cljc
index 8e6266d..9904a0e 100644
--- a/src/fipp/engine.cljc
+++ b/src/fipp/engine.cljc
@@ -12,7 +12,7 @@
 (defn serialize [doc]
   (cond
     (nil? doc) nil
-    (seq? doc) (mapcat serialize doc)
+    (seq? doc) (mapcat serialize (take 1e100 doc))
     (string? doc) [{:op :text, :text doc}]
     (keyword? doc) (serialize-node [doc])

I do not know what a clean fix for this would be. I'm not sure we can make the above change to fipp without potentially sacrificing performance in the farther-down-the-stack case where it's processing an actual large sequence, rather than a vector containing a large sequence as an element. And I can't think of anything that puget could do to cause fipp to behave differently.

non-global-extensibility

This is basically the exact same issue as dakrone/cheshire#77 (and affects virtually every other clojure serialization library I've ever used) so I won't elaborate further on the problem statement.

I'm thinking a solution would be another option to the pprint function, which would be a function that accepts a value to be pretty-printed and either returns {:tag __ :value __} or nil, and it would be checked before checking the global protocol (and perhaps before checking built-in functionality as well, though that would probably be a performance concern and I don't know of any use case for it).

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.