22
22
from sqlmesh .utils .metaprogramming import (
23
23
Executable ,
24
24
ExecutableKind ,
25
+ _deterministic_repr ,
25
26
build_env ,
26
27
func_globals ,
27
28
normalize_source ,
@@ -48,7 +49,7 @@ def test_print_exception(mocker: MockerFixture):
48
49
except Exception as ex :
49
50
print_exception (ex , test_env , out_mock )
50
51
51
- expected_message = r""" File ".*?.tests.utils.test_metaprogramming\.py", line 47 , in test_print_exception
52
+ expected_message = r""" File ".*?.tests.utils.test_metaprogramming\.py", line 48 , in test_print_exception
52
53
eval\("test_fun\(\)", env\).*
53
54
54
55
File '/test/path.py' \(or imported file\), line 2, in test_fun
@@ -457,3 +458,162 @@ def test_serialize_env_with_enum_import_appearing_in_two_functions() -> None:
457
458
}
458
459
459
460
assert serialized_env == expected_env
461
+
462
+
463
+ def test_deterministic_repr_basic_types ():
464
+ """Test _deterministic_repr with basic Python types."""
465
+ # Test basic types that should use standard repr
466
+ assert _deterministic_repr (42 ) == "42"
467
+ assert _deterministic_repr ("hello" ) == "'hello'"
468
+ assert _deterministic_repr (True ) == "True"
469
+ assert _deterministic_repr (None ) == "None"
470
+ assert _deterministic_repr (3.14 ) == "3.14"
471
+
472
+
473
+ def test_deterministic_repr_dict_ordering ():
474
+ """Test that _deterministic_repr produces consistent output for dicts with different key ordering."""
475
+ # Same dict with different key ordering
476
+ dict1 = {"c" : 3 , "a" : 1 , "b" : 2 }
477
+ dict2 = {"a" : 1 , "b" : 2 , "c" : 3 }
478
+ dict3 = {"b" : 2 , "c" : 3 , "a" : 1 }
479
+
480
+ repr1 = _deterministic_repr (dict1 )
481
+ repr2 = _deterministic_repr (dict2 )
482
+ repr3 = _deterministic_repr (dict3 )
483
+
484
+ # All should produce the same representation
485
+ assert repr1 == repr2 == repr3
486
+ assert repr1 == "{'a': 1, 'b': 2, 'c': 3}"
487
+
488
+
489
+ def test_deterministic_repr_mixed_key_types ():
490
+ """Test _deterministic_repr with mixed key types (strings and numbers)."""
491
+ dict1 = {42 : "number" , "string" : "text" , 1 : "one" }
492
+ dict2 = {"string" : "text" , 1 : "one" , 42 : "number" }
493
+
494
+ repr1 = _deterministic_repr (dict1 )
495
+ repr2 = _deterministic_repr (dict2 )
496
+
497
+ # Should produce consistent ordering despite mixed key types
498
+ assert repr1 == repr2
499
+ # Numbers come before strings when sorting by string representation
500
+ assert repr1 == "{1: 'one', 42: 'number', 'string': 'text'}"
501
+
502
+
503
+ def test_deterministic_repr_nested_structures ():
504
+ """Test _deterministic_repr with deeply nested dictionaries."""
505
+ nested1 = {"outer" : {"z" : 26 , "a" : 1 }, "list" : [3 , {"y" : 2 , "x" : 1 }], "simple" : "value" }
506
+
507
+ nested2 = {"simple" : "value" , "list" : [3 , {"x" : 1 , "y" : 2 }], "outer" : {"a" : 1 , "z" : 26 }}
508
+
509
+ repr1 = _deterministic_repr (nested1 )
510
+ repr2 = _deterministic_repr (nested2 )
511
+
512
+ assert repr1 == repr2
513
+ # Verify structure is maintained with sorted keys
514
+ expected = "{'list': [3, {'x': 1, 'y': 2}], 'outer': {'a': 1, 'z': 26}, 'simple': 'value'}"
515
+ assert repr1 == expected
516
+
517
+
518
+ def test_deterministic_repr_lists_and_tuples ():
519
+ """Test _deterministic_repr preserves order for lists/tuples but sorts nested dicts."""
520
+ # Lists should maintain their order
521
+ list_with_dicts = [{"b" : 2 , "a" : 1 }, {"d" : 4 , "c" : 3 }]
522
+ list_repr = _deterministic_repr (list_with_dicts )
523
+ expected_list = "[{'a': 1, 'b': 2}, {'c': 3, 'd': 4}]"
524
+ assert list_repr == expected_list
525
+
526
+ # Tuples should maintain their order
527
+ tuple_with_dicts = ({"z" : 26 , "a" : 1 }, {"y" : 25 , "b" : 2 })
528
+ tuple_repr = _deterministic_repr (tuple_with_dicts )
529
+ expected_tuple = "({'a': 1, 'z': 26}, {'b': 2, 'y': 25})"
530
+ assert tuple_repr == expected_tuple
531
+
532
+
533
+ def test_deterministic_repr_empty_containers ():
534
+ """Test _deterministic_repr with empty containers."""
535
+ assert _deterministic_repr ({}) == "{}"
536
+ assert _deterministic_repr ([]) == "[]"
537
+ assert _deterministic_repr (()) == "()"
538
+
539
+
540
+ def test_deterministic_repr_special_characters ():
541
+ """Test _deterministic_repr handles special characters correctly."""
542
+ special_dict = {
543
+ "quotes" : "text with 'single' and \" double\" quotes" ,
544
+ "unicode" : "unicode: ñáéíóú" ,
545
+ "newlines" : "text\n with\n newlines" ,
546
+ "backslashes" : "path\\ to\\ file" ,
547
+ }
548
+
549
+ result = _deterministic_repr (special_dict )
550
+
551
+ # Should be valid Python that can be evaluated
552
+ reconstructed = eval (result )
553
+ assert reconstructed == special_dict
554
+
555
+ # Should be deterministic - same input produces same output
556
+ result2 = _deterministic_repr (special_dict )
557
+ assert result == result2
558
+
559
+
560
+ def test_deterministic_repr_executable_integration ():
561
+ """Test that _deterministic_repr works correctly with Executable.value()."""
562
+ # Test the integration with Executable.value which is the main use case
563
+ variables1 = {"env" : "dev" , "debug" : True , "timeout" : 30 }
564
+ variables2 = {"timeout" : 30 , "debug" : True , "env" : "dev" }
565
+
566
+ exec1 = Executable .value (variables1 )
567
+ exec2 = Executable .value (variables2 )
568
+
569
+ # Should produce identical payloads despite different input ordering
570
+ assert exec1 .payload == exec2 .payload
571
+ assert exec1 .payload == "{'debug': True, 'env': 'dev', 'timeout': 30}"
572
+
573
+ # Should be valid Python
574
+ reconstructed = eval (exec1 .payload )
575
+ assert reconstructed == variables1
576
+
577
+
578
+ def test_deterministic_repr_complex_example ():
579
+ """Test _deterministic_repr with a complex real-world-like structure."""
580
+ complex_vars = {
581
+ "database_config" : {
582
+ "host" : "localhost" ,
583
+ "port" : 5432 ,
584
+ "credentials" : {"username" : "admin" , "password" : "secret" },
585
+ },
586
+ "feature_flags" : ["flag_b" , "flag_a" ],
587
+ "metadata" : {
588
+ "version" : "1.0.0" ,
589
+ "environment" : "production" ,
590
+ "tags" : {"team" : "data" , "project" : "analytics" },
591
+ },
592
+ 42 : "numeric_key" ,
593
+ "arrays" : [{"config" : {"nested" : True , "level" : 2 }}, {"simple" : "value" }],
594
+ }
595
+
596
+ expected_structure = {
597
+ 42 : "numeric_key" ,
598
+ "arrays" : [{"config" : {"level" : 2 , "nested" : True }}, {"simple" : "value" }],
599
+ "database_config" : {
600
+ "credentials" : {"password" : "secret" , "username" : "admin" },
601
+ "host" : "localhost" ,
602
+ "port" : 5432 ,
603
+ },
604
+ "feature_flags" : ["flag_b" , "flag_a" ],
605
+ "metadata" : {
606
+ "environment" : "production" ,
607
+ "tags" : {"project" : "analytics" , "team" : "data" },
608
+ "version" : "1.0.0" ,
609
+ },
610
+ }
611
+
612
+ actual_repr = _deterministic_repr (complex_vars )
613
+ expected_repr = repr (expected_structure )
614
+ assert actual_repr == expected_repr
615
+
616
+ # Should be valid Python
617
+ reconstructed = eval (actual_repr )
618
+ assert isinstance (reconstructed , dict )
619
+ assert reconstructed == complex_vars
0 commit comments