Changelogs » Jsonnet

PyUp Safety actively tracks 268,345 Python packages for vulnerabilities and notifies you when to upgrade.

















One major change:  Import paths now are properly escaped.  This was originally an oversight and went unnoticed because people don't usually use backslashes in their paths.  On Windows, we recommend using the verbatim string literals to avoid having to escape the path separator, e.g., `import "c:\foo.jsonnet"`.
  Remaining changes are minor:
  * Allow formatting of multiple files with a single jsonnet fmt invocation
  * jsonnet fmt will now sort your imports alphabetically
  * The Bazel Python build works again
  * jsonnet fmt fixes code with mismatched newlines in { } and similar start/end syntax
  * The Python bindings are now compatible with Python 3
  * There is a VS2017 solution file
  * The default -J paths were broken, now they include a proper "jsonnet" prefix.  It's unlikely anyone was using them, since they were never advertised.


Major changes:
  There are two new operators `e in e` and `e in super`.  They perform the same role as `std.objectHasAll`, i.e. they allow you to discover whether a field exists, except that `std.objectHasAll(super, e)` cannot be expressed since `super` is not allowed in isolation.  The semantics of `e in super` are quite interesting because it returns false even if there is no super object.
  There is a subtle change to the semantics of `+:` and related field definitions.  Previously if the super-object did not have the field `f` then `f +: e` was an error.  Now, it succeeds in that case yielding `f: e`.  This extends recursively, so `{ } + { f+: { x +: { y: 1 } } }` yields `{ f: { x: { y: 1 } } }`, and in fact the left hand side `{ }` is not even needed.  You can evaluate a mixin like `{ f+: { x +: { y: 1 } } }` without even applying it to anything.  This is a backwards compatible change because it only affects the behavior of programs that used to fail.
  Minor other changes in this release:
  - Addition of std.prune
  - Fix of uninitialized value (undefined behavior) in desugarer.


Python-only release.


Sorry for the churn.  Since the last release a buffer overrun was identified, so this release fixes it.  Also included are some fixes to jsonnet fmt behavior and a fix for [e::] when e was larger than one token.


New features:
  - error will implicitly convert to string
  - Verbatim string literals, e.g. " \ \" " can now be given as " \ "" " (note the escaping of "
  - std.md5("foo") is added.
  Bug fixes:
  Fixed the binding of self / super in e after the expansion of [e] +:
  Several improvements to the output of jsonnet fmt when -n is used.
  Avoid an infinite loop of memory consumption at a ||| syntax error.


Fix a segfault in the GC when using native functions.
  Some minor bug fixes.
  Imported values are now cached after execution, resulting in a ~2x performance improvement (measured on some real Jsonnet code).


This release renames all "library" jsonnet files to .libsonnet as proposed some months ago.  This is just a convention, it does not affect evaluation of Jsonnet configs.  As with all conventions, users can adopt it... or ignore it :)
  This release also has some new language features:
  Named params, default arguments
  As in Python, you can provide default arguments to functions, e.g.
  local add(x, y=3) = x + y;
  and also bind arguments to parameters by name instead of by position:
  The syntax and semantics matches Python but without _args and *_kwargs.
  Top-level arguments
  This is a more principled way of parameterizing an entire config.  Previously the only way to do that was with `std.extVar`, but this created a global variable of sorts.  Code anywhere in the config could use it and it in large configs this can become a maintenance hurdle.  `std.extVar` still exists but there is a new way where the parameter is scoped only to the main or root file of the config, and has to be explicitly threaded to imported files:
  Previously a Jsonnet file evaluating to a function would be rejected:
  $ jsonnet -e 'function() 42'
  RUNTIME ERROR: Couldn't manifest function in JSON output.
  Now, such a function is called with no arguments to yield the value that is manifested as JSON.  To add arguments, use the new commandline parameters.
  Available options for specifying values of 'top-level arguments':
  Provide the value as a string:
  -A / --tla-str <var>[=<val>]     If <val> is omitted, get from environment var <var>
  --tla-str-file <var>=<file> Read the string from the file
  Provide a value as Jsonnet code:
  --tla-code <var>[=<code>]    If <code> is omitted, get from environment var <var>
  --tla-code-file <var>=<file> Read the code from the file
  $ jsonnet -e 'function(a, b, c=3) a + b + c' --tla-code a=1 --tla-code b=2
  Note that TLAs provide default arguments for the entire config, something that was not possible (but often requested) with std.extVar().
  Native functions
  Now you can, via the C API (or wrappers, e.g. Python) add your own native functions to Jsonnet.  The purpose of these functions is to expose tricky functionality that you wouldn't want to implement in Jsonnet.  E.g. compression, or encryption.  Note that these functions must be pure.  They must have no side-effects and must give the same return value for the same input.  Since Jsonnet is a lazy language, native functions may be called at unusual times, in an unusual order, more times than expected, or not at all.  If the functionality being exposed is not naturally pure (e.g. random secret generation), please wrap it in a cache so that subsequent calls with the same params give the same result.
  Currently native extensions can have an arbitrary number of parameters but each has to be a primitive (i.e. not an object or an array).  One option for now is to wrap the native function in Jsonnet code that calls std.toString() on the value, then parse the JSON on the host language side.
  Native extensions can however return arbitrary JSON objects.


This release merely fixes some problems with the Bazel build files in the previous release.  As such, there is no need for a corresponding Python release.


Firstly, Jsonnet users should be aware of a tiny non-backwards-compatible change:  It is no longer possible to put spaces or comments between the characters of each of the following operators :: +: ::: and +::: .  The fact this was ever possible was an accident of implementation.  In fact, we expect no-one was actually writing code like { x: /*foo */: 3 }.
  With that out of the way, let's talk about the many new features this release:
  The major announcement is the Jsonnet reformatter, which is in many ways like the Go reformatter, including the way it is invoked:
  jsonnet fmt foo.jsonnet
  (See --help for more details.)
  This tool will clean up whitespace, re-indent, and use syntax sugars where possible.  It can control the way strings are quoted and the comment style.  However it will not (yet) break or join lines.  It is quite extensible so please submit issues for more things you think it should fix.  An obvious candidate is alphabetic re-ordering of imports!
  One thing for which it's very useful is the process of bootstrapping unformatted JSON code into beautiful Jsonnet.  To do this, use a standard JSON reformatter like json_pp to line-break the JSON how you want.  Then run jsonnet fmt on it to strip the quotes from field names where possible and put commas on the end of lines.  That takes care of the most boring aspects of refactoring JSON into Jsonnet!
  Another new feature is to add Python style array slicing.  This is the ability to do
  local arr = ['it', 'was', 'the', 'best', 'of', 'times'];
  assert arr[2:4] == ['the', 'best'];
  assert arr[1::2] == ['was', 'best', 'times'];
  This is designed to be compatible with Python, except the ability to use negative numbers to refer to elements relative to the far end of the array.  The rationale:  We'd rather have an explicit error to catch broken index arithmetic, and you can always use std.length() when you (rarely) need to refer to the last element of an array.
  Did you notice the last example used ' for string literals instead of the " as prescribed by JSON?  This new kind of string literal is useful in cases when you want to embed " in your string without escaping.  It also has a bit less visual clutter.
  Another innovation is --yaml-stream, which was implemented primarily for easy output into kubectl (from the Kubernetes project).  The idea is that the Jsonnet script manifests into a JSON array, and this is rendered as a "YAML stream".  For more context see
  $ jsonnet --yaml-stream -e '[{ animal: "dog", sound: "woof" }, { animal: "cat", sound: "meow" }]'
  "animal": "dog",
  "sound": "woof"
  "animal": "cat",
  "sound": "meow"
  The final new feature:  We now have JSON merge patch support (RFC7396) in the standard library.


Aside from minor improvements and bug fixes, this release has one change to the language that is not compatible with previous versions.
  Some users pointed out that import "foo" + bar had surprising behavior, because it looks like it will compute the name of the import, but in actual fact it is parsed as (import "foo") + bar, which converts the imported Jsonnet file to a string, then appends bar to it.  In order to avoid that confusion, we've make the parentheses mandatory in those cases.
  The vast majority of imports, which are unambiguous, are not affected.  For example:
  local foo = import "foo";
  This release also fixes the compilation of Jsonnet during pip install.


Yet another release needed to fix the Python build.
  This release also includes the restrictions on super that should have been in v0.8.1 but the change got lost in the gh-pages branch.


Fix the Python release (again).


The last release was broken on Python so this fixes that.  It also fixes a bug with decoding high unicode codepoints.


This release:
  - Adds unicode support (finally fixing 1)
  - Extends the array and object comprehension syntax to arbtirary if / for specifiers
  - Fixes a garbage collection bug when using assertions
  - Adds std.count() std.startsWith() and std.endsWith()
  - Re-organizes the source tree.


There are 2 major things in this release:
  The biggest change in this release is the addition of the assert construct.  This is backwards-compatible except if you were using 'assert' as an identifier, in which case you'll have to rename it (or use quotes "assert": in the case of a field name).
  Assert expressions
  assert e1 : e2; e3
  Is simply desugared to
  if e1 then e3 else error e2
  And the : e2 is optional like in Python and Java (except Python uses a comma instead of a colon).
  Object assertions (invariants)
  It is also possible to put asserts into objects:
  local Base = {
  assert self.x <= self.y : "%d greater than %d" % [self.x, self.y],
  x: 1,
  y: 2,
  These are preserved over inheritance and follow the usual late binding semantics.
  Base { x: 3 }  // Raises an error.
  Base { x: 3, y: 4 }  // Ok.
  This can be useful for enumerations:
  local Base = {
  assert std.setMember(self.animal, self.availableAnimals)
  : "%s is not a valid animal" % self.animal,
  animal: error "You must select an animal",
  availableAnimals: ["CAT", "DOG"],
  Base { animal: "FISH" }  // Raises an error.
  Base { animal: "FISH", availableAnimals+: ["FISH"] }  // Ok.
  If you want to constrain the base object so the animals cannot be extended, you can do:
  local Base = {
  local available_animals = ["CAT", "DOG"],
  assert std.setMember(self.animal, available_animals)
  : "%s is not a valid animal" % self.animal,
  animal: error "You must select an animal",
  Preventing override of fields
  Finally, using this mechanism you can lock down a template so that users can only override the "inputs", not the "outputs".  For example imagine building bash scripts as part of a workflow framework:
  local Template = {
  user:: error "Must set user",
  command:: error "Must set command",
  outputFile:: "out.log",
  local template = self,
  local output = {
  bash: "sudo %(user)s \"%(command)s\" > %(outputFile)s" % template,
  bash: output.bash,
  assert self == output : "You may not override this template's output."
  // User code:
  Template {user: "myname", command: "ls", bash+:"; echo hi"}
  This would produce an error because the assertion prevents the user from adding stuff onto the end of the bash command.
  Removal of super as a construct in its own right
  Early in development we decided to avoid restrictions in the language to see where this would lead us.  Two such cases led to implementation complexity and we were tempted to remove them, but we were not sure if they would be useful.  One case was mixins, which turned out to be enormously useful in many cases.  The other case was the ability to do super by itself, not just in the context of super.f or super[e].  The semantics of this were tricky to define in the case of extending super, or extending super with itself (although we did so, and the rules are on the website)!  It also makes the interpreter and object model harder to implement.  To the best of our knowledge, there is not one single person using super in this fashion, so we have now restricted it to just super.f and super[e] as done in other languages.


This release has a few more examples.
  The main new feature is the ability to specify multiline strings more easily, i.e. instead of the ugly
  foo: "!/bin/bash
  echo \"Hello world!\"",
  or the bureaucratic
  foo: std.lines([
  "echo \"Hello world!\"",
  you can now do the following, which is similar to the YAML "block style" construct.
  foo: |||
  echo "Hello world!"
  The indentation is stripped.
  Note that this is just a string literal, so it can be combined with % to do string templating:
  foo: |||
  echo "Hello %(greetable_entity)s!"
  ||| % {
  greetable_entity: "world"


This release has the following changes since v0.7.6 (intermediate versions exist on PyPy but not Github):
  - Add std.setMember
  - Various bug fixes related to transitive imports not being correctly resolved
  - Addition of import_callback to Python bindings


This release has the following changes:
  - Various minor bug fixes.
  - Replace garbage collector with one that can handle larger heaps.
  - Add tailstrict function call annotation for O(1) memory consumption in tail-recursive functions.
  - Added sort, uniq, and set functions to stdlib
  - Python extension is now built with


Some minor fixes to the manifestation of large numbers, as well as new errors when computing NaN/Inf during execution.


This release should be backwards-compatible.
  New features:
  Null field names now omitted from object 10
  std.base64 functions 28
  Trapping import to implement virtual filesystems 8
  Now possible to write Jsonnet libraries, importable via a search path 18
  Comments can now start with  (as well as the C++ and C styles /**/ and //).  This allows !/usr/bin/jsonnet 27


Three minor backwards-incompatible changes:
  1) std.env is renamed to std.extVar.  This is because environment variables are not the only way to get now-called 'external' variables into Jsonnet, you can also pass them explicitly on the command line (or via the library API).
  2) The hidden status of fields defined with : is now preserved over inheritance instead of being reset by the base class.  This means subobjects overriding fields from superobjects do not have to worry about whether those fields should appear in the output or not.
  3) A new keyword tailcall is reserved for future use.
  There are a number of enhancements to the standard library and interpreter, including the ability to generate multiple JSON files from a single authoritative Jsonnet configuration.


First version-numbered release of Jsonnet.