Skip to content

Commit

Permalink
move noDuplicateKeysInComprehension under a flag for easy migration
Browse files Browse the repository at this point in the history
  • Loading branch information
lihaoyi-databricks committed Jun 12, 2023
1 parent a425342 commit ea8720f
Show file tree
Hide file tree
Showing 6 changed files with 38 additions and 9 deletions.
5 changes: 5 additions & 0 deletions sjsonnet/src-jvm-native/sjsonnet/Config.scala
Original file line number Diff line number Diff line change
Expand Up @@ -123,4 +123,9 @@ case class Config(
doc = "Evaluate the given string as Jsonnet rather than treating it as a file name"
)
exec: Flag = Flag(),
@arg(
name = "no-duplicate-keys-in-comprehension",
doc = "Raise an error if an object comprehension contains duplicate keys"
)
noDuplicateKeysInComprehension: Flag = Flag(),
)
1 change: 1 addition & 0 deletions sjsonnet/src-jvm-native/sjsonnet/SjsonnetMain.scala
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ object SjsonnetMain {
preserveOrder = config.preserveOrder.value,
strict = config.strict.value,
noStaticErrors = config.noStaticErrors.value,
noDuplicateKeysInComprehension = config.noDuplicateKeysInComprehension.value,
),
storePos = if (config.yamlDebug.value) currentPos = _ else null,
warnLogger = warnLogger,
Expand Down
2 changes: 1 addition & 1 deletion sjsonnet/src/sjsonnet/Evaluator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -608,7 +608,7 @@ class Evaluator(resolver: CachedResolver,
s.extend(newBindings, self, null)
)
})
if (prev_length == builder.size()) {
if (prev_length == builder.size() && settings.noDuplicateKeysInComprehension) {
Error.fail(s"Duplicate key ${k} in evaluated object comprehension.", e.pos);
}
case Val.Null(_) => // do nothing
Expand Down
1 change: 1 addition & 0 deletions sjsonnet/src/sjsonnet/Settings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ class Settings(
val preserveOrder: Boolean = false,
val strict: Boolean = false,
val noStaticErrors: Boolean = false,
val noDuplicateKeysInComprehension: Boolean = false,
)

object Settings {
Expand Down
19 changes: 17 additions & 2 deletions sjsonnet/test/src/sjsonnet/EvaluatorTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -333,18 +333,33 @@ object EvaluatorTests extends TestSuite{
eval("""{ ["bar_" + x]: x for x in [5,12]}""") ==> ujson.Obj("bar_5" -> 5, "bar_12" -> 12)
}
test("givenDuplicateFieldsInListComprehension_expectFailure") {
evalErr("""{ [x]: x for x in ["A", "A"]}""") ==>
evalErr("""{ [x]: x for x in ["A", "A"]}""", noDuplicateKeysInComprehension = true) ==>
"""sjsonnet.Error: Duplicate key A in evaluated object comprehension.
|at .(:1:3)""".stripMargin
}
test("givenDuplicateFieldsInListComprehension_noflag_expectSuccess") {
eval("""{ [x]: x for x in ["A", "A"]}""", noDuplicateKeysInComprehension = false) ==>
ujson.Obj("A" -> "A")
}
test("givenDuplicateFieldsInIndirectListComprehension_expectFailure") {
evalErr(
"""local y = { a: "A" };
|local z = { a: "A" };
|{ [x.a]: x for x in [y, z]}""".stripMargin) ==>
|{ [x.a]: x for x in [y, z]}""".stripMargin,
noDuplicateKeysInComprehension = true
) ==>
"""sjsonnet.Error: Duplicate key A in evaluated object comprehension.
|at .(:3:3)""".stripMargin
}
test("givenDuplicateFieldsInIndirectListComprehension_noflag_expectSuccess") {
eval(
"""local y = { a: "A" };
|local z = { a: "A", "b": "B" };
|{ [x.a]: x for x in [y, z]}""".stripMargin,
noDuplicateKeysInComprehension = false
) ==>
ujson.Obj("A" -> ujson.Obj("a" -> "A", "b" -> "B"))
}
test("functionEqualsNull") {
eval("""local f(x)=null; f == null""") ==> ujson.False
eval("""local f=null; f == null""") ==> ujson.True
Expand Down
19 changes: 13 additions & 6 deletions sjsonnet/test/src/sjsonnet/TestUtils.scala
Original file line number Diff line number Diff line change
@@ -1,26 +1,33 @@
package sjsonnet

object TestUtils {
def eval0(s: String, preserveOrder: Boolean = false, strict: Boolean = false) = {
def eval0(s: String,
preserveOrder: Boolean = false,
strict: Boolean = false,
noDuplicateKeysInComprehension: Boolean = false) = {
new Interpreter(
Map(),
Map(),
DummyPath(),
Importer.empty,
parseCache = new DefaultParseCache,
new Settings(preserveOrder = preserveOrder, strict = strict)
new Settings(
preserveOrder = preserveOrder,
strict = strict,
noDuplicateKeysInComprehension = noDuplicateKeysInComprehension
)
).interpret(s, DummyPath("(memory)"))
}

def eval(s: String, preserveOrder: Boolean = false, strict: Boolean = false) = {
eval0(s, preserveOrder, strict) match {
def eval(s: String, preserveOrder: Boolean = false, strict: Boolean = false, noDuplicateKeysInComprehension: Boolean = false) = {
eval0(s, preserveOrder, strict, noDuplicateKeysInComprehension) match {
case Right(x) => x
case Left(e) => throw new Exception(e)
}
}

def evalErr(s: String, preserveOrder: Boolean = false, strict: Boolean = false) = {
eval0(s, preserveOrder, strict) match{
def evalErr(s: String, preserveOrder: Boolean = false, strict: Boolean = false, noDuplicateKeysInComprehension: Boolean = false) = {
eval0(s, preserveOrder, strict, noDuplicateKeysInComprehension) match{
case Left(err) => err.split('\n').map(_.trim).mkString("\n") // normalize inconsistent indenation on JVM vs JS
case Right(r) => throw new Exception(s"Expected exception, got result: $r")
}
Expand Down

0 comments on commit ea8720f

Please sign in to comment.