tinybird-cli 6.4.0__py3-none-any.whl → 6.4.2.dev0__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.
tinybird/__cli__.py CHANGED
@@ -4,5 +4,5 @@ __description__ = 'Tinybird Command Line Tool'
4
4
  __url__ = 'https://www.tinybird.co/docs/cli'
5
5
  __author__ = 'Tinybird'
6
6
  __author_email__ = 'support@tinybird.co'
7
- __version__ = '6.4.0'
8
- __revision__ = 'c8790f8'
7
+ __version__ = '6.4.2.dev0'
8
+ __revision__ = 'b2cf0b8'
tinybird/config.py CHANGED
@@ -37,7 +37,6 @@ LEGACY_HOSTS = {
37
37
  "https://api.wadus2.gcp.tinybird.co": "https://app.wadus.tinybird.co/gcp/wadus2",
38
38
  "https://api.wadus3.gcp.tinybird.co": "https://app.wadus.tinybird.co/gcp/wadus3",
39
39
  "https://api.wadus4.gcp.tinybird.co": "https://app.wadus.tinybird.co/gcp/wadus4",
40
- "https://api.wadus5.gcp.tinybird.co": "https://app.wadus.tinybird.co/gcp/wadus5",
41
40
  "https://api.wadus1.aws.tinybird.co": "https://app.wadus.tinybird.co/aws/wadus1",
42
41
  "https://api.wadus2.aws.tinybird.co": "https://app.wadus.tinybird.co/aws/wadus2",
43
42
  "https://api.wadus3.aws.tinybird.co": "https://app.wadus.tinybird.co/aws/wadus3",
@@ -59,7 +58,6 @@ LEGACY_HOSTS = {
59
58
  "https://ui.wadus2.gcp.tinybird.co": "https://app.wadus.tinybird.co/gcp/wadus2",
60
59
  "https://ui.wadus3.gcp.tinybird.co": "https://app.wadus.tinybird.co/gcp/wadus3",
61
60
  "https://ui.wadus4.gcp.tinybird.co": "https://app.wadus.tinybird.co/gcp/wadus4",
62
- "https://ui.wadus5.gcp.tinybird.co": "https://app.wadus.tinybird.co/gcp/wadus5",
63
61
  "https://ui.wadus1.aws.tinybird.co": "https://app.wadus.tinybird.co/aws/wadus1",
64
62
  "https://ui.wadus2.aws.tinybird.co": "https://app.wadus.tinybird.co/aws/wadus2",
65
63
  "https://ui.wadus3.aws.tinybird.co": "https://app.wadus.tinybird.co/aws/wadus3",
@@ -594,7 +594,9 @@ class Deployment:
594
594
  ):
595
595
  if self.is_git_release:
596
596
  if not self.current_release:
597
- raise CLIGitReleaseException(FeedbackManager.error_init_release(workspace=self.current_ws["name"]))
597
+ raise CLIGitReleaseException(
598
+ FeedbackManager.error_init_release(workspace=self.current_ws["name"], cli="tb")
599
+ )
598
600
  self.cli_git_release = CLIGitRelease()
599
601
  if not use_main:
600
602
  self.cli_git_release.validate_local_for_release(self.current_release, check_outdated=check_outdated)
@@ -613,7 +615,7 @@ class Deployment:
613
615
  # error until we support it https://gitlab.com/tinybird/analytics/-/issues/9655
614
616
  for d in diffs:
615
617
  if self.cli_git_release.ChangeType(d.change_type) == self.cli_git_release.ChangeType.RENAMED:
616
- raise CLIGitReleaseException(FeedbackManager.error_unsupported_diff())
618
+ raise CLIGitReleaseException(FeedbackManager.error_unsupported_diff(cli="tb"))
617
619
  if not diffs:
618
620
  click.echo(FeedbackManager.info_git_release_no_diffs())
619
621
  changed = self.cli_git_release.get_changes_from_diffs(diffs, filenames)
@@ -701,10 +703,10 @@ class Deployment:
701
703
  release = await self.cli_git_release.update_release(self.tb_client, self.current_ws, commit)
702
704
  click.echo(FeedbackManager.success_git_release(release_commit=release["commit"]))
703
705
  else:
704
- click.echo(FeedbackManager.warning_no_release())
706
+ click.echo(FeedbackManager.warning_no_release(cli="tb"))
705
707
  except Exception as e:
706
708
  if self.only_changes:
707
- click.echo(FeedbackManager.warning_no_release())
709
+ click.echo(FeedbackManager.warning_no_release(cli="tb"))
708
710
  else:
709
711
  raise e
710
712
 
@@ -3383,7 +3385,7 @@ async def new_ds(
3383
3385
 
3384
3386
  if alter_response:
3385
3387
  if git_release and not skip_confirmation:
3386
- click.echo(FeedbackManager.info_custom_deployment())
3388
+ click.echo(FeedbackManager.info_custom_deployment(cli="tb"))
3387
3389
  click.echo("***************************************")
3388
3390
  click.echo("***************************************")
3389
3391
  click.echo(FeedbackManager.info_datasource_doesnt_match(datasource=ds_name))
@@ -4270,13 +4272,13 @@ async def folder_push(
4270
4272
  existing_resources: List[str] = [x["name"] for x in datasources] + [x["name"] for x in pipes]
4271
4273
  # replace workspace mapping names
4272
4274
  for old_ws, new_ws in workspace_map.items():
4273
- existing_resources = [re.sub(f"^{old_ws}\.", f"{new_ws}.", x) for x in existing_resources]
4275
+ existing_resources = [re.sub(f"^{old_ws}\\.", f"{new_ws}.", x) for x in existing_resources]
4274
4276
 
4275
4277
  remote_resource_names = [get_remote_resource_name_without_version(x) for x in existing_resources]
4276
4278
 
4277
4279
  # replace workspace mapping names
4278
4280
  for old_ws, new_ws in workspace_map.items():
4279
- remote_resource_names = [re.sub(f"^{old_ws}\.", f"{new_ws}.", x) for x in remote_resource_names]
4281
+ remote_resource_names = [re.sub(f"^{old_ws}\\.", f"{new_ws}.", x) for x in remote_resource_names]
4280
4282
 
4281
4283
  if not filenames:
4282
4284
  filenames = get_project_filenames(folder)
tinybird/git_settings.py CHANGED
@@ -71,7 +71,7 @@ VERSION=0.0.0
71
71
  # TB_SKIP_REGRESSION=0
72
72
 
73
73
  # Use `OBFUSCATE_REGEX_PATTERN` and `OBFUSCATE_PATTERN_SEPARATOR` environment variables to define a regex pattern and a separator (in case of a single string with multiple regex) to obfuscate secrets in the CLI output.
74
- # OBFUSCATE_REGEX_PATTERN="https://(www\.)?[^/]+||^Follow these instructions =>"
74
+ # OBFUSCATE_REGEX_PATTERN="https://(www\\.)?[^/]+||^Follow these instructions =>"
75
75
  # OBFUSCATE_PATTERN_SEPARATOR=||
76
76
  ##########
77
77
  """
tinybird/sql.py CHANGED
@@ -6,7 +6,7 @@ from dataclasses import dataclass
6
6
  from typing import Any, Dict, Iterable, List, Optional
7
7
 
8
8
  valid_chars_name: str = string.ascii_letters + string.digits + "._`*<>+-'"
9
- valid_chars_fn: str = valid_chars_name + "[](),=!?:/ \n\t\r"
9
+ valid_chars_fn: str = valid_chars_name + "[](),=!?:/% \n\t\r"
10
10
  # Use sets for O(1) membership checks in hot loops
11
11
  _VALID_CHARS_NAME_SET = set(valid_chars_name)
12
12
  _VALID_CHARS_FN_SET = set(valid_chars_fn)
@@ -233,10 +233,21 @@ def try_to_fix_nullable_in_simple_aggregating_function(t: str) -> Optional[str]:
233
233
  if match := _RE_TRY_FIX_NULLABLE_SAF.search(t):
234
234
  fn = match.group(1)
235
235
  inner_type = match.group(2)
236
- result = f"SimpleAggregateFunction({fn}, Nullable({inner_type}))"
236
+ if "Nullable(" not in inner_type:
237
+ result = f"SimpleAggregateFunction({fn}, Nullable({inner_type}))"
237
238
  return result
238
239
 
239
240
 
241
+ def wrap_nullable(col: dict[str, Any]):
242
+ if col["nullable"]:
243
+ if (col_type := try_to_fix_nullable_in_simple_aggregating_function(col["type"])) is None:
244
+ # Skip wrapping if Nullable already present, e.g. LowCardinality(Nullable(String))
245
+ col_type = col["type"] if "Nullable(" in col["type"] else "Nullable(%s)" % col["type"]
246
+ else:
247
+ col_type = col["type"]
248
+ return col_type
249
+
250
+
240
251
  def schema_to_sql_columns(schema: List[Dict[str, Any]], skip_jsonpaths: bool = False) -> List[str]:
241
252
  """return an array with each column in SQL
242
253
  >>> schema_to_sql_columns([{'name': 'temperature', 'type': 'Float32', 'codec': None, 'default_value': None, 'nullable': False, 'normalized_name': 'temperature'}, {'name': 'temperature_delta', 'type': 'Float32', 'codec': 'CODEC(Delta(4), LZ4))', 'default_value': 'MATERIALIZED temperature', 'nullable': False, 'normalized_name': 'temperature_delta'}])
@@ -255,12 +266,7 @@ def schema_to_sql_columns(schema: List[Dict[str, Any]], skip_jsonpaths: bool = F
255
266
  columns: List[str] = []
256
267
  for x in schema:
257
268
  name = x["normalized_name"] if "normalized_name" in x else x["name"]
258
- if x["nullable"]:
259
- if (_type := try_to_fix_nullable_in_simple_aggregating_function(x["type"])) is None:
260
- # Skip wrapping if Nullable already present, e.g. LowCardinality(Nullable(String))
261
- _type = x["type"] if "Nullable(" in x["type"] else "Nullable(%s)" % x["type"]
262
- else:
263
- _type = x["type"]
269
+ _type = wrap_nullable(x)
264
270
  parts = [col_name(name, backquotes=True), _type]
265
271
  if x.get("jsonpath", None) and not skip_jsonpaths:
266
272
  parts.append(f"`json:{x['jsonpath']}`")
tinybird/sql_template.py CHANGED
@@ -20,6 +20,8 @@ from tinybird.context import (
20
20
  from .datatypes import testers
21
21
  from .tornado_template import VALID_CUSTOM_FUNCTION_NAMES, SecurityException, Template
22
22
 
23
+ VALID_ACTIVATE_FEATURES = frozenset(["analyzer", "parallel_replicas", "optimize_aggregation_in_order"])
24
+
23
25
  TB_SECRET_IN_TEST_MODE = "tb_secret_dont_raise"
24
26
  TB_SECRET_PREFIX = "tb_secret_"
25
27
  CH_PARAM_PREFIX = "param_"
@@ -1463,10 +1465,9 @@ def generate(self, **kwargs) -> Tuple[str, TemplateExecutionResults]:
1463
1465
  return Expression(f"-- cache_ttl {ttl_expression}\n")
1464
1466
 
1465
1467
  def set_activate(feature):
1466
- valid_features = ("analyzer", "parallel_replicas")
1467
- if feature not in valid_features:
1468
+ if feature not in VALID_ACTIVATE_FEATURES:
1468
1469
  raise SQLTemplateException(f"'{feature}' is not a valid 'activate' argument")
1469
- template_execution_results["activate"] = feature
1470
+ template_execution_results.setdefault("activate", set()).add(feature)
1470
1471
  return Expression(f"-- activate {feature}\n")
1471
1472
 
1472
1473
  def set_disable_feature(feature):
@@ -2143,7 +2144,7 @@ def get_var_data(content, node_id=None):
2143
2144
  return [dict(name=k, **v) for k, v in vars.items()]
2144
2145
 
2145
2146
 
2146
- def get_var_names_and_types(t, node_id=None):
2147
+ def get_var_names_and_types(t: Template, node_id: Optional[str] = None) -> List[Dict[str, Any]]:
2147
2148
  """
2148
2149
  >>> get_var_names_and_types(Template("SELECT * FROM filter_value WHERE description = {{Float32(with_value, 0.0)}}"))
2149
2150
  [{'name': 'with_value', 'type': 'Float32', 'default': 0.0}]
tinybird/sql_toolset.py CHANGED
@@ -75,19 +75,6 @@ def explain_plan(sql: str) -> str:
75
75
  return chquery.explain_ast(sql)
76
76
 
77
77
 
78
- @dataclass(frozen=True)
79
- class ColumnInfo:
80
- name: str
81
- type: str
82
- nullable: bool
83
- default_specifier: str = ""
84
- default_expression: str | None = None
85
- codec: str | None = None
86
- comment: str | None = None
87
- ttl: str | None = None
88
- is_primary_key: bool = False
89
-
90
-
91
78
  @dataclass
92
79
  class MaterializedViewTarget:
93
80
  database: Optional[str]
@@ -118,11 +105,6 @@ def parse_materialized_view_target(create_table_query: str) -> Optional[Material
118
105
  )
119
106
 
120
107
 
121
- def get_columns_from_create_query(sql_schema: str) -> list[ColumnInfo]:
122
- columns = chquery.get_columns_from_create_query(sql_schema)
123
- return [ColumnInfo(**col) for col in columns]
124
-
125
-
126
108
  def has_join(sql: str) -> bool:
127
109
  return any(line.rstrip().startswith("TableJoin") for line in explain_plan(sql).split())
128
110
 
@@ -234,7 +234,7 @@ async def auth_use(region_name_or_host_or_id: str) -> None:
234
234
  config.set_host(host)
235
235
 
236
236
  if not await try_authenticate(config, regions):
237
- msg = FeedbackManager.error_wrong_config_file(config_file=config._path)
237
+ msg = FeedbackManager.error_wrong_config_file(config_file=config._path, cli="tb")
238
238
  raise CLIAuthException(msg)
239
239
 
240
240
  config.persist_to_file()
@@ -79,7 +79,7 @@ async def print_releases(config: CLIConfig):
79
79
  )
80
80
  @coro
81
81
  async def release_generate(semver: str) -> None:
82
- click.echo(FeedbackManager.warning_deprecated_releases())
82
+ click.echo(FeedbackManager.warning_deprecated_releases(cli="tb"))
83
83
  if os.path.exists(".tinyenv"):
84
84
  async with aiofiles.open(".tinyenv", "r") as env_file:
85
85
  lines = await env_file.readlines()
@@ -147,7 +147,7 @@ set -euxo pipefail
147
147
  )
148
148
  @coro
149
149
  async def release_create(semver: str, commit: Optional[str]) -> None:
150
- click.echo(FeedbackManager.warning_deprecated_releases())
150
+ click.echo(FeedbackManager.warning_deprecated_releases(cli="tb"))
151
151
  config = CLIConfig.get_project_config()
152
152
  _ = await try_update_config_with_remote(config, only_if_needed=True)
153
153
 
@@ -165,7 +165,7 @@ async def release_promote(semver: str) -> None:
165
165
  """
166
166
  The oldest rollback Release will be automatically removed if no usage, otherwise export TB_FORCE_REMOVE_OLDEST_ROLLBACK="1" to force deletion
167
167
  """
168
- click.echo(FeedbackManager.warning_deprecated_releases())
168
+ click.echo(FeedbackManager.warning_deprecated_releases(cli="tb"))
169
169
  config = CLIConfig.get_project_config()
170
170
  _ = await try_update_config_with_remote(config, only_if_needed=True)
171
171
 
@@ -191,7 +191,7 @@ async def release_promote(semver: str) -> None:
191
191
  )
192
192
  @coro
193
193
  async def release_preview(semver: str) -> None:
194
- click.echo(FeedbackManager.warning_deprecated_releases())
194
+ click.echo(FeedbackManager.warning_deprecated_releases(cli="tb"))
195
195
  config = CLIConfig.get_project_config()
196
196
  _ = await try_update_config_with_remote(config, only_if_needed=True)
197
197
 
@@ -208,7 +208,7 @@ async def release_preview(semver: str) -> None:
208
208
  @click.option("--yes", is_flag=True, default=False, help="Do not ask for confirmation")
209
209
  @coro
210
210
  async def release_rollback(yes: bool) -> None:
211
- click.echo(FeedbackManager.warning_deprecated_releases())
211
+ click.echo(FeedbackManager.warning_deprecated_releases(cli="tb"))
212
212
  config = CLIConfig.get_project_config()
213
213
  _ = await try_update_config_with_remote(config, only_if_needed=False)
214
214
 
@@ -252,7 +252,7 @@ async def release_rollback(yes: bool) -> None:
252
252
  )
253
253
  @coro
254
254
  async def release_rm(semver: str, oldest_rollback: bool, force: bool, yes: bool, dry_run: bool) -> None:
255
- click.echo(FeedbackManager.warning_deprecated_releases())
255
+ click.echo(FeedbackManager.warning_deprecated_releases(cli="tb"))
256
256
  if (not semver and not oldest_rollback) or (semver and oldest_rollback):
257
257
  raise CLIException(FeedbackManager.error_release_rm_param())
258
258
 
@@ -278,7 +278,6 @@ async def release_rm(semver: str, oldest_rollback: bool, force: bool, yes: bool,
278
278
  @cli.group()
279
279
  def branch() -> None:
280
280
  """Branch commands. Branches are an experimental feature only available in beta. Running branch commands without activation will return an error"""
281
- pass
282
281
 
283
282
 
284
283
  @branch.command(name="ls")
@@ -427,7 +426,7 @@ async def delete_branch(branch_name_or_id: str, yes: bool) -> None:
427
426
  raise CLIBranchException(FeedbackManager.error_exception(error=str(e)))
428
427
 
429
428
  if not workspace_to_delete:
430
- raise CLIBranchException(FeedbackManager.error_branch(branch=branch_name_or_id))
429
+ raise CLIBranchException(FeedbackManager.error_branch(branch=branch_name_or_id, cli="tb"))
431
430
 
432
431
  if yes or click.confirm(FeedbackManager.warning_confirm_delete_branch(branch=workspace_to_delete["name"])):
433
432
  need_to_switch_to_main = workspace_to_delete.get("main") and config["id"] == workspace_to_delete["id"]
@@ -450,7 +449,7 @@ async def delete_branch(branch_name_or_id: str, yes: bool) -> None:
450
449
  if workspace_main:
451
450
  await switch_to_workspace_by_user_workspace_data(config, workspace_main)
452
451
  else:
453
- raise CLIException(FeedbackManager.error_switching_to_main())
452
+ raise CLIException(FeedbackManager.error_switching_to_main(cli="tb"))
454
453
 
455
454
 
456
455
  @branch.command(
@@ -127,7 +127,7 @@ async def cli(
127
127
  click.echo(FeedbackManager.warning_development_cli())
128
128
 
129
129
  if "x.y.z" not in CURRENT_VERSION and latest_version != CURRENT_VERSION:
130
- click.echo(FeedbackManager.warning_update_version(latest_version=latest_version))
130
+ click.echo(FeedbackManager.warning_update_version(latest_version=latest_version, cli="tb"))
131
131
  click.echo(FeedbackManager.warning_current_version(current_version=CURRENT_VERSION))
132
132
 
133
133
  if debug:
@@ -237,7 +237,7 @@ async def init(
237
237
  )
238
238
 
239
239
  if current_ws.get("is_branch"):
240
- raise CLIException(FeedbackManager.error_not_allowed_in_branch())
240
+ raise CLIException(FeedbackManager.error_not_allowed_in_branch(cli="tb"))
241
241
 
242
242
  await folder_init(client, folder, generate_datasources, generate_releases=True, force=force)
243
243
 
@@ -255,7 +255,7 @@ async def init(
255
255
 
256
256
  if sync_git:
257
257
  if not cli_git_release.is_main_branch() and not override_commit:
258
- raise CLIGitReleaseException(FeedbackManager.error_no_git_main_branch())
258
+ raise CLIGitReleaseException(FeedbackManager.error_no_git_main_branch(cli="tb"))
259
259
 
260
260
  if not cli_git_release.is_dottinyb_ignored():
261
261
  raise CLIGitReleaseException(
@@ -316,7 +316,7 @@ async def init(
316
316
 
317
317
  else:
318
318
  click.echo(FeedbackManager.info_no_git_release_yet(workspace=current_ws["name"]))
319
- click.echo(FeedbackManager.info_diff_resources_for_git_init())
319
+ click.echo(FeedbackManager.info_diff_resources_for_git_init(cli="tb"))
320
320
  changed = await diff_command(
321
321
  [], True, client, with_print=False, verbose=False, clean_up=True, progress_bar=True
322
322
  )
@@ -342,7 +342,7 @@ async def init(
342
342
  if cli_git_release.is_dirty_to_init():
343
343
  raise CLIGitReleaseException(
344
344
  FeedbackManager.error_commit_changes_to_init_release(
345
- path=cli_git_release.path, git_output=cli_git_release.status()
345
+ path=cli_git_release.path, git_output=cli_git_release.status(), cli="tb"
346
346
  )
347
347
  )
348
348
  try:
@@ -732,19 +732,19 @@ async def diff(
732
732
  for workspace in response["workspaces"]:
733
733
  if config["id"] == workspace["id"]:
734
734
  if not workspace.get("is_branch"):
735
- raise CLIException(FeedbackManager.error_not_a_branch())
735
+ raise CLIException(FeedbackManager.error_not_a_branch(cli="tb"))
736
736
 
737
737
  origin = workspace["main"]
738
738
  workspace = await get_current_main_workspace(config)
739
739
 
740
740
  if not workspace:
741
- raise CLIException(FeedbackManager.error_workspace(workspace=origin))
741
+ raise CLIException(FeedbackManager.error_workspace(workspace=origin, cli="tb"))
742
742
 
743
743
  ws_client = _get_tb_client(workspace["token"], config["host"])
744
744
  break
745
745
 
746
746
  if not ws_client:
747
- raise CLIException(FeedbackManager.error_workspace(workspace=origin))
747
+ raise CLIException(FeedbackManager.error_workspace(workspace=origin, cli="tb"))
748
748
  changed = await diff_command(
749
749
  list(filename) if filename else None, fmt, ws_client, no_color, with_print=not only_resources_changed
750
750
  )
@@ -1347,7 +1347,7 @@ async def deploy(
1347
1347
  current_semver = release.get("semver")
1348
1348
 
1349
1349
  if not current_semver:
1350
- click.echo(FeedbackManager.error_init_release(workspace=current_ws.get("name")))
1350
+ click.echo(FeedbackManager.error_init_release(workspace=current_ws.get("name"), cli="tb"))
1351
1351
  sys.exit(1)
1352
1352
 
1353
1353
  release_created = False
@@ -1358,7 +1358,7 @@ async def deploy(
1358
1358
  if not semver:
1359
1359
  semver = current_semver
1360
1360
  else:
1361
- click.echo(FeedbackManager.warning_deprecated_releases())
1361
+ click.echo(FeedbackManager.warning_deprecated_releases(cli="tb"))
1362
1362
 
1363
1363
  if semver and current_semver:
1364
1364
  new_version = version.parse(semver.split("-snapshot")[0])
@@ -234,7 +234,7 @@ variable to '1' or 'true'."""
234
234
  try:
235
235
  self.main(*args, **kwargs)
236
236
  except AuthNoTokenException:
237
- error_msg = FeedbackManager.error_notoken()
237
+ error_msg = FeedbackManager.error_notoken(cli="tb")
238
238
  error_event = "auth_error"
239
239
  exit_code = 1
240
240
  except AuthException as ex:
@@ -315,7 +315,6 @@ async def folder_init(
315
315
  except FileExistsError:
316
316
  if not force:
317
317
  click.echo(FeedbackManager.info_path_already_exists(path=x))
318
- pass
319
318
 
320
319
  if generate_datasources:
321
320
  for format in SUPPORTED_FORMATS:
@@ -690,7 +689,7 @@ async def create_workspace_branch(
690
689
  try:
691
690
  workspace = await get_current_workspace(config)
692
691
  if not workspace:
693
- raise CLIWorkspaceException(FeedbackManager.error_workspace())
692
+ raise CLIWorkspaceException(FeedbackManager.error_workspace(cli="tb"))
694
693
 
695
694
  if not branch_name:
696
695
  click.echo(FeedbackManager.info_workspace_branch_create_greeting())
@@ -1195,7 +1194,7 @@ def validate_string_connector_param(param, s):
1195
1194
 
1196
1195
  async def validate_connection_name(client, connection_name, service):
1197
1196
  if await client.get_connector(connection_name, service) is not None:
1198
- raise CLIConnectionException(FeedbackManager.error_connection_already_exists(name=connection_name))
1197
+ raise CLIConnectionException(FeedbackManager.error_connection_already_exists(name=connection_name, cli="tb"))
1199
1198
 
1200
1199
 
1201
1200
  def _get_setting_value(connection, setting, sensitive_settings):
@@ -1223,9 +1222,9 @@ async def switch_workspace(config: CLIConfig, workspace_name_or_id: str, only_en
1223
1222
 
1224
1223
  if not workspace:
1225
1224
  if only_environments:
1226
- raise CLIException(FeedbackManager.error_branch(branch=workspace_name_or_id))
1225
+ raise CLIException(FeedbackManager.error_branch(branch=workspace_name_or_id, cli="tb"))
1227
1226
  else:
1228
- raise CLIException(FeedbackManager.error_workspace(workspace=workspace_name_or_id))
1227
+ raise CLIException(FeedbackManager.error_workspace(workspace=workspace_name_or_id, cli="tb"))
1229
1228
 
1230
1229
  config.set_token(workspace["token"])
1231
1230
  config.set_token_for_host(workspace["token"], config.get_host())
@@ -1391,17 +1390,17 @@ async def get_host_from_region(
1391
1390
  try:
1392
1391
  host = regions[index - 1]["api_host"]
1393
1392
  except Exception:
1394
- raise CLIException(FeedbackManager.error_getting_region_by_index())
1393
+ raise CLIException(FeedbackManager.error_getting_region_by_index(cli="tb"))
1395
1394
  except ValueError:
1396
1395
  region_name = region_name_or_host_or_id.lower()
1397
1396
  try:
1398
1397
  region = get_region_from_host(region_name, regions)
1399
1398
  host = region["api_host"] if region else None
1400
1399
  except Exception:
1401
- raise CLIException(FeedbackManager.error_getting_region_by_name_or_url())
1400
+ raise CLIException(FeedbackManager.error_getting_region_by_name_or_url(cli="tb"))
1402
1401
 
1403
1402
  if not host:
1404
- raise CLIException(FeedbackManager.error_getting_region_by_name_or_url())
1403
+ raise CLIException(FeedbackManager.error_getting_region_by_name_or_url(cli="tb"))
1405
1404
 
1406
1405
  return regions, host
1407
1406
 
@@ -1562,7 +1561,7 @@ async def try_authenticate(
1562
1561
  break
1563
1562
 
1564
1563
  if not authenticated:
1565
- raise CLIAuthException(FeedbackManager.error_invalid_token())
1564
+ raise CLIAuthException(FeedbackManager.error_invalid_token(cli="tb"))
1566
1565
 
1567
1566
  config.persist_to_file()
1568
1567
 
@@ -1899,14 +1898,14 @@ async def validate_aws_iamrole_connection_name(
1899
1898
  ) -> str:
1900
1899
  if connection_name and no_validate is False:
1901
1900
  if await client.get_connector(connection_name) is not None:
1902
- raise CLIConnectionException(FeedbackManager.info_connection_already_exists(name=connection_name))
1901
+ raise CLIConnectionException(FeedbackManager.info_connection_already_exists(name=connection_name, cli="tb"))
1903
1902
  else:
1904
1903
  while not connection_name:
1905
1904
  connection_name = click.prompt("Enter the name for this connection", default=None, show_default=False)
1906
1905
  assert isinstance(connection_name, str)
1907
1906
 
1908
1907
  if no_validate is False and await client.get_connector(connection_name) is not None:
1909
- click.echo(FeedbackManager.info_connection_already_exists(name=connection_name))
1908
+ click.echo(FeedbackManager.info_connection_already_exists(name=connection_name, cli="tb"))
1910
1909
  connection_name = None
1911
1910
  assert isinstance(connection_name, str)
1912
1911
  return connection_name
@@ -556,10 +556,10 @@ async def datasource_share(ctx: Context, datasource_name: str, workspace_name_or
556
556
  current_workspace = next((workspace for workspace in workspaces if workspace["id"] == config["id"]), None)
557
557
 
558
558
  if not destination_workspace:
559
- raise CLIDatasourceException(FeedbackManager.error_workspace(workspace=workspace_name_or_id))
559
+ raise CLIDatasourceException(FeedbackManager.error_workspace(workspace=workspace_name_or_id, cli="tb"))
560
560
 
561
561
  if not current_workspace:
562
- raise CLIDatasourceException(FeedbackManager.error_not_authenticated())
562
+ raise CLIDatasourceException(FeedbackManager.error_not_authenticated(cli="tb"))
563
563
 
564
564
  if not user_token:
565
565
  user_token = ask_for_user_token("share a Data Source", ui_host)
@@ -626,10 +626,10 @@ async def datasource_unshare(ctx: Context, datasource_name: str, workspace_name_
626
626
  current_workspace = next((workspace for workspace in workspaces if workspace["id"] == config["id"]), None)
627
627
 
628
628
  if not destination_workspace:
629
- raise CLIDatasourceException(FeedbackManager.error_workspace(workspace=workspace_name_or_id))
629
+ raise CLIDatasourceException(FeedbackManager.error_workspace(workspace=workspace_name_or_id, cli="tb"))
630
630
 
631
631
  if not current_workspace:
632
- raise CLIDatasourceException(FeedbackManager.error_not_authenticated())
632
+ raise CLIDatasourceException(FeedbackManager.error_not_authenticated(cli="tb"))
633
633
 
634
634
  if not user_token:
635
635
  user_token = ask_for_user_token("unshare a Data Source", ui_host)
@@ -114,7 +114,7 @@ async def clear_workspace(ctx: Context, yes: bool, dry_run: bool) -> None:
114
114
  for workspace in response["workspaces"]:
115
115
  if config["id"] == workspace["id"]:
116
116
  if workspace.get("is_branch"):
117
- raise CLIWorkspaceException(FeedbackManager.error_not_allowed_in_branch())
117
+ raise CLIWorkspaceException(FeedbackManager.error_not_allowed_in_branch(cli="tb"))
118
118
  return
119
119
  else:
120
120
  click.echo(FeedbackManager.info_current_workspace())
@@ -297,7 +297,7 @@ async def delete_workspace(
297
297
  )
298
298
 
299
299
  if not workspace_to_delete:
300
- raise CLIWorkspaceException(FeedbackManager.error_workspace(workspace=workspace_name_or_id))
300
+ raise CLIWorkspaceException(FeedbackManager.error_workspace(workspace=workspace_name_or_id, cli="tb"))
301
301
 
302
302
  if yes or click.confirm(
303
303
  FeedbackManager.warning_confirm_delete_workspace(workspace_name=workspace_to_delete.get("name"))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: tinybird_cli
3
- Version: 6.4.0
3
+ Version: 6.4.2.dev0
4
4
  Summary: Tinybird Command Line Tool
5
5
  Home-page: https://www.tinybird.co/docs/cli
6
6
  Author: Tinybird
@@ -12,7 +12,7 @@ Requires-Dist: clickhouse-toolset==0.34.dev0
12
12
  Requires-Dist: click<8.2,>=8.1.8
13
13
  Requires-Dist: colorama==0.4.6
14
14
  Requires-Dist: cryptography~=41.0.0
15
- Requires-Dist: croniter==1.3.15
15
+ Requires-Dist: croniter==6.2.2
16
16
  Requires-Dist: GitPython~=3.1.32
17
17
  Requires-Dist: humanfriendly~=8.2
18
18
  Requires-Dist: pydantic~=2.8.0
@@ -43,6 +43,12 @@ The Tinybird command-line tool allows you to use all the Tinybird functionality
43
43
  Changelog
44
44
  ----------
45
45
 
46
+ 6.4.1
47
+ ***********
48
+
49
+ - `Improved` feedback messages when suggesting other cli commands.
50
+ - `Fixed` `tb push` rejecting the `%` operator in column DEFAULT/MATERIALIZED expressions, causing failures after `tb pull` on schemas using modulo arithmetic.
51
+
46
52
  6.4.0
47
53
  ***********
48
54
 
@@ -1,29 +1,29 @@
1
- tinybird/__cli__.py,sha256=3doFT2eSENhhYrItUxXmFDpZbayKkXuoG1qY-5ZEsVY,231
1
+ tinybird/__cli__.py,sha256=MTnPp_CJDk5yO8LKw-WUauu2RR-e5GX1XFuvzYUrOKI,236
2
2
  tinybird/check_pypi.py,sha256=_4NkharLyR_ELrAdit-ftqIWvOf7jZNPt3i76frlo9g,975
3
3
  tinybird/client.py,sha256=xzogEol-GJTpDCm5MQ_9Tj_GBiBuOZfIw_O3OQTUOvY,50615
4
- tinybird/config.py,sha256=zFwsrNrW0XxyZ1EWM45BEHwRkwfCfemoMhJt_0xK-_Q,7584
4
+ tinybird/config.py,sha256=g74rE9jbVcyFj1bms5T3VEITLr21_WFy79Uj5ovvj90,7413
5
5
  tinybird/context.py,sha256=o4yvlXPkMLmdh-XJl3wpmqPAMeRRz5ScKzKlHHKn_I8,1201
6
- tinybird/datafile_common.py,sha256=m0Ey8R2VxcIbBvIOCL8XD0e0666TyZTU3SNX3jHmk8k,233162
6
+ tinybird/datafile_common.py,sha256=BNoZqWVr7Nvn6GKb1mEvtqQieOquWW9eGBN3YhCTfFM,233244
7
7
  tinybird/datatypes.py,sha256=Ud_IphoDOMtTMzEsYIf2lO467EVWw_ctbsOnrEzDHvU,11359
8
8
  tinybird/feedback_manager.py,sha256=OehfKVruCHwUNN1bHIbDICvOaIovc3hb6RjGHTyIkBc,67667
9
- tinybird/git_settings.py,sha256=Sw_8rGmribEFJ4Z_6idrVytxpFYk7ez8ei0qHULzs3E,3934
10
- tinybird/sql.py,sha256=GkYat5LdigutlTmC3K1xSfLTX2AbBLcOo6eraDAk0Sk,48762
11
- tinybird/sql_template.py,sha256=3C4fooCCgPbZ_sCPHSVyDlWP2go5jCX3SS3_LdnJ7Cg,128509
9
+ tinybird/git_settings.py,sha256=mqWgeboOlOFsSo97qyv595UCR2R1QCAqT4GTawBNPBg,3935
10
+ tinybird/sql.py,sha256=8pvjlKwdJ-PuJkCo57W8e1gj5z0RzUP7vOnum6Pi134,48901
11
+ tinybird/sql_template.py,sha256=65wtfKygqkYjU57MOlOTtbUk3EKc-Px7GeEfk65s0WM,128636
12
12
  tinybird/sql_template_fmt.py,sha256=Ma4qcs-2r8ZXQC4GUmrCqYz34DsnGF8k5lE2Jwnr314,10638
13
- tinybird/sql_toolset.py,sha256=ATiaUuowBtF8INfVBsI4W7WTJqidB2oIcXEfNDxKif4,27695
13
+ tinybird/sql_toolset.py,sha256=FhSBvHUlr5NoSdZO86v1EDPRNf4V_MWP3wX6v3kmqTA,27222
14
14
  tinybird/syncasync.py,sha256=rIPmCvygWSFqfnlVqhZH4N9gVVTvD6DEPsfoxGizYrI,27776
15
15
  tinybird/tb_cli.py,sha256=q1LGAsBVVMJsjR2HK62Pu6vpVtLzNmH8wHrEVUUdVkU,744
16
16
  tinybird/tornado_template.py,sha256=1_0nYFk_xJh_TMHh6AKkJILvnNY6xYmaM-uJ3Ofv7e8,42085
17
17
  tinybird/ch_utils/constants.py,sha256=yTNizMzgYNBzUc2EV3moBfdrDIggOe9hiuAgWF7sv2c,4333
18
18
  tinybird/ch_utils/engine.py,sha256=NzYUnmXsrJQimwXfCqdtIMyuS_Ad0OSdEnqNXzzStvY,37489
19
- tinybird/tb_cli_modules/auth.py,sha256=dRg9tOwIuPcumGOHn03U9fCT-sBvoCo14uj1_yK2T2w,8731
20
- tinybird/tb_cli_modules/branch.py,sha256=jELRnzCd4tbx_MGsNaGQn8UEDgyCdPTnTSF6QMsA-2M,39325
19
+ tinybird/tb_cli_modules/auth.py,sha256=3xu8STgouOgLkqlBf9LWFg9Oto_NyuDKsUWF95-zGwI,8741
20
+ tinybird/tb_cli_modules/branch.py,sha256=-2OB-zNc4_bQfaPQWsbD-lR1CSKsdxAe2qKXBYHsFWo,39382
21
21
  tinybird/tb_cli_modules/cicd.py,sha256=i2Mw8AbmEVNBcEPYdio7liy3PGqh1ezVFZ0OmJ9ww5o,13809
22
- tinybird/tb_cli_modules/cli.py,sha256=ZxFgyKISQ581P9ueFPYFSAdi2y3-TQQ6rnsBgDn5_6o,60193
23
- tinybird/tb_cli_modules/common.py,sha256=xHaRI08Hbtbl5n9_pG8-Em01QgLjQacWMUdkQqgjUPo,77161
22
+ tinybird/tb_cli_modules/cli.py,sha256=2l-J4NblKBRF4XxCA6HqShk3nwhLGsncdG7itLmvOto,60283
23
+ tinybird/tb_cli_modules/common.py,sha256=9jAFQXOEiS5oomC5Im4ALcqgRld7A6mnOWDfoWI4OKc,77242
24
24
  tinybird/tb_cli_modules/config.py,sha256=0kFDmsDcjKon32rgFGMHHKSbv4j5dOrXtVOlyuAyEkk,11510
25
25
  tinybird/tb_cli_modules/connection.py,sha256=yoYUQo-Fl36LTHeGI3HpFOCLiP0wKhWsoP9P9G26NZ8,21305
26
- tinybird/tb_cli_modules/datasource.py,sha256=16E4s_fru2RKbhuxvZmUO80wV70szWElNp2u9fKX1Ro,32505
26
+ tinybird/tb_cli_modules/datasource.py,sha256=b12ClLFISGHqK7zrLZBX5OT-8Nxd2oW734-Xon0dTE8,32541
27
27
  tinybird/tb_cli_modules/exceptions.py,sha256=pmucP4kTF4irIt7dXiG-FcnI-o3mvDusPmch1L8RCWk,3367
28
28
  tinybird/tb_cli_modules/fmt.py,sha256=edQap4tAqWMWogSIx5zriT75naLi73XTB3NwatmcrFw,3518
29
29
  tinybird/tb_cli_modules/job.py,sha256=AG69LPb9MbobA1awwJFZJvxqarDKfRlsBjw2V1zvYqc,2964
@@ -33,12 +33,12 @@ tinybird/tb_cli_modules/tag.py,sha256=9YHnruPnUNp1IJUe4qcSEMg9EbquIRo--Nxcsbvkvq
33
33
  tinybird/tb_cli_modules/telemetry.py,sha256=W098H6jmS4kpE7hN3tadaREBTf7oMocel-lkKWN0pU8,10466
34
34
  tinybird/tb_cli_modules/test.py,sha256=Vf8oK96V81HdKGsT79y6MUz6oz_VrYIwTbRnzzJs4rQ,4350
35
35
  tinybird/tb_cli_modules/token.py,sha256=JXATKTlbXohP9ZDZjlz8E4VYG6zrknKZhuz_wh1zBBc,13793
36
- tinybird/tb_cli_modules/workspace.py,sha256=yg3yb7_GniGJnOy2HqwEzePl47gQD-hywuQJyclZHi0,12455
36
+ tinybird/tb_cli_modules/workspace.py,sha256=JljGmM2LFJIHx_lL-xXCx8TNqhWD0yFS4kKUDh0RiaY,12473
37
37
  tinybird/tb_cli_modules/workspace_members.py,sha256=ksXsjd233y9-sNlz4Qb-meZbX4zn1B84e_bSm2i8rhg,8731
38
38
  tinybird/tb_cli_modules/tinyunit/tinyunit.py,sha256=50uqMgJD2BqSVONtCm55nuGRWhBNZWRc2GP1Qb8URdg,11246
39
39
  tinybird/tb_cli_modules/tinyunit/tinyunit_lib.py,sha256=NHoXcCHPDcKWYLzgP3NViho3Ey-6RV-ynPDzySPrTPE,1817
40
- tinybird_cli-6.4.0.dist-info/METADATA,sha256=9Q6Hu8laie6qypJSrCdpEesdQtOgLsbFlrgucUQcEns,81345
41
- tinybird_cli-6.4.0.dist-info/WHEEL,sha256=beeZ86-EfXScwlR_HKu4SllMC9wUEj_8Z_4FJ3egI2w,91
42
- tinybird_cli-6.4.0.dist-info/entry_points.txt,sha256=PKPKuPmA4IfJYnCFHHUiw-aAWZuBomFvwCklv1OyCjE,43
43
- tinybird_cli-6.4.0.dist-info/top_level.txt,sha256=ZIQJTPCzMqnfDzM_hEGZrJqDSEcKnIK_49T86DGWpyQ,78
44
- tinybird_cli-6.4.0.dist-info/RECORD,,
40
+ tinybird_cli-6.4.2.dev0.dist-info/METADATA,sha256=NdxXrmkVZwO1TtVTM4IeEOVb05DZJZ2qqegCWu6kFwE,81596
41
+ tinybird_cli-6.4.2.dev0.dist-info/WHEEL,sha256=beeZ86-EfXScwlR_HKu4SllMC9wUEj_8Z_4FJ3egI2w,91
42
+ tinybird_cli-6.4.2.dev0.dist-info/entry_points.txt,sha256=PKPKuPmA4IfJYnCFHHUiw-aAWZuBomFvwCklv1OyCjE,43
43
+ tinybird_cli-6.4.2.dev0.dist-info/top_level.txt,sha256=ZIQJTPCzMqnfDzM_hEGZrJqDSEcKnIK_49T86DGWpyQ,78
44
+ tinybird_cli-6.4.2.dev0.dist-info/RECORD,,