Skip to content

Commit

Permalink
starlarkjson: a standard json module for Starlark
Browse files Browse the repository at this point in the history
This is a sketch of a standard module for JSON encoding/decoding for Starlark.

It is intended to subsume, generalize, and eventually
replace the ill-conceived struct.to_json method.

See related issues:
bazelbuild/bazel#7896
https://buganizer.corp.google.com/issues/23962735
https://buganizer.corp.google.com/issues/70210417
bazelbuild/bazel#7879 (comment)
bazelbuild/bazel#5542

Change-Id: I297ffaee9349eedeeb52f5a88f40636a4095f997
  • Loading branch information
adonovan committed Mar 29, 2019
1 parent 885f8b8 commit 6998490
Show file tree
Hide file tree
Showing 4 changed files with 373 additions and 0 deletions.
7 changes: 7 additions & 0 deletions starlark/eval_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import (
"go.starlark.net/internal/chunkedfile"
"go.starlark.net/resolve"
"go.starlark.net/starlark"
"go.starlark.net/starlarkjson"
"go.starlark.net/starlarkstruct"
"go.starlark.net/starlarktest"
"go.starlark.net/syntax"
)
Expand Down Expand Up @@ -112,6 +114,7 @@ func TestExecFile(t *testing.T) {
"testdata/float.star",
"testdata/function.star",
"testdata/int.star",
"testdata/json.star",
"testdata/list.star",
"testdata/misc.star",
"testdata/set.star",
Expand All @@ -125,6 +128,7 @@ func TestExecFile(t *testing.T) {
predeclared := starlark.StringDict{
"hasfields": starlark.NewBuiltin("hasfields", newHasFields),
"fibonacci": fib{},
"struct": starlark.NewBuiltin("struct", starlarkstruct.Make),
}

setOptions(chunk.Source)
Expand Down Expand Up @@ -179,6 +183,9 @@ func load(thread *starlark.Thread, module string) (starlark.StringDict, error) {
if module == "assert.star" {
return starlarktest.LoadAssertModule()
}
if module == "json.star" {
return starlark.StringDict{"json": starlarkjson.Module}, nil
}

// TODO(adonovan): test load() using this execution path.
filename := filepath.Join(filepath.Dir(thread.Caller().Position().Filename()), module)
Expand Down
68 changes: 68 additions & 0 deletions starlark/testdata/json.star
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Tests of json module.
# option:float

load("assert.star", "assert")
load("json.star", "json")

assert.eq(dir(json), ["decode", "encode", "indent"])

## json.encode

assert.eq(json.encode(None), "null")
assert.eq(json.encode(True), "true")
assert.eq(json.encode(-123), "-123")
assert.eq(json.encode(12345*12345*12345*12345*12345*12345), "3539537889086624823140625")
assert.eq(json.encode(12.345e67), "1.2345e+68")
assert.eq(json.encode("hello"), '"hello"')
assert.eq(json.encode([1, 2, 3]), "[1,2,3]")
assert.eq(json.encode((1, 2, 3)), "[1,2,3]")
assert.eq(json.encode(range(3)), "[0,1,2]") # a built-in iterable
assert.eq(json.encode(dict(x = 1, y = "two")), '{"x":1,"y":"two"}')
assert.eq(json.encode(struct(x = 1, y = "two")), '{"x":1,"y":"two"}') # a user-defined HasAttrs

# errors
assert.fails(lambda: json.encode(float("NaN")), "cannot encode non-finite float NaN")
assert.fails(lambda: json.encode({1: "two"}), "dict has int key, want string")
assert.fails(lambda: json.encode(len), "cannot encode builtin_function_or_method as JSON")
assert.fails(lambda: json.encode(struct(x=[1, {"x": len}])), # nested failure
'in field .x: at list index 1: in dict key "x": cannot encode...')

## json.decode

assert.eq(json.decode("null"), None)
assert.eq(json.decode("true"), True)
assert.eq(json.decode("-123"), -123)
assert.eq(json.decode("3539537889086624823140625"), float(3539537889086624823140625))
assert.eq(json.decode('[]'), ())
assert.eq(json.decode('[1]'), (1,))
assert.eq(json.decode('[1,2,3]'), (1, 2, 3))
assert.eq(json.decode('{"one": 1, "two": 2}'), dict(one=1, two=2))

# Exercise JSON string coding by round-tripping a string with every 16-bit code point.
def codec(x):
return json.decode(json.encode(x))
codepoints = ''.join(['%c' %c for c in range(65536)])
assert.eq(codec(codepoints), codepoints)

## json.indent

s = json.encode(dict(x = 1, y = ["one", "two"]))

assert.eq(json.indent(s), '''{
"x": 1,
"y": [
"one",
"two"
]
}''')

assert.eq(json.indent(s, prefix='¶', indent='–––'), '''{
¶–––"x": 1,
¶–––"y": [
¶––––––"one",
¶––––––"two"
¶–––]
¶}''')

assert.fails(lambda: json.indent("!@#$%^& this is not json"), 'invalid character')
---
1 change: 1 addition & 0 deletions starlark/testdata/paths.star
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ with backslash separators or drive letters.
"""

# This file is in the Bazel build language dialect of Starlark,

# so declarations of 'fail' and 'struct' are required to make
# it compile in the core language.
def fail(msg):
Expand Down
Loading

0 comments on commit 6998490

Please sign in to comment.