sqlmesh 0.200.1.dev19__py3-none-any.whl → 0.200.1.dev21__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.200.1.dev19'
21
- __version_tuple__ = version_tuple = (0, 200, 1, 'dev19')
20
+ __version__ = version = '0.200.1.dev21'
21
+ __version_tuple__ = version_tuple = (0, 200, 1, 'dev21')
sqlmesh/core/console.py CHANGED
@@ -289,11 +289,17 @@ class TableDiffConsole(abc.ABC):
289
289
 
290
290
  class BaseConsole(abc.ABC):
291
291
  @abc.abstractmethod
292
- def log_error(self, message: str) -> None:
292
+ def log_error(self, message: str, *args: t.Any, **kwargs: t.Any) -> None:
293
293
  """Display error info to the user."""
294
294
 
295
295
  @abc.abstractmethod
296
- def log_warning(self, short_message: str, long_message: t.Optional[str] = None) -> None:
296
+ def log_warning(
297
+ self,
298
+ short_message: str,
299
+ long_message: t.Optional[str] = None,
300
+ *args: t.Any,
301
+ **kwargs: t.Any,
302
+ ) -> None:
297
303
  """Display warning info to the user.
298
304
 
299
305
  Args:
@@ -3090,15 +3096,23 @@ class CaptureTerminalConsole(TerminalConsole):
3090
3096
  finally:
3091
3097
  self._errors = []
3092
3098
 
3093
- def log_warning(self, short_message: str, long_message: t.Optional[str] = None) -> None:
3099
+ def log_warning(
3100
+ self,
3101
+ short_message: str,
3102
+ long_message: t.Optional[str] = None,
3103
+ *args: t.Any,
3104
+ **kwargs: t.Any,
3105
+ ) -> None:
3094
3106
  if short_message not in self._warnings:
3095
3107
  self._warnings.append(short_message)
3096
- super().log_warning(short_message, long_message)
3108
+ if kwargs.pop("print", True):
3109
+ super().log_warning(short_message, long_message)
3097
3110
 
3098
- def log_error(self, message: str) -> None:
3111
+ def log_error(self, message: str, *args: t.Any, **kwargs: t.Any) -> None:
3099
3112
  if message not in self._errors:
3100
3113
  self._errors.append(message)
3101
- super().log_error(message)
3114
+ if kwargs.pop("print", True):
3115
+ super().log_error(message)
3102
3116
 
3103
3117
  def log_skipped_models(self, snapshot_names: t.Set[str]) -> None:
3104
3118
  if snapshot_names:
@@ -3135,6 +3149,11 @@ class MarkdownConsole(CaptureTerminalConsole):
3135
3149
  kwargs.pop("alert_block_collapsible_threshold", 200)
3136
3150
  )
3137
3151
 
3152
+ # capture_only = True: capture but dont print to console
3153
+ # capture_only = False: capture and also print to console
3154
+ self.warning_capture_only = kwargs.pop("warning_capture_only", False)
3155
+ self.error_capture_only = kwargs.pop("error_capture_only", False)
3156
+
3138
3157
  super().__init__(
3139
3158
  **{**kwargs, "console": RichConsole(no_color=True, width=kwargs.pop("width", None))}
3140
3159
  )
@@ -3409,6 +3428,12 @@ class MarkdownConsole(CaptureTerminalConsole):
3409
3428
  super().stop_promotion_progress(success)
3410
3429
  self._print("\n")
3411
3430
 
3431
+ def log_warning(self, short_message: str, long_message: t.Optional[str] = None) -> None:
3432
+ super().log_warning(short_message, long_message, print=not self.warning_capture_only)
3433
+
3434
+ def log_error(self, message: str) -> None:
3435
+ super().log_error(message, print=not self.error_capture_only)
3436
+
3412
3437
  def log_success(self, message: str) -> None:
3413
3438
  self._print(message)
3414
3439
 
@@ -3435,19 +3460,24 @@ class MarkdownConsole(CaptureTerminalConsole):
3435
3460
 
3436
3461
  def log_skipped_models(self, snapshot_names: t.Set[str]) -> None:
3437
3462
  if snapshot_names:
3438
- msg = " " + "\n ".join(snapshot_names)
3439
- self._print(f"**Skipped models**\n\n{msg}")
3463
+ self._print(f"**Skipped models**")
3464
+ for snapshot_name in snapshot_names:
3465
+ self._print(f"* `{snapshot_name}`")
3466
+ self._print("")
3440
3467
 
3441
3468
  def log_failed_models(self, errors: t.List[NodeExecutionFailedError]) -> None:
3442
3469
  if errors:
3443
- self._print("\n```\nFailed models\n")
3470
+ self._print("**Failed models**")
3444
3471
 
3445
3472
  error_messages = _format_node_errors(errors)
3446
3473
 
3447
3474
  for node_name, msg in error_messages.items():
3448
- self._print(f" **{node_name}**\n\n{msg}")
3475
+ self._print(f"* `{node_name}`\n")
3476
+ self._print(" ```")
3477
+ self._print(msg)
3478
+ self._print(" ```")
3449
3479
 
3450
- self._print("```\n")
3480
+ self._print("")
3451
3481
 
3452
3482
  def show_linter_violations(
3453
3483
  self, violations: t.List[RuleViolation], model: Model, is_error: bool = False
@@ -30,7 +30,7 @@ def github(ctx: click.Context, token: str) -> None:
30
30
  """Github Action CI/CD Bot. See https://sqlmesh.readthedocs.io/en/stable/integrations/github/ for details"""
31
31
  # set a larger width because if none is specified, it auto-detects 80 characters when running in GitHub Actions
32
32
  # which can result in surprise newlines when outputting dates to backfill
33
- set_console(MarkdownConsole(width=1000))
33
+ set_console(MarkdownConsole(width=1000, warning_capture_only=True, error_capture_only=True))
34
34
  ctx.obj["github"] = GithubController(
35
35
  paths=ctx.obj["paths"],
36
36
  token=token,
@@ -33,6 +33,9 @@ class GithubCICDBotConfig(BaseConfig):
33
33
  pr_environment_name: t.Optional[str] = None
34
34
  pr_min_intervals: t.Optional[int] = None
35
35
  prod_branch_names_: t.Optional[str] = Field(default=None, alias="prod_branch_name")
36
+ forward_only_branch_suffix_: t.Optional[str] = Field(
37
+ default=None, alias="forward_only_branch_suffix"
38
+ )
36
39
 
37
40
  @model_validator(mode="before")
38
41
  @classmethod
@@ -73,6 +76,10 @@ class GithubCICDBotConfig(BaseConfig):
73
76
  return True
74
77
  return self.skip_pr_backfill_
75
78
 
79
+ @property
80
+ def forward_only_branch_suffix(self) -> str:
81
+ return self.forward_only_branch_suffix_ or "-forward-only"
82
+
76
83
  FIELDS_FOR_ANALYTICS: t.ClassVar[t.Set[str]] = {
77
84
  "invalidate_environment_after_deploy",
78
85
  "enable_deploy_command",
@@ -83,4 +90,6 @@ class GithubCICDBotConfig(BaseConfig):
83
90
  "skip_pr_backfill",
84
91
  "pr_include_unmodified",
85
92
  "run_on_deploy_to_prod",
93
+ "pr_min_intervals",
94
+ "forward_only_branch_suffix",
86
95
  }
@@ -405,6 +405,7 @@ class GithubController:
405
405
  min_intervals=self.bot_config.pr_min_intervals,
406
406
  skip_backfill=self.bot_config.skip_pr_backfill,
407
407
  include_unmodified=self.bot_config.pr_include_unmodified,
408
+ forward_only=self.forward_only_plan,
408
409
  )
409
410
  assert self._pr_plan_builder
410
411
  return self._pr_plan_builder.build()
@@ -434,6 +435,7 @@ class GithubController:
434
435
  skip_linter=True,
435
436
  categorizer_config=self.bot_config.auto_categorize_changes,
436
437
  run=self.bot_config.run_on_deploy_to_prod,
438
+ forward_only=self.forward_only_plan,
437
439
  )
438
440
  assert self._prod_plan_builder
439
441
  return self._prod_plan_builder.build()
@@ -450,6 +452,7 @@ class GithubController:
450
452
  skip_tests=True,
451
453
  skip_linter=True,
452
454
  run=self.bot_config.run_on_deploy_to_prod,
455
+ forward_only=self.forward_only_plan,
453
456
  )
454
457
  assert self._prod_plan_with_gaps_builder
455
458
  return self._prod_plan_with_gaps_builder.build()
@@ -473,6 +476,13 @@ class GithubController:
473
476
  def pr_targets_prod_branch(self) -> bool:
474
477
  return self._pull_request.base.ref in self.bot_config.prod_branch_names
475
478
 
479
+ @property
480
+ def forward_only_plan(self) -> bool:
481
+ head_ref = self._pull_request.head.ref
482
+ if isinstance(head_ref, str):
483
+ return head_ref.endswith(self.bot_config.forward_only_branch_suffix)
484
+ return False
485
+
476
486
  @classmethod
477
487
  def _append_output(cls, key: str, value: str) -> None:
478
488
  """
@@ -485,6 +495,26 @@ class GithubController:
485
495
  with open(output_file, "a", encoding="utf-8") as fh:
486
496
  print(f"{key}={value}", file=fh)
487
497
 
498
+ def get_forward_only_plan_post_deployment_tip(self, plan: Plan) -> str:
499
+ if not plan.forward_only:
500
+ return ""
501
+
502
+ example_model_name = "<model name>"
503
+ for snapshot_id in sorted(plan.snapshots):
504
+ snapshot = plan.snapshots[snapshot_id]
505
+ if snapshot.is_incremental:
506
+ example_model_name = snapshot.node.name
507
+ break
508
+
509
+ return (
510
+ "> [!TIP]\n"
511
+ "> In order to see this forward-only plan retroactively apply to historical intervals on the production model, run the below for date ranges in scope:\n"
512
+ "> \n"
513
+ f"> `$ sqlmesh plan --restate-model {example_model_name} --start YYYY-MM-DD --end YYYY-MM-DD`\n"
514
+ ">\n"
515
+ "> Learn more: https://sqlmesh.readthedocs.io/en/stable/concepts/plans/?h=restate#restatement-plans"
516
+ )
517
+
488
518
  def get_plan_summary(self, plan: Plan) -> str:
489
519
  # use Verbosity.VERY_VERBOSE to prevent the list of models from being truncated
490
520
  # this is particularly important for the "Models needing backfill" list because
@@ -603,11 +633,11 @@ class GithubController:
603
633
 
604
634
  def _get_pr_environment_summary_failure(self, exception: t.Optional[Exception] = None) -> str:
605
635
  console_output = self._console.consume_captured_output()
636
+ failure_msg = ""
606
637
 
607
638
  if isinstance(exception, PlanError):
608
- failure_msg = f"Plan application failed.\n"
609
639
  if exception.args and (msg := exception.args[0]) and isinstance(msg, str):
610
- failure_msg += f"\n{msg}\n"
640
+ failure_msg += f"*{msg}*\n"
611
641
  if console_output:
612
642
  failure_msg += f"\n{console_output}"
613
643
  elif isinstance(exception, (SQLMeshError, SqlglotError, ValueError)):
@@ -713,6 +743,7 @@ class GithubController:
713
743
  Creates a PR environment from the logic present in the PR. If the PR contains changes that are
714
744
  uncategorized, then an error will be raised.
715
745
  """
746
+ self._console.consume_captured_output() # clear output buffer
716
747
  self._context.apply(self.pr_plan) # will raise if PR environment creation fails
717
748
 
718
749
  # update PR info comment
@@ -753,6 +784,11 @@ class GithubController:
753
784
  </details>
754
785
 
755
786
  """
787
+ if self.forward_only_plan:
788
+ plan_summary = (
789
+ f"{self.get_forward_only_plan_post_deployment_tip(self.prod_plan)}\n{plan_summary}"
790
+ )
791
+
756
792
  self.update_sqlmesh_comment_info(
757
793
  value=plan_summary,
758
794
  dedup_regex=None,
@@ -1095,6 +1131,7 @@ class GithubController:
1095
1131
  summary = "Got an action required conclusion but no plan error was provided. This is unexpected."
1096
1132
  else:
1097
1133
  summary = "**Generated Prod Plan**\n" + self.get_plan_summary(self.prod_plan)
1134
+
1098
1135
  return conclusion, title, summary
1099
1136
 
1100
1137
  self._update_check_handler(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqlmesh
3
- Version: 0.200.1.dev19
3
+ Version: 0.200.1.dev21
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=SOHAISeyY3qtH30N4aOOPbIi9trE7w0omXMhZqFV9dc,530
2
+ sqlmesh/_version.py,sha256=O6irubeLG1L1ES_dC9s03OMk3P36iamLtDEK-3x2Z60,530
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
@@ -11,7 +11,7 @@ sqlmesh/cli/options.py,sha256=-4ZclMIeIg8tghLowtmQNmpEFlm0QtOVEh40NqRdFHA,2499
11
11
  sqlmesh/cli/project_init.py,sha256=5JmDmUK0WJs39hM-p76uzHYdo1Hnx6fKuJg_pR3Hn5A,16419
12
12
  sqlmesh/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
13
  sqlmesh/core/_typing.py,sha256=e1f7xO2aZCy38M_3VoDy-XI3shyd99-IFctgIFSgXWM,497
14
- sqlmesh/core/console.py,sha256=g-xHXFAXhqrkqr_uADOgBtxSw2-96f5fo4YxF7irKq4,162837
14
+ sqlmesh/core/console.py,sha256=GQAWuxK8KZl6UclgPM51upiZXag9u8h1-HKotzjvblA,163884
15
15
  sqlmesh/core/constants.py,sha256=gUABXWYgH_XOSbaZAJsR5o1dmA61JDCvBDyxqqXX3LM,2630
16
16
  sqlmesh/core/context.py,sha256=GhR82ApxzgdDP9QN95vdKsOV7rAu8e9SVXvxw1dZExY,124800
17
17
  sqlmesh/core/context_diff.py,sha256=_Ur55fGLJ58Icsp44I4pVhdi7_JHiHq45mWIco9oFzk,21792
@@ -160,9 +160,9 @@ sqlmesh/integrations/llm.py,sha256=URgp3JKr06HYv6hGlJ7DQ9m3QCc41xRWK23HzmGpbDI,1
160
160
  sqlmesh/integrations/slack.py,sha256=nxLxu5WztGbZH3JdqnzyPqmJUMfRdJ_49LQ7zR-u39Q,6801
161
161
  sqlmesh/integrations/github/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
162
162
  sqlmesh/integrations/github/cicd/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
163
- sqlmesh/integrations/github/cicd/command.py,sha256=yW0dmsnL-gFTzUvTX1RvCOXDcsOdi0M7IVCpsLR9IN8,12194
164
- sqlmesh/integrations/github/cicd/config.py,sha256=Cnk1xClYZpYuyCjj1cJnAdlqe0RObd6bg75dnmStEBA,3351
165
- sqlmesh/integrations/github/cicd/controller.py,sha256=8tFGVmPxdOl6rMj6X-50ZbzfgaQHVl2fa4QbVhr3WY4,53248
163
+ sqlmesh/integrations/github/cicd/command.py,sha256=w3oWhvR4utEurB5UABXj4iVBIojkCnZ0wTazIFmVmnc,12246
164
+ sqlmesh/integrations/github/cicd/config.py,sha256=n4KyNv65y1gpys82iLAQZUb6g2pnzywyV_7cOrQXsnI,3669
165
+ sqlmesh/integrations/github/cicd/controller.py,sha256=TseVIg_sRm39d_lSFIqaqDwWEnmJwNc8vg2Wf1Px9VU,54746
166
166
  sqlmesh/lsp/api.py,sha256=_lA_buSPZFIoLJuINbS53LbNOOaHp3orb0uFTE3aCqI,2328
167
167
  sqlmesh/lsp/completions.py,sha256=Nt1XpFL_-QAIRM8BBBC6Ia6YG0hMZriiNJra7nGfD4Q,6676
168
168
  sqlmesh/lsp/context.py,sha256=szHCrmKAjg4wDRnSbjRU158wSGx9HBRnNswSNw9N0Qc,12439
@@ -284,7 +284,7 @@ sqlmesh/utils/pydantic.py,sha256=o_NsXbIpDqNpUA1Uc5xF0ZzoXQYYB0DfHwdRxBwXPNk,120
284
284
  sqlmesh/utils/rich.py,sha256=BpB47G0-JcU41LkGkeGKroexj4NOV5fox4-GOIXPgIc,3545
285
285
  sqlmesh/utils/windows.py,sha256=r7Yin4WEwbvH7sKA3hOJtRsclmscNuBeCklCfVPvtQs,611
286
286
  sqlmesh/utils/yaml.py,sha256=hvo5lmybYLcIx4p6CQwufS8X4MbwS60lEBnCmYJCXmU,2336
287
- sqlmesh-0.200.1.dev19.dist-info/licenses/LICENSE,sha256=OlMefUjgWJdULtf84BLW0AZZcY8DwdgQqb_1j2862j8,11346
287
+ sqlmesh-0.200.1.dev21.dist-info/licenses/LICENSE,sha256=OlMefUjgWJdULtf84BLW0AZZcY8DwdgQqb_1j2862j8,11346
288
288
  web/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
289
289
  web/client/dist/index.html,sha256=DAv6ABGogw4xRLb0T_92WP_S7XWyqVhwBgGKWtuQwU4,5472
290
290
  web/client/dist/assets/Audits-BuHAmFO8.js,sha256=zbYUF6WdUGMbnMm01GMNPb7xAJwj-o5wMdL56vPo2qE,1041
@@ -402,8 +402,8 @@ web/server/api/endpoints/models.py,sha256=kwj0s7uve3iZSMfmjkoPVMFMeY1sD0peTeyrWf
402
402
  web/server/api/endpoints/modules.py,sha256=8hqqgonGay_mJmpCw0IdbjsPhWlQH2VLdKAqha-myac,468
403
403
  web/server/api/endpoints/plan.py,sha256=bbbY50W_2MsZSTxOHWMKz0tbIm75nsRSlPy8GI2fg9Q,9306
404
404
  web/server/api/endpoints/table_diff.py,sha256=SCy3-pr5x6bEEiOaZGCDbiHQhpgxwmnz3Acqa2Ie6jI,2090
405
- sqlmesh-0.200.1.dev19.dist-info/METADATA,sha256=Ny0Zc2-k411vatccGqa-JIHmP1miLT84VvGm7zcDk5w,26679
406
- sqlmesh-0.200.1.dev19.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
407
- sqlmesh-0.200.1.dev19.dist-info/entry_points.txt,sha256=pIYKtg-u-uO7BK_Y0qrdmfyj0bYrcMc3Xiqr5WE3VFk,121
408
- sqlmesh-0.200.1.dev19.dist-info/top_level.txt,sha256=bspjo5vjELQkuN0qKuTFIbsrUt3Zeo8D2mlKeHG1LdE,12
409
- sqlmesh-0.200.1.dev19.dist-info/RECORD,,
405
+ sqlmesh-0.200.1.dev21.dist-info/METADATA,sha256=1u2BkMrE_TXnVs6u788bzl6l_la00zf6wLcPqMb_LuY,26679
406
+ sqlmesh-0.200.1.dev21.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
407
+ sqlmesh-0.200.1.dev21.dist-info/entry_points.txt,sha256=pIYKtg-u-uO7BK_Y0qrdmfyj0bYrcMc3Xiqr5WE3VFk,121
408
+ sqlmesh-0.200.1.dev21.dist-info/top_level.txt,sha256=bspjo5vjELQkuN0qKuTFIbsrUt3Zeo8D2mlKeHG1LdE,12
409
+ sqlmesh-0.200.1.dev21.dist-info/RECORD,,