sqlmesh 0.227.2.dev5__py3-none-any.whl → 0.227.2.dev7__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
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '0.227.2.dev5'
32
- __version_tuple__ = version_tuple = (0, 227, 2, 'dev5')
31
+ __version__ = version = '0.227.2.dev7'
32
+ __version_tuple__ = version_tuple = (0, 227, 2, 'dev7')
33
33
 
34
34
  __commit_id__ = commit_id = None
sqlmesh/core/context.py CHANGED
@@ -115,6 +115,7 @@ from sqlmesh.core.test import (
115
115
  ModelTestMetadata,
116
116
  generate_test,
117
117
  run_tests,
118
+ filter_tests_by_patterns,
118
119
  )
119
120
  from sqlmesh.core.user import User
120
121
  from sqlmesh.utils import UniqueKeyDict, Verbosity
@@ -146,8 +147,8 @@ if t.TYPE_CHECKING:
146
147
  from typing_extensions import Literal
147
148
 
148
149
  from sqlmesh.core.engine_adapter._typing import (
149
- BigframeSession,
150
150
  DF,
151
+ BigframeSession,
151
152
  PySparkDataFrame,
152
153
  PySparkSession,
153
154
  SnowparkSession,
@@ -398,6 +399,10 @@ class GenericContext(BaseContext, t.Generic[C]):
398
399
  self._standalone_audits: UniqueKeyDict[str, StandaloneAudit] = UniqueKeyDict(
399
400
  "standaloneaudits"
400
401
  )
402
+ self._model_test_metadata: t.List[ModelTestMetadata] = []
403
+ self._model_test_metadata_path_index: t.Dict[Path, t.List[ModelTestMetadata]] = {}
404
+ self._model_test_metadata_fully_qualified_name_index: t.Dict[str, ModelTestMetadata] = {}
405
+ self._models_with_tests: t.Set[str] = set()
401
406
  self._macros: UniqueKeyDict[str, ExecutableOrMacro] = UniqueKeyDict("macros")
402
407
  self._metrics: UniqueKeyDict[str, Metric] = UniqueKeyDict("metrics")
403
408
  self._jinja_macros = JinjaMacroRegistry()
@@ -636,6 +641,10 @@ class GenericContext(BaseContext, t.Generic[C]):
636
641
  self._excluded_requirements.clear()
637
642
  self._linters.clear()
638
643
  self._environment_statements = []
644
+ self._model_test_metadata.clear()
645
+ self._model_test_metadata_path_index.clear()
646
+ self._model_test_metadata_fully_qualified_name_index.clear()
647
+ self._models_with_tests.clear()
639
648
 
640
649
  for loader, project in zip(self._loaders, loaded_projects):
641
650
  self._jinja_macros = self._jinja_macros.merge(project.jinja_macros)
@@ -647,6 +656,15 @@ class GenericContext(BaseContext, t.Generic[C]):
647
656
  self._requirements.update(project.requirements)
648
657
  self._excluded_requirements.update(project.excluded_requirements)
649
658
  self._environment_statements.extend(project.environment_statements)
659
+ self._model_test_metadata.extend(project.model_test_metadata)
660
+ for metadata in project.model_test_metadata:
661
+ if metadata.path not in self._model_test_metadata_path_index:
662
+ self._model_test_metadata_path_index[metadata.path] = []
663
+ self._model_test_metadata_path_index[metadata.path].append(metadata)
664
+ self._model_test_metadata_fully_qualified_name_index[
665
+ metadata.fully_qualified_test_name
666
+ ] = metadata
667
+ self._models_with_tests.add(metadata.model_name)
650
668
 
651
669
  config = loader.config
652
670
  self._linters[config.project] = Linter.from_rules(
@@ -1049,6 +1067,11 @@ class GenericContext(BaseContext, t.Generic[C]):
1049
1067
  """Returns all registered standalone audits in this context."""
1050
1068
  return MappingProxyType(self._standalone_audits)
1051
1069
 
1070
+ @property
1071
+ def models_with_tests(self) -> t.Set[str]:
1072
+ """Returns all models with tests in this context."""
1073
+ return self._models_with_tests
1074
+
1052
1075
  @property
1053
1076
  def snapshots(self) -> t.Dict[str, Snapshot]:
1054
1077
  """Generates and returns snapshots based on models registered in this context.
@@ -2220,7 +2243,9 @@ class GenericContext(BaseContext, t.Generic[C]):
2220
2243
 
2221
2244
  pd.set_option("display.max_columns", None)
2222
2245
 
2223
- test_meta = self.load_model_tests(tests=tests, patterns=match_patterns)
2246
+ test_meta = self._select_tests(
2247
+ test_meta=self._model_test_metadata, tests=tests, patterns=match_patterns
2248
+ )
2224
2249
 
2225
2250
  result = run_tests(
2226
2251
  model_test_metadata=test_meta,
@@ -2782,6 +2807,33 @@ class GenericContext(BaseContext, t.Generic[C]):
2782
2807
  raise SQLMeshError(f"Gateway '{gateway}' not found in the available engine adapters.")
2783
2808
  return self.engine_adapter
2784
2809
 
2810
+ def _select_tests(
2811
+ self,
2812
+ test_meta: t.List[ModelTestMetadata],
2813
+ tests: t.Optional[t.List[str]] = None,
2814
+ patterns: t.Optional[t.List[str]] = None,
2815
+ ) -> t.List[ModelTestMetadata]:
2816
+ """Filter pre-loaded test metadata based on tests and patterns."""
2817
+
2818
+ if tests:
2819
+ filtered_tests = []
2820
+ for test in tests:
2821
+ if "::" in test:
2822
+ if test in self._model_test_metadata_fully_qualified_name_index:
2823
+ filtered_tests.append(
2824
+ self._model_test_metadata_fully_qualified_name_index[test]
2825
+ )
2826
+ else:
2827
+ test_path = Path(test)
2828
+ if test_path in self._model_test_metadata_path_index:
2829
+ filtered_tests.extend(self._model_test_metadata_path_index[test_path])
2830
+ test_meta = filtered_tests
2831
+
2832
+ if patterns:
2833
+ test_meta = filter_tests_by_patterns(test_meta, patterns)
2834
+
2835
+ return test_meta
2836
+
2785
2837
  def _snapshots(
2786
2838
  self, models_override: t.Optional[UniqueKeyDict[str, Model]] = None
2787
2839
  ) -> t.Dict[str, Snapshot]:
@@ -394,3 +394,17 @@ class DatabricksEngineAdapter(SparkEngineAdapter, GrantsFromInfoSchemaMixin):
394
394
  expressions.append(clustered_by_exp)
395
395
  properties = exp.Properties(expressions=expressions)
396
396
  return properties
397
+
398
+ def _build_column_defs(
399
+ self,
400
+ target_columns_to_types: t.Dict[str, exp.DataType],
401
+ column_descriptions: t.Optional[t.Dict[str, str]] = None,
402
+ is_view: bool = False,
403
+ ) -> t.List[exp.ColumnDef]:
404
+ # Databricks requires column types to be specified when adding column comments
405
+ # in CREATE MATERIALIZED VIEW statements. Override is_view to False to force
406
+ # column types to be included when comments are present.
407
+ if is_view and column_descriptions:
408
+ is_view = False
409
+
410
+ return super()._build_column_defs(target_columns_to_types, column_descriptions, is_view)
@@ -129,6 +129,21 @@ class NoMissingAudits(Rule):
129
129
  return self.violation()
130
130
 
131
131
 
132
+ class NoMissingUnitTest(Rule):
133
+ """All models must have a unit test found in the test/ directory yaml files"""
134
+
135
+ def check_model(self, model: Model) -> t.Optional[RuleViolation]:
136
+ # External models cannot have unit tests
137
+ if isinstance(model, ExternalModel):
138
+ return None
139
+
140
+ if model.name not in self.context.models_with_tests:
141
+ return self.violation(
142
+ violation_msg=f"Model {model.name} is missing unit test(s). Please add in the tests/ directory."
143
+ )
144
+ return None
145
+
146
+
132
147
  class NoMissingExternalModels(Rule):
133
148
  """All external models must be registered in the external_models.yaml file"""
134
149
 
sqlmesh/core/loader.py CHANGED
@@ -64,6 +64,7 @@ class LoadedProject:
64
64
  excluded_requirements: t.Set[str]
65
65
  environment_statements: t.List[EnvironmentStatements]
66
66
  user_rules: RuleSet
67
+ model_test_metadata: t.List[ModelTestMetadata]
67
68
 
68
69
 
69
70
  class CacheBase(abc.ABC):
@@ -243,6 +244,8 @@ class Loader(abc.ABC):
243
244
 
244
245
  user_rules = self._load_linting_rules()
245
246
 
247
+ model_test_metadata = self.load_model_tests()
248
+
246
249
  project = LoadedProject(
247
250
  macros=macros,
248
251
  jinja_macros=jinja_macros,
@@ -254,6 +257,7 @@ class Loader(abc.ABC):
254
257
  excluded_requirements=excluded_requirements,
255
258
  environment_statements=environment_statements,
256
259
  user_rules=user_rules,
260
+ model_test_metadata=model_test_metadata,
257
261
  )
258
262
  return project
259
263
 
@@ -20,6 +20,10 @@ class ModelTestMetadata(PydanticModel):
20
20
  def fully_qualified_test_name(self) -> str:
21
21
  return f"{self.path}::{self.test_name}"
22
22
 
23
+ @property
24
+ def model_name(self) -> str:
25
+ return self.body.get("model", "")
26
+
23
27
  def __hash__(self) -> int:
24
28
  return self.fully_qualified_test_name.__hash__()
25
29
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqlmesh
3
- Version: 0.227.2.dev5
3
+ Version: 0.227.2.dev7
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=v_spqQEhcnGaahp1yPvMqUIa6mhH3cs3Bc1CznxvCEA,7965
2
- sqlmesh/_version.py,sha256=Bz8PCaGCLCvM-UpGJrgN-1lIPfiCmdjQ8CSjFtdRJxc,721
2
+ sqlmesh/_version.py,sha256=mpzvXCVjwx1_Z7KgS_YADLshcqrw9_uxYiLUBo2eWQ8,721
3
3
  sqlmesh/magics.py,sha256=xLh3u4eqpVrKRVN5KF3X84RPRqjygAB9AJP1TXwH8hg,42086
4
4
  sqlmesh/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  sqlmesh/cicd/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -13,13 +13,13 @@ sqlmesh/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
13
  sqlmesh/core/_typing.py,sha256=PzXxMYnORq18JhblAOUttms3zPJZzZpIbfFA_jgKYPA,498
14
14
  sqlmesh/core/console.py,sha256=MYpVlciUY6rUuoqXyKfXTxD6a4-Bw4-ooATUTj_VHGg,172830
15
15
  sqlmesh/core/constants.py,sha256=BuQk43vluUm7LfP9nKp5o9qRhqIenWF_LiLXO_t_53c,2699
16
- sqlmesh/core/context.py,sha256=UxK9h9qvjwmnRTL7JLUEJFZvinWbJ_dDkXIKHq0s5ZU,131264
16
+ sqlmesh/core/context.py,sha256=PZcI06NldePYMDkvsMuZf8vGreDk7dzjnzX5rZ422AM,133656
17
17
  sqlmesh/core/context_diff.py,sha256=mxkJu0IthFMOlaQ_kcq5C09mlgkq2RQb-pG2rd-x_nA,21648
18
18
  sqlmesh/core/dialect.py,sha256=CnKcPj6BnREfu9Zn1OyS7hZ3ktnaX03ygOg91nADlTU,53029
19
19
  sqlmesh/core/environment.py,sha256=Kgs_gUEUI072mh0JJFWNRynrCxp1TzRHZhX_NWJRfXc,13142
20
20
  sqlmesh/core/janitor.py,sha256=zJRN48ENjKexeiqa1Kmwyj_HsEEEIAa8hsFD8gTCmfg,7194
21
21
  sqlmesh/core/lineage.py,sha256=LtiOztX1xIbFfWz-eb5dPZW4B0o2sI942_IM4YDbsso,3163
22
- sqlmesh/core/loader.py,sha256=2u91WKnXWHbAmyo9mluXHhSNwhe4r35QHv031S4OXjU,37291
22
+ sqlmesh/core/loader.py,sha256=sXGTeyDISd3Gtu9Ej9iEz1CMM4SXSwZMSBpbZNohE10,37458
23
23
  sqlmesh/core/macros.py,sha256=rkklwVnUEmEro4wpdel289mKhaS3x5_SPZrkYZt3Q9E,63173
24
24
  sqlmesh/core/node.py,sha256=2ejDwH1whl_ic1CRzX16Be-FQrosAf8pdyWb7oPzU6M,19895
25
25
  sqlmesh/core/notification_target.py,sha256=PPGoDrgbRKxr27vJEu03XqNTQLYTw0ZF_b0yAapxGeI,16158
@@ -64,7 +64,7 @@ sqlmesh/core/engine_adapter/base.py,sha256=t6c8HlWLmN_2GaWA3EV85Sq29al3fZpesE7Nr
64
64
  sqlmesh/core/engine_adapter/base_postgres.py,sha256=WTU0QingaTNM7n-mTVxS-sg4f6jFZGOSryK5IYacveY,7734
65
65
  sqlmesh/core/engine_adapter/bigquery.py,sha256=edBWbAbeXA4bOtVG-YNTQbt9qqwL9QFffZti8Ozv-Cw,60923
66
66
  sqlmesh/core/engine_adapter/clickhouse.py,sha256=GWGpwdxZd4RqLSAMlOHjtO8nPpSIo3zFeRWnj9eSOrM,36072
67
- sqlmesh/core/engine_adapter/databricks.py,sha256=452Og5LriNtvXk0DElUGmoR_pUFQvBgNZchpprTIJxA,15846
67
+ sqlmesh/core/engine_adapter/databricks.py,sha256=no6X3E9T31gI-GO7wZaq2r2E7SgLROHLzmot8reVZNY,16485
68
68
  sqlmesh/core/engine_adapter/duckdb.py,sha256=9AXeRhaYXBcYSmIavyFY9LUzfgh94qkTO98v0-suQ8I,7993
69
69
  sqlmesh/core/engine_adapter/fabric.py,sha256=wky02p3UVu0FvEZwqqb5XBW--XCc1JLMLrvY6TVqCdM,14172
70
70
  sqlmesh/core/engine_adapter/mixins.py,sha256=3rB7B2PZSB920BODO7k_kKqu6z0N-zj1etiRCYzpUcQ,27096
@@ -82,7 +82,7 @@ sqlmesh/core/linter/definition.py,sha256=1EOhKdF16jmeqISfcrR-8fzMdgXuxpB7wb3Qaep
82
82
  sqlmesh/core/linter/helpers.py,sha256=cwKXP4sL6azRtNVGbMfJ5_6Hqq5Xx2M2rRLCgH3Y3ag,10743
83
83
  sqlmesh/core/linter/rule.py,sha256=nB3o1rHyN44ZOg5ImICP16SeUHimf-12ObdXJjkTGyM,3964
84
84
  sqlmesh/core/linter/rules/__init__.py,sha256=gevzfb67vFqckTCoVAe_TBGf6hQ-YtE1_YuGuXyh1L0,77
85
- sqlmesh/core/linter/rules/builtin.py,sha256=C6_cAeZEmZphl9SjGUFMsHGrDKNjxQ5Y6g66eO8HCZg,11177
85
+ sqlmesh/core/linter/rules/builtin.py,sha256=mJjRrL97mTyqPgrUGk-1Ceml6ATCxElZVgiwi6WFqmU,11727
86
86
  sqlmesh/core/metric/__init__.py,sha256=H1HmoD5IwN4YWe9iJXyueLYNmTQFZwok5nSWNJcZIBQ,237
87
87
  sqlmesh/core/metric/definition.py,sha256=Yd5aVgsZCDPJ43aGP7WqtzZOuuSUtB8uJGVA6Jw9x9M,7201
88
88
  sqlmesh/core/metric/rewriter.py,sha256=GiSTHfn2kinqCfNPYgZPRk93JFLzVaaejHtHDQ0yXZI,7326
@@ -124,7 +124,7 @@ sqlmesh/core/state_sync/db/version.py,sha256=q5VDIIvY-585vTbvqPalU0N4qjG6RKs4gr8
124
124
  sqlmesh/core/test/__init__.py,sha256=e83TJPwPRR_rAG29Y0OVbZb-5oWVBzz-_wrcd22Qk10,418
125
125
  sqlmesh/core/test/context.py,sha256=-TjUrhM3WLtVPBgOMTkvRrnuZq7mT7BeIIyuCbrPePU,2332
126
126
  sqlmesh/core/test/definition.py,sha256=Lfflu-qgkqkI7T977F4h4X7c5Co7i3uBt5Efsi4XaZE,42219
127
- sqlmesh/core/test/discovery.py,sha256=WXIwPidLoacAGHoT_3TVFFEKamG89nHNtaMsZ01uGjs,1188
127
+ sqlmesh/core/test/discovery.py,sha256=5duKXgH4Lms7rXhJ8tOLCmCtqHpv7c7a4VJf12VkGw8,1278
128
128
  sqlmesh/core/test/result.py,sha256=6gOKEsERciHhcrw9TedtNr7g1ynTO7UwA5-PPrzvYuM,4564
129
129
  sqlmesh/core/test/runner.py,sha256=8I-cL7Q9CggLvET_GPkrXB2YjlyCIHrvbFbbRDnSHRE,6169
130
130
  sqlmesh/dbt/__init__.py,sha256=KUv-lW5sG9D2ceXAIzA4MLcjyhzq3E-7qJP4P_PH2EU,144
@@ -238,7 +238,7 @@ sqlmesh/utils/pydantic.py,sha256=-yppkVlw6iSBaSiKjbe7OChxL-u3urOS4-KCjJEgsRU,120
238
238
  sqlmesh/utils/rich.py,sha256=cwQ5nJ6sgz64xHtoh6_ec7ReV5YpsOGhMtUJnwoRfEI,3549
239
239
  sqlmesh/utils/windows.py,sha256=0F9RdpuuCoG5NiEDXvWlAGCiJ-59OjSAmgFF5wW05aY,1133
240
240
  sqlmesh/utils/yaml.py,sha256=KFBd7hsKNRTtRudGR7d410qUYffQv0EWRcDM8hVNNZg,3025
241
- sqlmesh-0.227.2.dev5.dist-info/licenses/LICENSE,sha256=OlMefUjgWJdULtf84BLW0AZZcY8DwdgQqb_1j2862j8,11346
241
+ sqlmesh-0.227.2.dev7.dist-info/licenses/LICENSE,sha256=OlMefUjgWJdULtf84BLW0AZZcY8DwdgQqb_1j2862j8,11346
242
242
  sqlmesh_dbt/__init__.py,sha256=awYS5y5mz-1NUmx6i5h5NSTJ7tidRl9NC0FAnFWSF6U,350
243
243
  sqlmesh_dbt/cli.py,sha256=p9foHjAW9ni7BTOJ2loynk47M0Sf43QIJZRggOzF5tc,6351
244
244
  sqlmesh_dbt/console.py,sha256=RwWLYnEZHzn9Xp-e2gbZvkdKbWbBLN146geI84mJitg,1132
@@ -363,8 +363,8 @@ web/server/api/endpoints/models.py,sha256=kwj0s7uve3iZSMfmjkoPVMFMeY1sD0peTeyrWf
363
363
  web/server/api/endpoints/modules.py,sha256=8hqqgonGay_mJmpCw0IdbjsPhWlQH2VLdKAqha-myac,468
364
364
  web/server/api/endpoints/plan.py,sha256=bbbY50W_2MsZSTxOHWMKz0tbIm75nsRSlPy8GI2fg9Q,9306
365
365
  web/server/api/endpoints/table_diff.py,sha256=8XTwgOh6QBbNy_hTM1JuHgRjbnie-pGPrphiW-FNLjQ,6058
366
- sqlmesh-0.227.2.dev5.dist-info/METADATA,sha256=IoFrCngJNn_KZY-PdY4qMp2EYD7cMuz9bxJh4UApwIM,26685
367
- sqlmesh-0.227.2.dev5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
368
- sqlmesh-0.227.2.dev5.dist-info/entry_points.txt,sha256=sHAf6tQczIM8xZoduN4qaUjV7QEPVUUW_LCT8EDUMv4,155
369
- sqlmesh-0.227.2.dev5.dist-info/top_level.txt,sha256=RQ-33FPe2IgL0rgossAfJkCRtqslz9b7wFARqiWLC5Q,24
370
- sqlmesh-0.227.2.dev5.dist-info/RECORD,,
366
+ sqlmesh-0.227.2.dev7.dist-info/METADATA,sha256=rH_cwyelAa0zkv2CW9gkxpX8wSQIZbKZPR8Vjic16TY,26685
367
+ sqlmesh-0.227.2.dev7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
368
+ sqlmesh-0.227.2.dev7.dist-info/entry_points.txt,sha256=sHAf6tQczIM8xZoduN4qaUjV7QEPVUUW_LCT8EDUMv4,155
369
+ sqlmesh-0.227.2.dev7.dist-info/top_level.txt,sha256=RQ-33FPe2IgL0rgossAfJkCRtqslz9b7wFARqiWLC5Q,24
370
+ sqlmesh-0.227.2.dev7.dist-info/RECORD,,