sqlmesh 0.201.2.dev1__py3-none-any.whl → 0.201.2.dev3__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.201.2.dev1'
21
- __version_tuple__ = version_tuple = (0, 201, 2, 'dev1')
20
+ __version__ = version = '0.201.2.dev3'
21
+ __version_tuple__ = version_tuple = (0, 201, 2, 'dev3')
sqlmesh/core/context.py CHANGED
@@ -2649,13 +2649,16 @@ class GenericContext(BaseContext, t.Generic[C]):
2649
2649
 
2650
2650
  @cached_property
2651
2651
  def engine_adapters(self) -> t.Dict[str, EngineAdapter]:
2652
- """Returns all the engine adapters for the gateways defined in the configuration."""
2652
+ """Returns all the engine adapters for the gateways defined in the configurations."""
2653
2653
  adapters: t.Dict[str, EngineAdapter] = {self.selected_gateway: self.engine_adapter}
2654
- for gateway_name in self.config.gateways:
2655
- if gateway_name != self.selected_gateway:
2656
- connection = self.config.get_connection(gateway_name)
2657
- adapter = connection.create_engine_adapter(concurrent_tasks=self.concurrent_tasks)
2658
- adapters[gateway_name] = adapter
2654
+ for config in self.configs.values():
2655
+ for gateway_name in config.gateways:
2656
+ if gateway_name not in adapters:
2657
+ connection = config.get_connection(gateway_name)
2658
+ adapter = connection.create_engine_adapter(
2659
+ concurrent_tasks=self.concurrent_tasks,
2660
+ )
2661
+ adapters[gateway_name] = adapter
2659
2662
  return adapters
2660
2663
 
2661
2664
  @cached_property
@@ -153,14 +153,16 @@ def _add_variables_to_python_env(
153
153
 
154
154
  variables = {k: v for k, v in (variables or {}).items() if k in used_variables}
155
155
  if variables:
156
- python_env[c.SQLMESH_VARS] = Executable.value(variables)
156
+ python_env[c.SQLMESH_VARS] = Executable.value(variables, sort_root_dict=True)
157
157
 
158
158
  if blueprint_variables:
159
159
  blueprint_variables = {
160
160
  k: SqlValue(sql=v.sql(dialect=dialect)) if isinstance(v, exp.Expression) else v
161
161
  for k, v in blueprint_variables.items()
162
162
  }
163
- python_env[c.SQLMESH_BLUEPRINT_VARS] = Executable.value(blueprint_variables)
163
+ python_env[c.SQLMESH_BLUEPRINT_VARS] = Executable.value(
164
+ blueprint_variables, sort_root_dict=True
165
+ )
164
166
 
165
167
  return python_env
166
168
 
@@ -4,6 +4,7 @@ and therefore this migration applies deterministic sorting to the keys of the di
4
4
  """
5
5
 
6
6
  import json
7
+ import logging
7
8
  import typing as t
8
9
  from dataclasses import dataclass
9
10
 
@@ -12,6 +13,12 @@ from sqlglot import exp
12
13
  from sqlmesh.utils.migration import index_text_type, blob_text_type
13
14
 
14
15
 
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ KEYS_TO_MAKE_DETERMINISTIC = ["__sqlmesh__vars__", "__sqlmesh__blueprint__vars__"]
20
+
21
+
15
22
  # Make sure `SqlValue` is defined so it can be used by `eval` call in the migration
16
23
  @dataclass
17
24
  class SqlValue:
@@ -20,25 +27,13 @@ class SqlValue:
20
27
  sql: str
21
28
 
22
29
 
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
-
30
+ def _dict_sort(obj: t.Any) -> str:
38
31
  try:
39
- return repr(_normalize_for_repr(obj))
32
+ if isinstance(obj, dict):
33
+ obj = dict(sorted(obj.items(), key=lambda x: str(x[0])))
40
34
  except Exception:
41
- return repr(obj)
35
+ logger.warning("Failed to sort non-recursive dict", exc_info=True)
36
+ return repr(obj)
42
37
 
43
38
 
44
39
  def migrate(state_sync, **kwargs): # type: ignore
@@ -82,12 +77,14 @@ def migrate(state_sync, **kwargs): # type: ignore
82
77
 
83
78
  if python_env:
84
79
  for key, executable in python_env.items():
80
+ if key not in KEYS_TO_MAKE_DETERMINISTIC:
81
+ continue
85
82
  if isinstance(executable, dict) and executable.get("kind") == "value":
86
83
  old_payload = executable["payload"]
87
84
  try:
88
85
  # Try to parse the old payload and re-serialize it deterministically
89
86
  parsed_value = eval(old_payload)
90
- new_payload = _deterministic_repr(parsed_value)
87
+ new_payload = _dict_sort(parsed_value)
91
88
 
92
89
  # Only update if the representation changed
93
90
  if old_payload != new_payload:
@@ -95,7 +92,7 @@ def migrate(state_sync, **kwargs): # type: ignore
95
92
  migration_needed = True
96
93
  except Exception:
97
94
  # If we still can't eval it, leave it as-is
98
- pass
95
+ logger.warning("Exception trying to eval payload", exc_info=True)
99
96
 
100
97
  new_snapshots.append(
101
98
  {
@@ -0,0 +1,82 @@
1
+ import json
2
+ import logging
3
+
4
+ from sqlglot import exp
5
+
6
+ from sqlmesh.core.console import get_console
7
+
8
+
9
+ logger = logging.getLogger(__name__)
10
+ KEYS_TO_MAKE_DETERMINISTIC = ["__sqlmesh__vars__", "__sqlmesh__blueprint__vars__"]
11
+
12
+
13
+ def migrate(state_sync, **kwargs): # type: ignore
14
+ engine_adapter = state_sync.engine_adapter
15
+ schema = state_sync.schema
16
+ snapshots_table = "_snapshots"
17
+ versions_table = "_versions"
18
+ if schema:
19
+ snapshots_table = f"{schema}.{snapshots_table}"
20
+ versions_table = f"{schema}.{versions_table}"
21
+
22
+ result = engine_adapter.fetchone(
23
+ exp.select("schema_version").from_(versions_table), quote_identifiers=True
24
+ )
25
+ if not result:
26
+ # This must be the first migration, so we can skip the check since the project was not exposed to 85 migration bug
27
+ return
28
+ schema_version = result[0]
29
+ if schema_version < 85:
30
+ # The project was not exposed to the bugged 85 migration, so we can skip it.
31
+ return
32
+
33
+ warning = (
34
+ "SQLMesh detected that it may not be able to fully migrate the state database. This should not impact "
35
+ "the migration process, but may result in unexpected changes being reported by the next `sqlmesh plan` "
36
+ "command. Please run `sqlmesh diff prod` after the migration has completed, before making any new "
37
+ "changes. If any unexpected changes are reported, consider running a forward-only plan to apply these "
38
+ "changes and avoid unnecessary backfills: sqlmesh plan prod --forward-only. "
39
+ "See https://sqlmesh.readthedocs.io/en/stable/concepts/plans/#forward-only-plans for more details.\n"
40
+ )
41
+
42
+ for (
43
+ name,
44
+ identifier,
45
+ version,
46
+ snapshot,
47
+ kind_name,
48
+ updated_ts,
49
+ unpaused_ts,
50
+ ttl_ms,
51
+ unrestorable,
52
+ ) in engine_adapter.fetchall(
53
+ exp.select(
54
+ "name",
55
+ "identifier",
56
+ "version",
57
+ "snapshot",
58
+ "kind_name",
59
+ "updated_ts",
60
+ "unpaused_ts",
61
+ "ttl_ms",
62
+ "unrestorable",
63
+ ).from_(snapshots_table),
64
+ quote_identifiers=True,
65
+ ):
66
+ parsed_snapshot = json.loads(snapshot)
67
+ python_env = parsed_snapshot["node"].get("python_env")
68
+
69
+ if python_env:
70
+ for key, executable in python_env.items():
71
+ if (
72
+ key not in KEYS_TO_MAKE_DETERMINISTIC
73
+ and isinstance(executable, dict)
74
+ and executable.get("kind") == "value"
75
+ ):
76
+ try:
77
+ parsed_value = eval(executable["payload"])
78
+ if isinstance(parsed_value, dict):
79
+ get_console().log_warning(warning)
80
+ return
81
+ except Exception:
82
+ logger.warning("Exception trying to eval payload", exc_info=True)
@@ -5,6 +5,7 @@ import dis
5
5
  import importlib
6
6
  import inspect
7
7
  import linecache
8
+ import logging
8
9
  import os
9
10
  import re
10
11
  import sys
@@ -23,6 +24,9 @@ from sqlmesh.utils import format_exception, unique
23
24
  from sqlmesh.utils.errors import SQLMeshError
24
25
  from sqlmesh.utils.pydantic import PydanticModel
25
26
 
27
+ logger = logging.getLogger(__name__)
28
+
29
+
26
30
  IGNORE_DECORATORS = {"macro", "model", "signal"}
27
31
  SERIALIZABLE_CALLABLES = (type, types.FunctionType)
28
32
  LITERALS = (Number, str, bytes, tuple, list, dict, set, bool)
@@ -424,10 +428,11 @@ class Executable(PydanticModel):
424
428
  return self.kind == ExecutableKind.VALUE
425
429
 
426
430
  @classmethod
427
- def value(cls, v: t.Any, is_metadata: t.Optional[bool] = None) -> Executable:
428
- return Executable(
429
- payload=_deterministic_repr(v), kind=ExecutableKind.VALUE, is_metadata=is_metadata
430
- )
431
+ def value(
432
+ cls, v: t.Any, is_metadata: t.Optional[bool] = None, sort_root_dict: bool = False
433
+ ) -> Executable:
434
+ payload = _dict_sort(v) if sort_root_dict else repr(v)
435
+ return Executable(payload=payload, kind=ExecutableKind.VALUE, is_metadata=is_metadata)
431
436
 
432
437
 
433
438
  def serialize_env(env: t.Dict[str, t.Any], path: Path) -> t.Dict[str, Executable]:
@@ -635,36 +640,13 @@ def print_exception(
635
640
  out.write(tb)
636
641
 
637
642
 
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
-
643
+ def _dict_sort(obj: t.Any) -> str:
664
644
  try:
665
- return repr(_normalize_for_repr(obj))
645
+ if isinstance(obj, dict):
646
+ obj = dict(sorted(obj.items(), key=lambda x: str(x[0])))
666
647
  except Exception:
667
- return repr(obj)
648
+ logger.warning("Failed to sort non-recursive dict", exc_info=True)
649
+ return repr(obj)
668
650
 
669
651
 
670
652
  def import_python_file(path: Path, relative_base: Path = Path()) -> types.ModuleType:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqlmesh
3
- Version: 0.201.2.dev1
3
+ Version: 0.201.2.dev3
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=hQHazrZwjilrrZX8wL1OKnB-tUikQtLSNpN9LZPiJ2c,528
2
+ sqlmesh/_version.py,sha256=WiIT14sA7aFc6g1yecX7SEN0IfRMBpNW8l6mQIEj-2E,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
@@ -13,7 +13,7 @@ sqlmesh/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
13
  sqlmesh/core/_typing.py,sha256=e1f7xO2aZCy38M_3VoDy-XI3shyd99-IFctgIFSgXWM,497
14
14
  sqlmesh/core/console.py,sha256=lb6Zq6BhimfQkcSd19t35owSmaGCqw0WHMhGK-WZ-y4,165491
15
15
  sqlmesh/core/constants.py,sha256=gUABXWYgH_XOSbaZAJsR5o1dmA61JDCvBDyxqqXX3LM,2630
16
- sqlmesh/core/context.py,sha256=0EH5FEoPEoxgr3_yu2pasH_Itei7daInEL1uEJgpgwM,127360
16
+ sqlmesh/core/context.py,sha256=34hhZPwqxdjLBuZ-kSlOen07CtlmD3KocQlQdXY8KeI,127454
17
17
  sqlmesh/core/context_diff.py,sha256=_Ur55fGLJ58Icsp44I4pVhdi7_JHiHq45mWIco9oFzk,21792
18
18
  sqlmesh/core/dialect.py,sha256=ELBtB8ft0OyalhgqQ1CUnn26xJxLDah2SP1v8kh_FB8,50903
19
19
  sqlmesh/core/environment.py,sha256=odus40R-YzdQdS_IiUq_tFouUXIhnpwmNwo8dE8Aw1k,13041
@@ -86,7 +86,7 @@ sqlmesh/core/metric/definition.py,sha256=Yd5aVgsZCDPJ43aGP7WqtzZOuuSUtB8uJGVA6Jw
86
86
  sqlmesh/core/metric/rewriter.py,sha256=GiSTHfn2kinqCfNPYgZPRk93JFLzVaaejHtHDQ0yXZI,7326
87
87
  sqlmesh/core/model/__init__.py,sha256=C8GRZ53xuXEA9hQv3BQS9pNNyd9rZ06R_B96UYGhDu4,1644
88
88
  sqlmesh/core/model/cache.py,sha256=csun0RJguHzKX6-qITcOs4fVP4f8_Ts8qiUVV4sHY6Q,7869
89
- sqlmesh/core/model/common.py,sha256=2kXT6DhDAOR59x3EVYrhyoU4QxKv_ScG0zzp98wU8m0,17897
89
+ sqlmesh/core/model/common.py,sha256=mUVJZ1otbcY9IPqeJQWxZOgv_2QnYEEKdFwcPvZGe9c,17961
90
90
  sqlmesh/core/model/decorator.py,sha256=wUUg5t0K559tV9Pqt6FE2EWXt5TPGb4wF2Yg4B4hX4w,9251
91
91
  sqlmesh/core/model/definition.py,sha256=PvE6EYZrPFQHZgSw7kn-V85qpwBf07TOGMnjdz03CE0,112811
92
92
  sqlmesh/core/model/kind.py,sha256=8Dp8yiTiXltboZWrB2FJ-LQ3zx4Fv_5Tj0KdUIPwCHI,36358
@@ -260,7 +260,8 @@ sqlmesh/migrations/v0081_update_partitioned_by.py,sha256=Rj9RDd7WBonbGfTJx3vAqSv
260
260
  sqlmesh/migrations/v0082_warn_if_incorrectly_duplicated_statements.py,sha256=K-Z0ghTCFSZuAYzNSUXF4L8axihPemM15EOXHcqkK3s,2489
261
261
  sqlmesh/migrations/v0083_use_sql_for_scd_time_data_type_data_hash.py,sha256=0r-SlncPec4TBpnQTeblPsiIjIOVKc10GjBVbFPGcFA,148
262
262
  sqlmesh/migrations/v0084_normalize_quote_when_matched_and_merge_filter.py,sha256=w1TbhvB98czuq5VZxsfcQE2w7o-nd96aKEIG-M1dn6A,329
263
- sqlmesh/migrations/v0085_deterministic_repr.py,sha256=gedfV5hoq62mp9HK3eiJ1p9aYAfJ4Cku_cnQHUrEinE,4388
263
+ sqlmesh/migrations/v0085_deterministic_repr.py,sha256=mkbZV0rzXJ5bmN1Y75kFI9T6gbnx_sy-zn_UzbOL1Sg,4265
264
+ sqlmesh/migrations/v0086_check_deterministic_bug.py,sha256=WiYcmhQD434GXWiiakQtcy9ESmB35spXMltV43nvU7Q,2953
264
265
  sqlmesh/schedulers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
265
266
  sqlmesh/utils/__init__.py,sha256=51VqA2etTZ6kX54250s1hN5ui7D89OeUTUQ9kgMze7s,11554
266
267
  sqlmesh/utils/aws.py,sha256=krxyhrKtsMJDEjT567UbGZogjJ9JRHkvAV-Cg3FVndM,1062
@@ -276,7 +277,7 @@ sqlmesh/utils/errors.py,sha256=8FE33Eo3KN3Y4s5EZM5t92jZi9BAVGzkr3QCGlrLeDk,5679
276
277
  sqlmesh/utils/git.py,sha256=v1MD4Zwn52UBn_tTfoKn8SAPJVGC6SEFrJ4WEJFbgF0,1868
277
278
  sqlmesh/utils/hashing.py,sha256=4Bi0NvRctBHzg58btNxXwSNgcvj8doIVVvOjJ40mHBg,512
278
279
  sqlmesh/utils/jinja.py,sha256=zulSPIICdFOjN__xkAiMpxbCS7S0--xAFK9d8VG0KvU,27844
279
- sqlmesh/utils/metaprogramming.py,sha256=5KW6LyjD509I2wi8pZmz2V0fXlzYq5EQ6C42ApWLJxc,24311
280
+ sqlmesh/utils/metaprogramming.py,sha256=yDWS3_tkm6Yd5TNHgii2x_VdZ2pVM0vL6oFuwFMMs0A,23554
280
281
  sqlmesh/utils/migration.py,sha256=R7Ck6FX-LfcujGy7sgS0kDWlK-xAEBODr3gqIQ8gC08,835
281
282
  sqlmesh/utils/pandas.py,sha256=FytTSLcRNtO0-YMvpoDBkkut3yoQJR9mBSKpif8lxug,2497
282
283
  sqlmesh/utils/process.py,sha256=vpXcpJ1eIbivs7gZdwTKSJI7OS6kKBebnaBzz75ykvs,2428
@@ -284,7 +285,7 @@ sqlmesh/utils/pydantic.py,sha256=o_NsXbIpDqNpUA1Uc5xF0ZzoXQYYB0DfHwdRxBwXPNk,120
284
285
  sqlmesh/utils/rich.py,sha256=cwQ5nJ6sgz64xHtoh6_ec7ReV5YpsOGhMtUJnwoRfEI,3549
285
286
  sqlmesh/utils/windows.py,sha256=r7Yin4WEwbvH7sKA3hOJtRsclmscNuBeCklCfVPvtQs,611
286
287
  sqlmesh/utils/yaml.py,sha256=hvo5lmybYLcIx4p6CQwufS8X4MbwS60lEBnCmYJCXmU,2336
287
- sqlmesh-0.201.2.dev1.dist-info/licenses/LICENSE,sha256=OlMefUjgWJdULtf84BLW0AZZcY8DwdgQqb_1j2862j8,11346
288
+ sqlmesh-0.201.2.dev3.dist-info/licenses/LICENSE,sha256=OlMefUjgWJdULtf84BLW0AZZcY8DwdgQqb_1j2862j8,11346
288
289
  web/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
289
290
  web/client/dist/index.html,sha256=DAv6ABGogw4xRLb0T_92WP_S7XWyqVhwBgGKWtuQwU4,5472
290
291
  web/client/dist/assets/Audits-BuHAmFO8.js,sha256=zbYUF6WdUGMbnMm01GMNPb7xAJwj-o5wMdL56vPo2qE,1041
@@ -402,8 +403,8 @@ web/server/api/endpoints/models.py,sha256=kwj0s7uve3iZSMfmjkoPVMFMeY1sD0peTeyrWf
402
403
  web/server/api/endpoints/modules.py,sha256=8hqqgonGay_mJmpCw0IdbjsPhWlQH2VLdKAqha-myac,468
403
404
  web/server/api/endpoints/plan.py,sha256=bbbY50W_2MsZSTxOHWMKz0tbIm75nsRSlPy8GI2fg9Q,9306
404
405
  web/server/api/endpoints/table_diff.py,sha256=SCy3-pr5x6bEEiOaZGCDbiHQhpgxwmnz3Acqa2Ie6jI,2090
405
- sqlmesh-0.201.2.dev1.dist-info/METADATA,sha256=k-puQ_Tjflc39pgRlT-XmHBmN8RfmgdYBwdaLZCIqZ0,26678
406
- sqlmesh-0.201.2.dev1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
407
- sqlmesh-0.201.2.dev1.dist-info/entry_points.txt,sha256=pIYKtg-u-uO7BK_Y0qrdmfyj0bYrcMc3Xiqr5WE3VFk,121
408
- sqlmesh-0.201.2.dev1.dist-info/top_level.txt,sha256=bspjo5vjELQkuN0qKuTFIbsrUt3Zeo8D2mlKeHG1LdE,12
409
- sqlmesh-0.201.2.dev1.dist-info/RECORD,,
406
+ sqlmesh-0.201.2.dev3.dist-info/METADATA,sha256=o8uHF5DQnR5z4OfzBTgjDRT0UEbEMhKGhJdI3Zvplhw,26678
407
+ sqlmesh-0.201.2.dev3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
408
+ sqlmesh-0.201.2.dev3.dist-info/entry_points.txt,sha256=pIYKtg-u-uO7BK_Y0qrdmfyj0bYrcMc3Xiqr5WE3VFk,121
409
+ sqlmesh-0.201.2.dev3.dist-info/top_level.txt,sha256=bspjo5vjELQkuN0qKuTFIbsrUt3Zeo8D2mlKeHG1LdE,12
410
+ sqlmesh-0.201.2.dev3.dist-info/RECORD,,