Commit ff2f64a
authored
fix: convert format parse errors from Internal Error to user-facing messages (#1076)
Motivation:
Jsonnet documents `std.format(str, vals)` as Python-style percent
formatting, and documents the `%` operator as shorthand for
`std.format`. Malformed percent-format specs are therefore user input
errors, not interpreter failures.
Before this PR, sjsonnet's format scanner/lowering path could throw
plain JVM exceptions for malformed specs such as `%z`, a trailing `%`,
or an unterminated named label. Those exceptions surfaced as `Internal
Error` with Java stack traces, while Python and other Jsonnet
implementations report user-facing format/runtime errors.
Modification:
- Add `parseFormatOrFail` around both cached format-string parsing and
legacy lowered-format conversion.
- Preserve existing sjsonnet `Error` values unchanged.
- Convert `NonFatal` parser/lowering exceptions to `Error.fail(...,
pos)` so malformed format specs are reported at the Jsonnet expression
position.
- Add regression tests for invalid conversions, truncated format specs,
unterminated named labels, the `%` shorthand path, and representative
successful formats.
Result:
Malformed format strings now produce user-facing sjsonnet errors instead
of leaking Java stack traces. The `%` shorthand still uses the same
formatting implementation; its diagnostic keeps the existing
binary-operator stack shape, so it does not add a `[std.format]` builtin
frame. The table below includes the old sjsonnet behavior from `master`
before this PR.
| Case | Python `%` formatting | C++ jsonnet observed | go-jsonnet
observed | jrsonnet observed | sjsonnet before this PR | sjsonnet after
this PR |
|---|---|---|---|---|---|---|
| `std.format("%z", [1])` | `ValueError: unsupported format character z`
| `RUNTIME ERROR: Unrecognised conversion type: z` | `RUNTIME ERROR:
Unrecognised conversion type: z` | `format error: unrecognized
conversion type: z` | `[std.format] Internal Error`, caused by
`Unrecognized conversion type: z` | `[std.format] Unrecognized
conversion type: z` |
| `std.format("hello %", [1])` | `ValueError: incomplete format` |
`RUNTIME ERROR: Truncated format code.` | `RUNTIME ERROR: Truncated
format code.` | `format error: truncated format code` | `[std.format]
Internal Error`, caused by `Truncated format code at end of string` |
`[std.format] Truncated format code at end of string` |
| `std.format("%(key", {key: 1})` | `ValueError: incomplete format key`
| `RUNTIME ERROR: Truncated format code.` | `RUNTIME ERROR: Truncated
format code.` | `format error: truncated format code` | `[std.format]
Internal Error`, caused by `Unterminated ( in format spec` |
`[std.format] Unterminated ( in format spec` |
| `"%z" % [1]` | same malformed `%z` error | `RUNTIME ERROR:
Unrecognised conversion type: z` | `RUNTIME ERROR: Unrecognised
conversion type: z` | `format error: unrecognized conversion type: z` |
`Internal Error`, caused by `Unrecognized conversion type: z` |
`Unrecognized conversion type: z` |
References:
- Official Jsonnet `std.format` documentation:
https://jsonnet.org/ref/stdlib.html#std-format
- Python printf-style string formatting:
https://docs.python.org/3/library/stdtypes.html#printf-style-string-formatting1 parent e686d89 commit ff2f64a
11 files changed
Lines changed: 30 additions & 2 deletions
File tree
- sjsonnet
- src/sjsonnet
- test/resources/new_test_suite
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
| 3 | + | |
| 4 | + | |
3 | 5 | | |
4 | 6 | | |
5 | 7 | | |
| |||
273 | 275 | | |
274 | 276 | | |
275 | 277 | | |
276 | | - | |
| 278 | + | |
277 | 279 | | |
278 | 280 | | |
279 | 281 | | |
| |||
558 | 560 | | |
559 | 561 | | |
560 | 562 | | |
561 | | - | |
| 563 | + | |
| 564 | + | |
562 | 565 | | |
563 | 566 | | |
| 567 | + | |
| 568 | + | |
| 569 | + | |
| 570 | + | |
| 571 | + | |
| 572 | + | |
| 573 | + | |
| 574 | + | |
| 575 | + | |
564 | 576 | | |
565 | 577 | | |
566 | 578 | | |
| |||
Lines changed: 1 addition & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
Lines changed: 2 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
Lines changed: 1 addition & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
Lines changed: 1 addition & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
Lines changed: 1 addition & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
Lines changed: 2 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
Lines changed: 1 addition & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
Lines changed: 1 addition & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
Lines changed: 5 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
0 commit comments