sqlmesh 0.187.6.dev25__py3-none-any.whl → 0.187.7__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.
Files changed (71) hide show
  1. sqlmesh/_version.py +2 -2
  2. sqlmesh/core/config/root.py +0 -2
  3. sqlmesh/core/config/scheduler.py +7 -11
  4. sqlmesh/core/context.py +45 -76
  5. sqlmesh/core/context_diff.py +18 -38
  6. sqlmesh/core/model/cache.py +3 -11
  7. sqlmesh/core/plan/builder.py +5 -6
  8. sqlmesh/core/schema_diff.py +0 -22
  9. sqlmesh/core/selector.py +1 -1
  10. sqlmesh/core/test/definition.py +1 -4
  11. sqlmesh/dbt/loader.py +22 -17
  12. sqlmesh/dbt/manifest.py +4 -31
  13. sqlmesh/dbt/package.py +0 -1
  14. sqlmesh/integrations/github/cicd/command.py +4 -2
  15. sqlmesh/integrations/github/cicd/controller.py +8 -1
  16. sqlmesh/lsp/custom.py +1 -1
  17. sqlmesh/lsp/main.py +33 -178
  18. sqlmesh/lsp/reference.py +3 -194
  19. {sqlmesh-0.187.6.dev25.dist-info → sqlmesh-0.187.7.dist-info}/METADATA +7 -29
  20. {sqlmesh-0.187.6.dev25.dist-info → sqlmesh-0.187.7.dist-info}/RECORD +70 -70
  21. web/client/dist/assets/{Audits-CxDNe6z-.js → Audits-DB1HcP9M.js} +1 -1
  22. web/client/dist/assets/{Banner-DgEFplVH.js → Banner-DNY__4sG.js} +1 -1
  23. web/client/dist/assets/{ChevronDownIcon-BH5O_hpi.js → ChevronDownIcon-lHn9zjvd.js} +1 -1
  24. web/client/dist/assets/{ChevronRightIcon-B2tR7wtU.js → ChevronRightIcon-oQ5f5ZGP.js} +1 -1
  25. web/client/dist/assets/{Content-CiZNHHa7.js → Content-Da0wstVW.js} +1 -1
  26. web/client/dist/assets/{Content-CQOt5HQJ.js → Content-QpBh5BIf.js} +1 -1
  27. web/client/dist/assets/{Data-CcafCjCX.js → Data-C7p96ruh.js} +1 -1
  28. web/client/dist/assets/{DataCatalog-FDQcoVzu.js → DataCatalog-C7cUGv8x.js} +1 -1
  29. web/client/dist/assets/{Editor-DPJUo9Bh.js → Editor-B6PRl__L.js} +1 -1
  30. web/client/dist/assets/{Editor-UHmKFABp.js → Editor-S5kDO3v1.js} +1 -1
  31. web/client/dist/assets/{Errors-PPhF0BLv.js → Errors-CxCutNFm.js} +1 -1
  32. web/client/dist/assets/{FileExplorer-Dn5aKM9r.js → FileExplorer-kP-IjmYP.js} +1 -1
  33. web/client/dist/assets/{Footer-C3l7iM3A.js → Footer-rRlYYZGX.js} +1 -1
  34. web/client/dist/assets/{Header-iNVdvXN8.js → Header-DWsJQ7Zd.js} +1 -1
  35. web/client/dist/assets/{Input-CettPX-D.js → Input-DdPN19Px.js} +1 -1
  36. web/client/dist/assets/Lineage-BQJPyHVL.js +1 -0
  37. web/client/dist/assets/{ListboxShow-BaJGpuWH.js → ListboxShow-BmQk-sC6.js} +1 -1
  38. web/client/dist/assets/{ModelLineage-zSYJ8qL6.js → ModelLineage-Cb-ryU8k.js} +1 -1
  39. web/client/dist/assets/{Models-BpC_5kGJ.js → Models-Bv0YKo6C.js} +1 -1
  40. web/client/dist/assets/{Page-Bq-HPl_L.js → Page-C-F04Qw-.js} +1 -1
  41. web/client/dist/assets/{Plan-DBHhKyK_.js → Plan-DfF6_rdE.js} +1 -1
  42. web/client/dist/assets/{PlusCircleIcon-Bh1eQZZw.js → PlusCircleIcon-D8BJTuwF.js} +1 -1
  43. web/client/dist/assets/{ReportErrors-BvekyVWZ.js → ReportErrors-BR3yV5Ru.js} +1 -1
  44. web/client/dist/assets/{Root-B3mVFWm-.js → Root-gCyYAZOq.js} +1 -1
  45. web/client/dist/assets/{SearchList-BoZPD5Ug.js → SearchList-Mw4DP7x1.js} +1 -1
  46. web/client/dist/assets/{SelectEnvironment-DVTyPJgk.js → SelectEnvironment-C-I8dl8g.js} +1 -1
  47. web/client/dist/assets/{SourceList-De_bjt2z.js → SourceList-nJbKxW-u.js} +1 -1
  48. web/client/dist/assets/{SourceListItem-s3cjyYyg.js → SourceListItem-Cpx6p_wh.js} +1 -1
  49. web/client/dist/assets/{SplitPane-YXb1GDjg.js → SplitPane-CwtTNpO8.js} +1 -1
  50. web/client/dist/assets/{Tests-CABcm50Q.js → Tests-BKeeabY_.js} +1 -1
  51. web/client/dist/assets/{Welcome-BmBt__Vn.js → Welcome-D191sGJC.js} +1 -1
  52. web/client/dist/assets/{context-C6hLZh3v.js → context-5PbIKqqT.js} +1 -1
  53. web/client/dist/assets/{context-0jZBOaYJ.js → context-CVkhr9QB.js} +2 -2
  54. web/client/dist/assets/{editor-Dn-V5hys.js → editor-DWINdd81.js} +1 -1
  55. web/client/dist/assets/{file-FSJHKTZR.js → file-e9qyDfWg.js} +1 -1
  56. web/client/dist/assets/{floating-ui.react-dom-CwIPOwQR.js → floating-ui.react-dom-B-NNWGkz.js} +1 -1
  57. web/client/dist/assets/{help-Eh_Sg5R6.js → help-CjbSQwWi.js} +1 -1
  58. web/client/dist/assets/{index-OgPYo7hm.js → index-BZGZqBUO.js} +1 -1
  59. web/client/dist/assets/{index-hHSGLUzW.js → index-BkNpRAHt.js} +15 -15
  60. web/client/dist/assets/{plan-Crz4bZML.js → plan-C2UHjG7p.js} +1 -1
  61. web/client/dist/assets/{popover-CiOVW6r6.js → popover-C8e3ENo3.js} +1 -1
  62. web/client/dist/assets/{project-6bhvIw3F.js → project-BDZ1fnja.js} +1 -1
  63. web/client/dist/index.html +1 -1
  64. web/server/api/endpoints/commands.py +1 -1
  65. web/server/api/endpoints/files.py +1 -1
  66. web/server/utils.py +2 -1
  67. web/client/dist/assets/Lineage-DPXlJvwM.js +0 -1
  68. {sqlmesh-0.187.6.dev25.dist-info → sqlmesh-0.187.7.dist-info}/WHEEL +0 -0
  69. {sqlmesh-0.187.6.dev25.dist-info → sqlmesh-0.187.7.dist-info}/entry_points.txt +0 -0
  70. {sqlmesh-0.187.6.dev25.dist-info → sqlmesh-0.187.7.dist-info}/licenses/LICENSE +0 -0
  71. {sqlmesh-0.187.6.dev25.dist-info → sqlmesh-0.187.7.dist-info}/top_level.txt +0 -0
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.187.6.dev25'
21
- __version_tuple__ = version_tuple = (0, 187, 6, 'dev25')
20
+ __version__ = version = '0.187.7'
21
+ __version_tuple__ = version_tuple = (0, 187, 7)
@@ -107,7 +107,6 @@ class Config(BaseConfig):
107
107
  physical_schema_mapping: A mapping from regular expressions to names of schemas in which physical tables for corresponding models will be placed.
108
108
  environment_suffix_target: Indicates whether to append the environment name to the schema or table name.
109
109
  gateway_managed_virtual_layer: Whether the models' views in the virtual layer are created by the model-specific gateway rather than the default gateway.
110
- infer_python_dependencies: Whether to statically analyze Python code to automatically infer Python package requirements.
111
110
  environment_catalog_mapping: A mapping from regular expressions to catalog names. The catalog name is used to determine the target catalog for a given environment.
112
111
  default_target_environment: The name of the environment that will be the default target for the `sqlmesh plan` and `sqlmesh run` commands.
113
112
  log_limit: The default number of logs to keep.
@@ -147,7 +146,6 @@ class Config(BaseConfig):
147
146
  default=EnvironmentSuffixTarget.default
148
147
  )
149
148
  gateway_managed_virtual_layer: bool = False
150
- infer_python_dependencies: bool = True
151
149
  environment_catalog_mapping: RegexKeyDict = {}
152
150
  default_target_environment: str = c.PROD
153
151
  log_limit: int = c.DEFAULT_LOG_LIMIT
@@ -47,8 +47,8 @@ class SchedulerConfig(abc.ABC):
47
47
  """
48
48
 
49
49
  @abc.abstractmethod
50
- def get_default_catalog_per_gateway(self, context: GenericContext) -> t.Dict[str, str]:
51
- """Returns the default catalog for each gateway.
50
+ def get_default_catalog(self, context: GenericContext) -> t.Optional[str]:
51
+ """Returns the default catalog for the Scheduler.
52
52
 
53
53
  Args:
54
54
  context: The SQLMesh Context.
@@ -66,7 +66,7 @@ class SchedulerConfig(abc.ABC):
66
66
  class _EngineAdapterStateSyncSchedulerConfig(SchedulerConfig):
67
67
  def create_state_sync(self, context: GenericContext) -> StateSync:
68
68
  state_connection = (
69
- context.config.get_state_connection(context.gateway) or context.connection_config
69
+ context.config.get_state_connection(context.gateway) or context._connection_config
70
70
  )
71
71
 
72
72
  warehouse_connection = context.config.get_connection(context.gateway)
@@ -110,7 +110,7 @@ class _EngineAdapterStateSyncSchedulerConfig(SchedulerConfig):
110
110
 
111
111
  def state_sync_fingerprint(self, context: GenericContext) -> str:
112
112
  state_connection = (
113
- context.config.get_state_connection(context.gateway) or context.connection_config
113
+ context.config.get_state_connection(context.gateway) or context._connection_config
114
114
  )
115
115
  return md5(
116
116
  [
@@ -132,16 +132,12 @@ class BuiltInSchedulerConfig(_EngineAdapterStateSyncSchedulerConfig, BaseConfig)
132
132
  state_sync=context.state_sync,
133
133
  snapshot_evaluator=context.snapshot_evaluator,
134
134
  create_scheduler=context.create_scheduler,
135
- default_catalog=context.default_catalog,
135
+ default_catalog=self.get_default_catalog(context),
136
136
  console=context.console,
137
137
  )
138
138
 
139
- def get_default_catalog_per_gateway(self, context: GenericContext) -> t.Dict[str, str]:
140
- default_catalogs_per_gateway: t.Dict[str, str] = {}
141
- for gateway, adapter in context.engine_adapters.items():
142
- if catalog := adapter.default_catalog:
143
- default_catalogs_per_gateway[gateway] = catalog
144
- return default_catalogs_per_gateway
139
+ def get_default_catalog(self, context: GenericContext) -> t.Optional[str]:
140
+ return context.engine_adapter.default_catalog
145
141
 
146
142
 
147
143
  SCHEDULER_CONFIG_TO_TYPE = {
sqlmesh/core/context.py CHANGED
@@ -62,9 +62,7 @@ from sqlmesh.core.config import (
62
62
  Config,
63
63
  load_configs,
64
64
  )
65
- from sqlmesh.core.config.connection import ConnectionConfig
66
65
  from sqlmesh.core.config.loader import C
67
- from sqlmesh.core.config.root import RegexKeyDict
68
66
  from sqlmesh.core.console import get_console
69
67
  from sqlmesh.core.context_diff import ContextDiff
70
68
  from sqlmesh.core.dialect import (
@@ -369,9 +367,6 @@ class GenericContext(BaseContext, t.Generic[C]):
369
367
  self._excluded_requirements: t.Set[str] = set()
370
368
  self._default_catalog: t.Optional[str] = None
371
369
  self._default_catalog_per_gateway: t.Optional[t.Dict[str, str]] = None
372
- self._engine_adapter: t.Optional[EngineAdapter] = None
373
- self._connection_config: t.Optional[ConnectionConfig] = None
374
- self._test_connection_config: t.Optional[ConnectionConfig] = None
375
370
  self._linters: t.Dict[str, Linter] = {}
376
371
  self._loaded: bool = False
377
372
 
@@ -388,6 +383,7 @@ class GenericContext(BaseContext, t.Generic[C]):
388
383
  self.pinned_environments = Environment.sanitize_names(self.config.pinned_environments)
389
384
  self.auto_categorize_changes = self.config.plan.auto_categorize_changes
390
385
  self.selected_gateway = gateway or self.config.default_gateway_name
386
+ self.gateway_managed_virtual_layer = self.config.gateway_managed_virtual_layer
391
387
 
392
388
  gw_model_defaults = self.config.gateways[self.selected_gateway].model_defaults
393
389
  if gw_model_defaults:
@@ -411,15 +407,24 @@ class GenericContext(BaseContext, t.Generic[C]):
411
407
  for path, config in self.configs.items()
412
408
  ]
413
409
 
414
- self._concurrent_tasks = concurrent_tasks
410
+ self._connection_config = self.config.get_connection(self.gateway)
415
411
  self._state_connection_config = (
416
- self.config.get_state_connection(self.gateway) or self.connection_config
412
+ self.config.get_state_connection(self.gateway) or self._connection_config
417
413
  )
414
+ self.concurrent_tasks = concurrent_tasks or self._connection_config.concurrent_tasks
415
+
416
+ self._engine_adapters: t.Dict[str, EngineAdapter] = {
417
+ self.selected_gateway: self._connection_config.create_engine_adapter()
418
+ }
418
419
 
419
420
  self._snapshot_evaluator: t.Optional[SnapshotEvaluator] = None
420
421
 
421
422
  self.console = get_console()
422
- setattr(self.console, "dialect", self.config.dialect)
423
+ setattr(self.console, "dialect", self.engine_adapter.dialect)
424
+
425
+ self._test_connection_config = self.config.get_test_connection(
426
+ self.gateway, self.default_catalog, default_catalog_dialect=self.engine_adapter.DIALECT
427
+ )
423
428
 
424
429
  self._provided_state_sync: t.Optional[StateSync] = state_sync
425
430
  self._state_sync: t.Optional[StateSync] = None
@@ -430,6 +435,14 @@ class GenericContext(BaseContext, t.Generic[C]):
430
435
  self.users = list({user.username: user for user in self.users}.values())
431
436
  self._register_notification_targets()
432
437
 
438
+ if (
439
+ self.config.environment_catalog_mapping
440
+ and not self.engine_adapter.catalog_support.is_multi_catalog_supported
441
+ ):
442
+ raise SQLMeshError(
443
+ "Environment catalog mapping is only supported for engine adapters that support multiple catalogs"
444
+ )
445
+
433
446
  if load:
434
447
  self.load()
435
448
 
@@ -440,9 +453,7 @@ class GenericContext(BaseContext, t.Generic[C]):
440
453
  @property
441
454
  def engine_adapter(self) -> EngineAdapter:
442
455
  """Returns the default engine adapter."""
443
- if self._engine_adapter is None:
444
- self._engine_adapter = self.connection_config.create_engine_adapter()
445
- return self._engine_adapter
456
+ return self._engine_adapters[self.selected_gateway]
446
457
 
447
458
  @property
448
459
  def snapshot_evaluator(self) -> SnapshotEvaluator:
@@ -921,22 +932,19 @@ class GenericContext(BaseContext, t.Generic[C]):
921
932
 
922
933
  return snapshot
923
934
 
924
- def config_for_path(self, path: Path) -> t.Tuple[Config, Path]:
925
- """Returns the config and path of the said project for a given file path."""
935
+ def config_for_path(self, path: Path) -> Config:
926
936
  for config_path, config in self.configs.items():
927
937
  try:
928
938
  path.relative_to(config_path)
929
- return config, config_path
939
+ return config
930
940
  except ValueError:
931
941
  pass
932
- return self.config, self.path
942
+ return self.config
933
943
 
934
944
  def config_for_node(self, node: str | Model | Audit) -> Config:
935
945
  if isinstance(node, str):
936
- return self.config_for_path(self.get_snapshot(node, raise_if_missing=True).node._path)[
937
- 0
938
- ] # type: ignore
939
- return self.config_for_path(node._path)[0] # type: ignore
946
+ return self.config_for_path(self.get_snapshot(node, raise_if_missing=True).node._path) # type: ignore
947
+ return self.config_for_path(node._path) # type: ignore
940
948
 
941
949
  @property
942
950
  def models(self) -> MappingProxyType[str, Model]:
@@ -969,8 +977,8 @@ class GenericContext(BaseContext, t.Generic[C]):
969
977
 
970
978
  @property
971
979
  def default_catalog(self) -> t.Optional[str]:
972
- if self._default_catalog is None and self.default_catalog_per_gateway:
973
- self._default_catalog = self.default_catalog_per_gateway[self.selected_gateway]
980
+ if self._default_catalog is None:
981
+ self._default_catalog = self._scheduler.get_default_catalog(self)
974
982
  return self._default_catalog
975
983
 
976
984
  @python_api_analytics
@@ -1527,7 +1535,7 @@ class GenericContext(BaseContext, t.Generic[C]):
1527
1535
  allow_destructive_models=expanded_destructive_models,
1528
1536
  environment_ttl=environment_ttl,
1529
1537
  environment_suffix_target=self.config.environment_suffix_target,
1530
- environment_catalog_mapping=self.environment_catalog_mapping,
1538
+ environment_catalog_mapping=self.config.environment_catalog_mapping,
1531
1539
  categorizer_config=categorizer_config or self.auto_categorize_changes,
1532
1540
  auto_categorization_enabled=not no_auto_categorization,
1533
1541
  effective_from=effective_from,
@@ -1539,6 +1547,7 @@ class GenericContext(BaseContext, t.Generic[C]):
1539
1547
  ),
1540
1548
  end_bounded=not run,
1541
1549
  ensure_finalized_snapshots=self.config.plan.use_finalized_state,
1550
+ engine_schema_differ=self.engine_adapter.SCHEMA_DIFFER,
1542
1551
  interval_end_per_model=max_interval_end_per_model,
1543
1552
  console=self.console,
1544
1553
  user_provided_flags=user_provided_flags,
@@ -1627,7 +1636,7 @@ class GenericContext(BaseContext, t.Generic[C]):
1627
1636
  self.console.show_model_difference_summary(
1628
1637
  context_diff,
1629
1638
  EnvironmentNamingInfo.from_environment_catalog_mapping(
1630
- self.environment_catalog_mapping,
1639
+ self.config.environment_catalog_mapping,
1631
1640
  name=environment,
1632
1641
  suffix_target=self.config.environment_suffix_target,
1633
1642
  normalize_name=context_diff.normalize_environment_name,
@@ -1715,7 +1724,7 @@ class GenericContext(BaseContext, t.Generic[C]):
1715
1724
  if target_snapshot and source_snapshot:
1716
1725
  if (source_snapshot.fingerprint != target_snapshot.fingerprint) and (
1717
1726
  (source_snapshot.version != target_snapshot.version)
1718
- or source_snapshot.is_forward_only
1727
+ or (source_snapshot.is_forward_only or target_snapshot.is_forward_only)
1719
1728
  ):
1720
1729
  # Compare the virtual layer instead of the physical layer because the virtual layer is guaranteed to point
1721
1730
  # to the correct/active snapshot for the model in the specified environment, taking into account things like dev previews
@@ -1981,7 +1990,7 @@ class GenericContext(BaseContext, t.Generic[C]):
1981
1990
 
1982
1991
  try:
1983
1992
  model_to_test = self.get_model(model, raise_if_missing=True)
1984
- test_adapter = self.test_connection_config.create_engine_adapter(
1993
+ test_adapter = self._test_connection_config.create_engine_adapter(
1985
1994
  register_comments_override=False
1986
1995
  )
1987
1996
 
@@ -2027,7 +2036,7 @@ class GenericContext(BaseContext, t.Generic[C]):
2027
2036
  preserve_fixtures=preserve_fixtures,
2028
2037
  stream=stream,
2029
2038
  default_catalog=self.default_catalog,
2030
- default_catalog_dialect=self.config.dialect or "",
2039
+ default_catalog_dialect=self.engine_adapter.DIALECT,
2031
2040
  )
2032
2041
 
2033
2042
  @python_api_analytics
@@ -2466,7 +2475,7 @@ class GenericContext(BaseContext, t.Generic[C]):
2466
2475
  self.console.log_test_results(
2467
2476
  result,
2468
2477
  test_output,
2469
- self.test_connection_config._engine_adapter.DIALECT,
2478
+ self._test_connection_config._engine_adapter.DIALECT,
2470
2479
  )
2471
2480
  if not result.wasSuccessful():
2472
2481
  raise PlanError(
@@ -2487,7 +2496,7 @@ class GenericContext(BaseContext, t.Generic[C]):
2487
2496
  if snapshot.version
2488
2497
  else snapshot.qualified_view_name.for_environment(
2489
2498
  EnvironmentNamingInfo.from_environment_catalog_mapping(
2490
- self.environment_catalog_mapping,
2499
+ self.config.environment_catalog_mapping,
2491
2500
  name=c.PROD,
2492
2501
  suffix_target=self.config.environment_suffix_target,
2493
2502
  )
@@ -2499,63 +2508,24 @@ class GenericContext(BaseContext, t.Generic[C]):
2499
2508
  @cached_property
2500
2509
  def engine_adapters(self) -> t.Dict[str, EngineAdapter]:
2501
2510
  """Returns all the engine adapters for the gateways defined in the configuration."""
2502
- adapters: t.Dict[str, EngineAdapter] = {self.selected_gateway: self.engine_adapter}
2503
2511
  for gateway_name in self.config.gateways:
2504
2512
  if gateway_name != self.selected_gateway:
2505
2513
  connection = self.config.get_connection(gateway_name)
2506
2514
  adapter = connection.create_engine_adapter(concurrent_tasks=self.concurrent_tasks)
2507
- adapters[gateway_name] = adapter
2508
- return adapters
2515
+ self._engine_adapters[gateway_name] = adapter
2516
+ return self._engine_adapters
2509
2517
 
2510
2518
  @cached_property
2511
2519
  def default_catalog_per_gateway(self) -> t.Dict[str, str]:
2512
2520
  """Returns the default catalogs for each engine adapter."""
2513
2521
  if self._default_catalog_per_gateway is None:
2514
- self._default_catalog_per_gateway = self._scheduler.get_default_catalog_per_gateway(
2515
- self
2516
- )
2522
+ self._default_catalog_per_gateway = {
2523
+ name: adapter.default_catalog
2524
+ for name, adapter in self.engine_adapters.items()
2525
+ if adapter.default_catalog
2526
+ }
2517
2527
  return self._default_catalog_per_gateway
2518
2528
 
2519
- @cached_property
2520
- def concurrent_tasks(self) -> int:
2521
- if self._concurrent_tasks is None:
2522
- self._concurrent_tasks = self.connection_config.concurrent_tasks
2523
- return self._concurrent_tasks
2524
-
2525
- @cached_property
2526
- def connection_config(self) -> ConnectionConfig:
2527
- if self._connection_config is None:
2528
- self._connection_config = self.config.get_connection(self.selected_gateway)
2529
- return self._connection_config
2530
-
2531
- @cached_property
2532
- def test_connection_config(self) -> ConnectionConfig:
2533
- if self._test_connection_config is None:
2534
- self._test_connection_config = self.config.get_test_connection(
2535
- self.gateway,
2536
- self.default_catalog,
2537
- default_catalog_dialect=self.engine_adapter.DIALECT,
2538
- )
2539
- return self._test_connection_config
2540
-
2541
- @cached_property
2542
- def environment_catalog_mapping(self) -> RegexKeyDict:
2543
- engine_adapter = None
2544
- try:
2545
- engine_adapter = self.engine_adapter
2546
- except Exception:
2547
- pass
2548
-
2549
- if (
2550
- self.config.environment_catalog_mapping
2551
- and engine_adapter
2552
- and not self.engine_adapter.catalog_support.is_multi_catalog_supported
2553
- ):
2554
- raise SQLMeshError(
2555
- "Environment catalog mapping is only supported for engine adapters that support multiple catalogs"
2556
- )
2557
- return self.config.environment_catalog_mapping
2558
-
2559
2529
  def _get_engine_adapter(self, gateway: t.Optional[str] = None) -> EngineAdapter:
2560
2530
  if gateway:
2561
2531
  if adapter := self.engine_adapters.get(gateway):
@@ -2616,8 +2586,7 @@ class GenericContext(BaseContext, t.Generic[C]):
2616
2586
  ensure_finalized_snapshots=ensure_finalized_snapshots,
2617
2587
  diff_rendered=diff_rendered,
2618
2588
  environment_statements=self._environment_statements,
2619
- gateway_managed_virtual_layer=self.config.gateway_managed_virtual_layer,
2620
- infer_python_dependencies=self.config.infer_python_dependencies,
2589
+ gateway_managed_virtual_layer=self.gateway_managed_virtual_layer,
2621
2590
  )
2622
2591
 
2623
2592
  def _destroy(self) -> None:
@@ -102,7 +102,6 @@ class ContextDiff(PydanticModel):
102
102
  diff_rendered: bool = False,
103
103
  environment_statements: t.Optional[t.List[EnvironmentStatements]] = [],
104
104
  gateway_managed_virtual_layer: bool = False,
105
- infer_python_dependencies: bool = True,
106
105
  ) -> ContextDiff:
107
106
  """Create a ContextDiff object.
108
107
 
@@ -117,12 +116,6 @@ class ContextDiff(PydanticModel):
117
116
  the environment is not finalized.
118
117
  provided_requirements: Python dependencies sourced from the lock file.
119
118
  excluded_requirements: Python dependencies to exclude.
120
- diff_rendered: Whether to compute the diff of the rendered version of the compared expressions.
121
- environment_statements: A list of `before_all` or `after_all` statements associated with the environment.
122
- gateway_managed_virtual_layer: Whether the models' views in the virtual layer are created by the
123
- model-specific gateway rather than the default gateway.
124
- infer_python_dependencies: Whether to statically analyze Python code to automatically infer Python
125
- package requirements.
126
119
 
127
120
  Returns:
128
121
  The ContextDiff object.
@@ -215,7 +208,6 @@ class ContextDiff(PydanticModel):
215
208
  provided_requirements or {},
216
209
  excluded_requirements or set(),
217
210
  snapshots.values(),
218
- infer_python_dependencies=infer_python_dependencies,
219
211
  )
220
212
 
221
213
  previous_environment_statements = state_reader.get_environment_statements(environment)
@@ -483,41 +475,29 @@ def _build_requirements(
483
475
  provided_requirements: t.Dict[str, str],
484
476
  excluded_requirements: t.Set[str],
485
477
  snapshots: t.Collection[Snapshot],
486
- infer_python_dependencies: bool = True,
487
478
  ) -> t.Dict[str, str]:
488
479
  requirements = {
489
480
  k: v for k, v in provided_requirements.items() if k not in excluded_requirements
490
481
  }
491
-
492
- if not infer_python_dependencies:
493
- return requirements
494
-
495
482
  distributions = metadata.packages_distributions()
496
483
 
497
484
  for snapshot in snapshots:
498
- if not snapshot.is_model:
499
- continue
500
-
501
- for executable in snapshot.model.python_env.values():
502
- if executable.kind != "import":
503
- continue
504
-
505
- try:
506
- start = "from " if executable.payload.startswith("from ") else "import "
507
- lib = executable.payload.split(start)[1].split()[0].split(".")[0]
508
- if lib not in distributions:
509
- continue
510
-
511
- for dist in distributions[lib]:
512
- if (
513
- dist not in requirements
514
- and dist not in IGNORED_PACKAGES
515
- and dist not in excluded_requirements
516
- ):
517
- requirements[dist] = metadata.version(dist)
518
- except metadata.PackageNotFoundError:
519
- from sqlmesh.core.console import get_console
520
-
521
- get_console().log_warning(f"Failed to find package for {lib}.")
522
-
485
+ if snapshot.is_model:
486
+ for executable in snapshot.model.python_env.values():
487
+ if executable.kind == "import":
488
+ try:
489
+ start = "from " if executable.payload.startswith("from ") else "import "
490
+ lib = executable.payload.split(start)[1].split()[0].split(".")[0]
491
+ if lib in distributions:
492
+ for dist in distributions[lib]:
493
+ if (
494
+ dist not in requirements
495
+ and dist not in IGNORED_PACKAGES
496
+ and dist not in excluded_requirements
497
+ ):
498
+ requirements[dist] = metadata.version(dist)
499
+ except metadata.PackageNotFoundError:
500
+ from sqlmesh.core.console import get_console
501
+
502
+ get_console().log_warning(f"Failed to find package for {lib}.")
523
503
  return requirements
@@ -142,7 +142,6 @@ class OptimizedQueryCache:
142
142
 
143
143
  def _put(self, name: str, model: SqlModel) -> None:
144
144
  optimized_query = model.render_query()
145
-
146
145
  new_entry = OptimizedQueryCacheEntry(
147
146
  optimized_rendered_query=optimized_query,
148
147
  renderer_violations=model.violated_rules_for_query,
@@ -181,17 +180,10 @@ def load_optimized_query(
181
180
  assert _optimized_query_cache
182
181
  model, snapshot_id = model_snapshot_id
183
182
 
184
- entry_name = None
185
-
186
183
  if isinstance(model, SqlModel):
187
- try:
188
- entry_name = _optimized_query_cache.put(model)
189
- except:
190
- # this can happen if there is a query rendering error.
191
- # for example, the model query references some python library or function that was available
192
- # at the time the model was created but has since been removed locally
193
- logger.exception(f"Failed to cache optimized query for model '{model.name}'")
194
-
184
+ entry_name = _optimized_query_cache.put(model)
185
+ else:
186
+ entry_name = None
195
187
  return snapshot_id, entry_name
196
188
 
197
189
 
@@ -21,11 +21,7 @@ from sqlmesh.core.plan.definition import (
21
21
  UserProvidedFlags,
22
22
  earliest_interval_start,
23
23
  )
24
- from sqlmesh.core.schema_diff import (
25
- get_schema_differ,
26
- has_drop_alteration,
27
- get_dropped_column_names,
28
- )
24
+ from sqlmesh.core.schema_diff import SchemaDiffer, has_drop_alteration, get_dropped_column_names
29
25
  from sqlmesh.core.snapshot import (
30
26
  DeployabilityIndex,
31
27
  Snapshot,
@@ -82,12 +78,14 @@ class PlanBuilder:
82
78
  ensure_finalized_snapshots: Whether to compare against snapshots from the latest finalized
83
79
  environment state, or to use whatever snapshots are in the current environment state even if
84
80
  the environment is not finalized.
81
+ engine_schema_differ: Schema differ from the context engine adapter.
85
82
  interval_end_per_model: The mapping from model FQNs to target end dates.
86
83
  """
87
84
 
88
85
  def __init__(
89
86
  self,
90
87
  context_diff: ContextDiff,
88
+ engine_schema_differ: SchemaDiffer,
91
89
  start: t.Optional[TimeLike] = None,
92
90
  end: t.Optional[TimeLike] = None,
93
91
  execution_time: t.Optional[TimeLike] = None,
@@ -139,6 +137,7 @@ class PlanBuilder:
139
137
  self._backfill_models = backfill_models
140
138
  self._end = end or default_end
141
139
  self._apply = apply
140
+ self._engine_schema_differ = engine_schema_differ
142
141
  self._console = console or get_console()
143
142
  self._choices: t.Dict[SnapshotId, SnapshotChangeCategory] = {}
144
143
  self._user_provided_flags = user_provided_flags
@@ -494,7 +493,7 @@ class PlanBuilder:
494
493
  if columns_to_types_all_known(old_columns_to_types) and columns_to_types_all_known(
495
494
  new_columns_to_types
496
495
  ):
497
- schema_diff = get_schema_differ(snapshot.model.dialect).compare_columns(
496
+ schema_diff = self._engine_schema_differ.compare_columns(
498
497
  new.name,
499
498
  old_columns_to_types,
500
499
  new_columns_to_types,
@@ -723,27 +723,5 @@ def get_dropped_column_names(alter_expressions: t.List[exp.Alter]) -> t.List[str
723
723
  return dropped_columns
724
724
 
725
725
 
726
- def get_schema_differ(dialect: str) -> SchemaDiffer:
727
- """
728
- Returns the appropriate SchemaDiffer for a given dialect without initializing the engine adapter.
729
-
730
- Args:
731
- dialect: The dialect for which to get the schema differ.
732
-
733
- Returns:
734
- The SchemaDiffer instance configured for the given dialect.
735
- """
736
- from sqlmesh.core.engine_adapter import (
737
- DIALECT_TO_ENGINE_ADAPTER,
738
- DIALECT_ALIASES,
739
- EngineAdapter,
740
- )
741
-
742
- dialect = dialect.lower()
743
- dialect = DIALECT_ALIASES.get(dialect, dialect)
744
- engine_adapter_class = DIALECT_TO_ENGINE_ADAPTER.get(dialect, EngineAdapter)
745
- return getattr(engine_adapter_class, "SCHEMA_DIFFER", SchemaDiffer())
746
-
747
-
748
726
  def _get_name_and_type(struct: exp.ColumnDef) -> t.Tuple[exp.Identifier, exp.DataType]:
749
727
  return struct.this, struct.args["kind"]
sqlmesh/core/selector.py CHANGED
@@ -93,7 +93,7 @@ class Selector:
93
93
  }
94
94
 
95
95
  all_selected_models = self.expand_model_selections(
96
- model_selections, models={**env_models, **self._models}
96
+ model_selections, models={**self._models, **env_models}
97
97
  )
98
98
 
99
99
  dag: DAG[str] = DAG()
@@ -559,10 +559,7 @@ class ModelTest(unittest.TestCase):
559
559
  ) -> pd.DataFrame:
560
560
  query = values.get("query")
561
561
  if query:
562
- if not partial:
563
- query = self._add_missing_columns(query, columns)
564
-
565
- return self._execute(query)
562
+ return self._execute(self._add_missing_columns(query, columns))
566
563
 
567
564
  rows = values["rows"]
568
565
  if columns:
sqlmesh/dbt/loader.py CHANGED
@@ -28,6 +28,7 @@ from sqlmesh.utils import UniqueKeyDict
28
28
  from sqlmesh.utils.errors import ConfigError
29
29
  from sqlmesh.utils.jinja import (
30
30
  JinjaMacroRegistry,
31
+ extract_macro_references_and_variables,
31
32
  make_jinja_registry,
32
33
  )
33
34
 
@@ -239,11 +240,11 @@ class DbtLoader(Loader):
239
240
  def _load_environment_statements(self, macros: MacroRegistry) -> t.List[EnvironmentStatements]:
240
241
  """Loads dbt's on_run_start, on_run_end hooks into sqlmesh's before_all, after_all statements respectively."""
241
242
 
242
- hooks_by_package_name: t.Dict[str, EnvironmentStatements] = {}
243
- project_names: t.Set[str] = set()
243
+ environment_statements: t.List[EnvironmentStatements] = []
244
244
  dialect = self.config.dialect
245
245
  for project in self._load_projects():
246
246
  context = project.context
247
+ hooks_by_package_name: t.Dict[str, EnvironmentStatements] = {}
247
248
  for package_name, package in project.packages.items():
248
249
  context.set_and_render_variables(package.variables, package_name)
249
250
  on_run_start: t.List[str] = [
@@ -255,14 +256,18 @@ class DbtLoader(Loader):
255
256
  for on_run_hook in sorted(package.on_run_end.values(), key=lambda h: h.index)
256
257
  ]
257
258
 
258
- if on_run_start or on_run_end:
259
- dependencies = Dependencies()
260
- for hook in [*package.on_run_start.values(), *package.on_run_end.values()]:
261
- dependencies = dependencies.union(hook.dependencies)
259
+ if statements := on_run_start + on_run_end:
260
+ jinja_references, used_variables = extract_macro_references_and_variables(
261
+ *statements
262
+ )
262
263
 
263
- statements_context = context.context_for_dependencies(dependencies)
264
+ statements_context = context.context_for_dependencies(
265
+ Dependencies(
266
+ variables=used_variables,
267
+ )
268
+ )
264
269
  jinja_registry = make_jinja_registry(
265
- statements_context.jinja_macros, package_name, set(dependencies.macros)
270
+ statements_context.jinja_macros, package_name, jinja_references
266
271
  )
267
272
  jinja_registry.add_globals(statements_context.jinja_globals)
268
273
 
@@ -278,15 +283,15 @@ class DbtLoader(Loader):
278
283
  python_env={},
279
284
  jinja_macros=jinja_registry,
280
285
  )
281
- project_names.add(package_name)
282
-
283
- return [
284
- statements
285
- for _, statements in sorted(
286
- hooks_by_package_name.items(),
287
- key=lambda item: 0 if item[0] in project_names else 1,
288
- )
289
- ]
286
+ # Project hooks should be executed first and then rest of the packages
287
+ environment_statements = [
288
+ statements
289
+ for _, statements in sorted(
290
+ hooks_by_package_name.items(),
291
+ key=lambda item: 0 if item[0] == context.project_name else 1,
292
+ )
293
+ ]
294
+ return environment_statements
290
295
 
291
296
  def _compute_yaml_max_mtime_per_subfolder(self, root: Path) -> t.Dict[Path, float]:
292
297
  if not root.is_dir():