sqlmesh 0.199.1.dev45__py3-none-any.whl → 0.200.1.dev1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
sqlmesh/_version.py CHANGED
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '0.199.1.dev45'
21
- __version_tuple__ = version_tuple = (0, 199, 1, 'dev45')
20
+ __version__ = version = '0.200.1.dev1'
21
+ __version_tuple__ = version_tuple = (0, 200, 1, 'dev1')
@@ -0,0 +1,134 @@
1
+ """
2
+ When serializing some objects, like `__sqlmesh__vars__`, the order of keys in the dictionary were not deterministic
3
+ and therefore this migration applies deterministic sorting to the keys of the dictionary.
4
+ """
5
+
6
+ import json
7
+ import typing as t
8
+ from dataclasses import dataclass
9
+
10
+ from sqlglot import exp
11
+
12
+ from sqlmesh.utils.migration import index_text_type, blob_text_type
13
+
14
+
15
+ # Make sure `SqlValue` is defined so it can be used by `eval` call in the migration
16
+ @dataclass
17
+ class SqlValue:
18
+ """A SQL string representing a generated SQLGlot AST."""
19
+
20
+ sql: str
21
+
22
+
23
+ def _deterministic_repr(obj: t.Any) -> str:
24
+ """
25
+ This is a copy of the function from utils.metaprogramming
26
+ """
27
+
28
+ def _normalize_for_repr(o: t.Any) -> t.Any:
29
+ if isinstance(o, dict):
30
+ sorted_items = sorted(o.items(), key=lambda x: str(x[0]))
31
+ return {k: _normalize_for_repr(v) for k, v in sorted_items}
32
+ if isinstance(o, (list, tuple)):
33
+ # Recursively normalize nested structures
34
+ normalized = [_normalize_for_repr(item) for item in o]
35
+ return type(o)(normalized)
36
+ return o
37
+
38
+ try:
39
+ return repr(_normalize_for_repr(obj))
40
+ except Exception:
41
+ return repr(obj)
42
+
43
+
44
+ def migrate(state_sync, **kwargs): # type: ignore
45
+ import pandas as pd
46
+
47
+ engine_adapter = state_sync.engine_adapter
48
+ schema = state_sync.schema
49
+ snapshots_table = "_snapshots"
50
+ if schema:
51
+ snapshots_table = f"{schema}.{snapshots_table}"
52
+
53
+ migration_needed = False
54
+ new_snapshots = []
55
+
56
+ for (
57
+ name,
58
+ identifier,
59
+ version,
60
+ snapshot,
61
+ kind_name,
62
+ updated_ts,
63
+ unpaused_ts,
64
+ ttl_ms,
65
+ unrestorable,
66
+ ) in engine_adapter.fetchall(
67
+ exp.select(
68
+ "name",
69
+ "identifier",
70
+ "version",
71
+ "snapshot",
72
+ "kind_name",
73
+ "updated_ts",
74
+ "unpaused_ts",
75
+ "ttl_ms",
76
+ "unrestorable",
77
+ ).from_(snapshots_table),
78
+ quote_identifiers=True,
79
+ ):
80
+ parsed_snapshot = json.loads(snapshot)
81
+ python_env = parsed_snapshot["node"].get("python_env")
82
+
83
+ if python_env:
84
+ for key, executable in python_env.items():
85
+ if isinstance(executable, dict) and executable.get("kind") == "value":
86
+ old_payload = executable["payload"]
87
+ try:
88
+ # Try to parse the old payload and re-serialize it deterministically
89
+ parsed_value = eval(old_payload)
90
+ new_payload = _deterministic_repr(parsed_value)
91
+
92
+ # Only update if the representation changed
93
+ if old_payload != new_payload:
94
+ executable["payload"] = new_payload
95
+ migration_needed = True
96
+ except Exception:
97
+ # If we still can't eval it, leave it as-is
98
+ pass
99
+
100
+ new_snapshots.append(
101
+ {
102
+ "name": name,
103
+ "identifier": identifier,
104
+ "version": version,
105
+ "snapshot": json.dumps(parsed_snapshot),
106
+ "kind_name": kind_name,
107
+ "updated_ts": updated_ts,
108
+ "unpaused_ts": unpaused_ts,
109
+ "ttl_ms": ttl_ms,
110
+ "unrestorable": unrestorable,
111
+ }
112
+ )
113
+
114
+ if migration_needed and new_snapshots:
115
+ engine_adapter.delete_from(snapshots_table, "TRUE")
116
+
117
+ index_type = index_text_type(engine_adapter.dialect)
118
+ blob_type = blob_text_type(engine_adapter.dialect)
119
+
120
+ engine_adapter.insert_append(
121
+ snapshots_table,
122
+ pd.DataFrame(new_snapshots),
123
+ columns_to_types={
124
+ "name": exp.DataType.build(index_type),
125
+ "identifier": exp.DataType.build(index_type),
126
+ "version": exp.DataType.build(index_type),
127
+ "snapshot": exp.DataType.build(blob_type),
128
+ "kind_name": exp.DataType.build("text"),
129
+ "updated_ts": exp.DataType.build("bigint"),
130
+ "unpaused_ts": exp.DataType.build("bigint"),
131
+ "ttl_ms": exp.DataType.build("bigint"),
132
+ "unrestorable": exp.DataType.build("boolean"),
133
+ },
134
+ )
@@ -425,7 +425,9 @@ class Executable(PydanticModel):
425
425
 
426
426
  @classmethod
427
427
  def value(cls, v: t.Any, is_metadata: t.Optional[bool] = None) -> Executable:
428
- return Executable(payload=repr(v), kind=ExecutableKind.VALUE, is_metadata=is_metadata)
428
+ return Executable(
429
+ payload=_deterministic_repr(v), kind=ExecutableKind.VALUE, is_metadata=is_metadata
430
+ )
429
431
 
430
432
 
431
433
  def serialize_env(env: t.Dict[str, t.Any], path: Path) -> t.Dict[str, Executable]:
@@ -633,6 +635,38 @@ def print_exception(
633
635
  out.write(tb)
634
636
 
635
637
 
638
+ def _deterministic_repr(obj: t.Any) -> str:
639
+ """Create a deterministic representation by ensuring consistent ordering before repr().
640
+
641
+ For dictionaries, ensures consistent key ordering to prevent non-deterministic
642
+ serialization that affects fingerprinting. Uses Python's native repr() logic
643
+ for all formatting to handle edge cases properly.
644
+
645
+ Note that this function assumes list/tuple order is significant and therefore does not sort them.
646
+
647
+ Args:
648
+ obj: The object to represent as a string.
649
+
650
+ Returns:
651
+ A deterministic string representation of the object.
652
+ """
653
+
654
+ def _normalize_for_repr(o: t.Any) -> t.Any:
655
+ if isinstance(o, dict):
656
+ sorted_items = sorted(o.items(), key=lambda x: str(x[0]))
657
+ return {k: _normalize_for_repr(v) for k, v in sorted_items}
658
+ if isinstance(o, (list, tuple)):
659
+ # Recursively normalize nested structures
660
+ normalized = [_normalize_for_repr(item) for item in o]
661
+ return type(o)(normalized)
662
+ return o
663
+
664
+ try:
665
+ return repr(_normalize_for_repr(obj))
666
+ except Exception:
667
+ return repr(obj)
668
+
669
+
636
670
  def import_python_file(path: Path, relative_base: Path = Path()) -> types.ModuleType:
637
671
  relative_path = path.absolute().relative_to(relative_base.absolute())
638
672
  module_name = str(relative_path.with_suffix("")).replace(os.path.sep, ".")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqlmesh
3
- Version: 0.199.1.dev45
3
+ Version: 0.200.1.dev1
4
4
  Summary: Next-generation data transformation framework
5
5
  Author-email: "TobikoData Inc." <engineering@tobikodata.com>
6
6
  License: Apache License
@@ -1,5 +1,5 @@
1
1
  sqlmesh/__init__.py,sha256=QjwaamJIdW7voXoS880yAx7tHm-Im4_mqGA-XJgafos,7631
2
- sqlmesh/_version.py,sha256=HL_ihNgGoramFkQE1PrHObtLhsJZGjVBQK7cM0fe3O4,530
2
+ sqlmesh/_version.py,sha256=KslZWeWoEUEoAhNTAI5BzM2M3yOb3IJN3RCD9jDFfNY,528
3
3
  sqlmesh/magics.py,sha256=bEzYqXHcM0P3CjyUZ1O9arlmXAdLAhoglKbQ29fD9AE,41842
4
4
  sqlmesh/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  sqlmesh/cicd/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -259,6 +259,7 @@ sqlmesh/migrations/v0081_update_partitioned_by.py,sha256=Rj9RDd7WBonbGfTJx3vAqSv
259
259
  sqlmesh/migrations/v0082_warn_if_incorrectly_duplicated_statements.py,sha256=K-Z0ghTCFSZuAYzNSUXF4L8axihPemM15EOXHcqkK3s,2489
260
260
  sqlmesh/migrations/v0083_use_sql_for_scd_time_data_type_data_hash.py,sha256=0r-SlncPec4TBpnQTeblPsiIjIOVKc10GjBVbFPGcFA,148
261
261
  sqlmesh/migrations/v0084_normalize_quote_when_matched_and_merge_filter.py,sha256=w1TbhvB98czuq5VZxsfcQE2w7o-nd96aKEIG-M1dn6A,329
262
+ sqlmesh/migrations/v0085_deterministic_repr.py,sha256=gedfV5hoq62mp9HK3eiJ1p9aYAfJ4Cku_cnQHUrEinE,4388
262
263
  sqlmesh/schedulers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
263
264
  sqlmesh/utils/__init__.py,sha256=51VqA2etTZ6kX54250s1hN5ui7D89OeUTUQ9kgMze7s,11554
264
265
  sqlmesh/utils/aws.py,sha256=krxyhrKtsMJDEjT567UbGZogjJ9JRHkvAV-Cg3FVndM,1062
@@ -274,7 +275,7 @@ sqlmesh/utils/errors.py,sha256=HtpLcs1IN0aq65G372flV9kqqJUULLC3Ci7wvgoBANk,5392
274
275
  sqlmesh/utils/git.py,sha256=v1MD4Zwn52UBn_tTfoKn8SAPJVGC6SEFrJ4WEJFbgF0,1868
275
276
  sqlmesh/utils/hashing.py,sha256=4Bi0NvRctBHzg58btNxXwSNgcvj8doIVVvOjJ40mHBg,512
276
277
  sqlmesh/utils/jinja.py,sha256=zulSPIICdFOjN__xkAiMpxbCS7S0--xAFK9d8VG0KvU,27844
277
- sqlmesh/utils/metaprogramming.py,sha256=ebN5p4Boi9VmsoNVsoUIaW3obgsrtZ6Gt1FlgbA-9xM,23126
278
+ sqlmesh/utils/metaprogramming.py,sha256=5KW6LyjD509I2wi8pZmz2V0fXlzYq5EQ6C42ApWLJxc,24311
278
279
  sqlmesh/utils/migration.py,sha256=R7Ck6FX-LfcujGy7sgS0kDWlK-xAEBODr3gqIQ8gC08,835
279
280
  sqlmesh/utils/pandas.py,sha256=FytTSLcRNtO0-YMvpoDBkkut3yoQJR9mBSKpif8lxug,2497
280
281
  sqlmesh/utils/process.py,sha256=vpXcpJ1eIbivs7gZdwTKSJI7OS6kKBebnaBzz75ykvs,2428
@@ -282,7 +283,7 @@ sqlmesh/utils/pydantic.py,sha256=o_NsXbIpDqNpUA1Uc5xF0ZzoXQYYB0DfHwdRxBwXPNk,120
282
283
  sqlmesh/utils/rich.py,sha256=BpB47G0-JcU41LkGkeGKroexj4NOV5fox4-GOIXPgIc,3545
283
284
  sqlmesh/utils/windows.py,sha256=r7Yin4WEwbvH7sKA3hOJtRsclmscNuBeCklCfVPvtQs,611
284
285
  sqlmesh/utils/yaml.py,sha256=hvo5lmybYLcIx4p6CQwufS8X4MbwS60lEBnCmYJCXmU,2336
285
- sqlmesh-0.199.1.dev45.dist-info/licenses/LICENSE,sha256=OlMefUjgWJdULtf84BLW0AZZcY8DwdgQqb_1j2862j8,11346
286
+ sqlmesh-0.200.1.dev1.dist-info/licenses/LICENSE,sha256=OlMefUjgWJdULtf84BLW0AZZcY8DwdgQqb_1j2862j8,11346
286
287
  web/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
287
288
  web/client/dist/index.html,sha256=DAv6ABGogw4xRLb0T_92WP_S7XWyqVhwBgGKWtuQwU4,5472
288
289
  web/client/dist/assets/Audits-BuHAmFO8.js,sha256=zbYUF6WdUGMbnMm01GMNPb7xAJwj-o5wMdL56vPo2qE,1041
@@ -400,8 +401,8 @@ web/server/api/endpoints/models.py,sha256=kwj0s7uve3iZSMfmjkoPVMFMeY1sD0peTeyrWf
400
401
  web/server/api/endpoints/modules.py,sha256=8hqqgonGay_mJmpCw0IdbjsPhWlQH2VLdKAqha-myac,468
401
402
  web/server/api/endpoints/plan.py,sha256=bbbY50W_2MsZSTxOHWMKz0tbIm75nsRSlPy8GI2fg9Q,9306
402
403
  web/server/api/endpoints/table_diff.py,sha256=SCy3-pr5x6bEEiOaZGCDbiHQhpgxwmnz3Acqa2Ie6jI,2090
403
- sqlmesh-0.199.1.dev45.dist-info/METADATA,sha256=NL8VQrIbMzzmLYAeFcIgiKFZUofxtSDUsBBZKgTJTHU,26658
404
- sqlmesh-0.199.1.dev45.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
405
- sqlmesh-0.199.1.dev45.dist-info/entry_points.txt,sha256=pIYKtg-u-uO7BK_Y0qrdmfyj0bYrcMc3Xiqr5WE3VFk,121
406
- sqlmesh-0.199.1.dev45.dist-info/top_level.txt,sha256=bspjo5vjELQkuN0qKuTFIbsrUt3Zeo8D2mlKeHG1LdE,12
407
- sqlmesh-0.199.1.dev45.dist-info/RECORD,,
404
+ sqlmesh-0.200.1.dev1.dist-info/METADATA,sha256=zG4lNQXIkIYODLR_cZogwPgZhYbu8g67qxsqQLTIo2Y,26657
405
+ sqlmesh-0.200.1.dev1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
406
+ sqlmesh-0.200.1.dev1.dist-info/entry_points.txt,sha256=pIYKtg-u-uO7BK_Y0qrdmfyj0bYrcMc3Xiqr5WE3VFk,121
407
+ sqlmesh-0.200.1.dev1.dist-info/top_level.txt,sha256=bspjo5vjELQkuN0qKuTFIbsrUt3Zeo8D2mlKeHG1LdE,12
408
+ sqlmesh-0.200.1.dev1.dist-info/RECORD,,