tinybird-cli 6.1.1.dev0__tar.gz → 6.3.0__tar.gz

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 (47) hide show
  1. {tinybird_cli-6.1.1.dev0 → tinybird_cli-6.3.0}/PKG-INFO +11 -1
  2. {tinybird_cli-6.1.1.dev0 → tinybird_cli-6.3.0}/tinybird/__cli__.py +2 -2
  3. {tinybird_cli-6.1.1.dev0 → tinybird_cli-6.3.0}/tinybird/ch_utils/engine.py +2 -2
  4. {tinybird_cli-6.1.1.dev0 → tinybird_cli-6.3.0}/tinybird/client.py +11 -5
  5. {tinybird_cli-6.1.1.dev0 → tinybird_cli-6.3.0}/tinybird/config.py +1 -1
  6. {tinybird_cli-6.1.1.dev0 → tinybird_cli-6.3.0}/tinybird/datafile_common.py +13 -14
  7. {tinybird_cli-6.1.1.dev0 → tinybird_cli-6.3.0}/tinybird/sql.py +5 -4
  8. {tinybird_cli-6.1.1.dev0 → tinybird_cli-6.3.0}/tinybird/sql_template.py +1 -1
  9. {tinybird_cli-6.1.1.dev0 → tinybird_cli-6.3.0}/tinybird/sql_toolset.py +52 -3
  10. {tinybird_cli-6.1.1.dev0 → tinybird_cli-6.3.0}/tinybird/syncasync.py +1 -0
  11. {tinybird_cli-6.1.1.dev0 → tinybird_cli-6.3.0}/tinybird/tb_cli_modules/branch.py +1 -3
  12. {tinybird_cli-6.1.1.dev0 → tinybird_cli-6.3.0}/tinybird/tb_cli_modules/cicd.py +3 -3
  13. {tinybird_cli-6.1.1.dev0 → tinybird_cli-6.3.0}/tinybird/tb_cli_modules/cli.py +2 -2
  14. {tinybird_cli-6.1.1.dev0 → tinybird_cli-6.3.0}/tinybird/tb_cli_modules/common.py +2 -2
  15. {tinybird_cli-6.1.1.dev0 → tinybird_cli-6.3.0}/tinybird/tb_cli_modules/datasource.py +1 -3
  16. {tinybird_cli-6.1.1.dev0 → tinybird_cli-6.3.0}/tinybird/tb_cli_modules/pipe.py +14 -2
  17. {tinybird_cli-6.1.1.dev0 → tinybird_cli-6.3.0}/tinybird/tb_cli_modules/tinyunit/tinyunit.py +2 -2
  18. {tinybird_cli-6.1.1.dev0 → tinybird_cli-6.3.0}/tinybird/tornado_template.py +1 -1
  19. {tinybird_cli-6.1.1.dev0 → tinybird_cli-6.3.0}/tinybird_cli.egg-info/PKG-INFO +11 -1
  20. {tinybird_cli-6.1.1.dev0 → tinybird_cli-6.3.0}/tinybird_cli.egg-info/top_level.txt +2 -0
  21. {tinybird_cli-6.1.1.dev0 → tinybird_cli-6.3.0}/setup.cfg +0 -0
  22. {tinybird_cli-6.1.1.dev0 → tinybird_cli-6.3.0}/tinybird/ch_utils/constants.py +0 -0
  23. {tinybird_cli-6.1.1.dev0 → tinybird_cli-6.3.0}/tinybird/check_pypi.py +0 -0
  24. {tinybird_cli-6.1.1.dev0 → tinybird_cli-6.3.0}/tinybird/context.py +0 -0
  25. {tinybird_cli-6.1.1.dev0 → tinybird_cli-6.3.0}/tinybird/datatypes.py +0 -0
  26. {tinybird_cli-6.1.1.dev0 → tinybird_cli-6.3.0}/tinybird/feedback_manager.py +0 -0
  27. {tinybird_cli-6.1.1.dev0 → tinybird_cli-6.3.0}/tinybird/git_settings.py +0 -0
  28. {tinybird_cli-6.1.1.dev0 → tinybird_cli-6.3.0}/tinybird/sql_template_fmt.py +0 -0
  29. {tinybird_cli-6.1.1.dev0 → tinybird_cli-6.3.0}/tinybird/tb_cli.py +0 -0
  30. {tinybird_cli-6.1.1.dev0 → tinybird_cli-6.3.0}/tinybird/tb_cli_modules/auth.py +0 -0
  31. {tinybird_cli-6.1.1.dev0 → tinybird_cli-6.3.0}/tinybird/tb_cli_modules/config.py +0 -0
  32. {tinybird_cli-6.1.1.dev0 → tinybird_cli-6.3.0}/tinybird/tb_cli_modules/connection.py +0 -0
  33. {tinybird_cli-6.1.1.dev0 → tinybird_cli-6.3.0}/tinybird/tb_cli_modules/exceptions.py +0 -0
  34. {tinybird_cli-6.1.1.dev0 → tinybird_cli-6.3.0}/tinybird/tb_cli_modules/fmt.py +0 -0
  35. {tinybird_cli-6.1.1.dev0 → tinybird_cli-6.3.0}/tinybird/tb_cli_modules/job.py +0 -0
  36. {tinybird_cli-6.1.1.dev0 → tinybird_cli-6.3.0}/tinybird/tb_cli_modules/regions.py +0 -0
  37. {tinybird_cli-6.1.1.dev0 → tinybird_cli-6.3.0}/tinybird/tb_cli_modules/tag.py +0 -0
  38. {tinybird_cli-6.1.1.dev0 → tinybird_cli-6.3.0}/tinybird/tb_cli_modules/telemetry.py +0 -0
  39. {tinybird_cli-6.1.1.dev0 → tinybird_cli-6.3.0}/tinybird/tb_cli_modules/test.py +0 -0
  40. {tinybird_cli-6.1.1.dev0 → tinybird_cli-6.3.0}/tinybird/tb_cli_modules/tinyunit/tinyunit_lib.py +0 -0
  41. {tinybird_cli-6.1.1.dev0 → tinybird_cli-6.3.0}/tinybird/tb_cli_modules/token.py +0 -0
  42. {tinybird_cli-6.1.1.dev0 → tinybird_cli-6.3.0}/tinybird/tb_cli_modules/workspace.py +0 -0
  43. {tinybird_cli-6.1.1.dev0 → tinybird_cli-6.3.0}/tinybird/tb_cli_modules/workspace_members.py +0 -0
  44. {tinybird_cli-6.1.1.dev0 → tinybird_cli-6.3.0}/tinybird_cli.egg-info/SOURCES.txt +0 -0
  45. {tinybird_cli-6.1.1.dev0 → tinybird_cli-6.3.0}/tinybird_cli.egg-info/dependency_links.txt +0 -0
  46. {tinybird_cli-6.1.1.dev0 → tinybird_cli-6.3.0}/tinybird_cli.egg-info/entry_points.txt +0 -0
  47. {tinybird_cli-6.1.1.dev0 → tinybird_cli-6.3.0}/tinybird_cli.egg-info/requires.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: tinybird_cli
3
- Version: 6.1.1.dev0
3
+ Version: 6.3.0
4
4
  Summary: Tinybird Command Line Tool
5
5
  Home-page: https://www.tinybird.co/docs/cli
6
6
  Author: Tinybird
@@ -43,6 +43,16 @@ The Tinybird command-line tool allows you to use all the Tinybird functionality
43
43
  Changelog
44
44
  ----------
45
45
 
46
+ 6.3.0
47
+ ***********
48
+
49
+ - `Added` `tb copy run` and `tb pipe copy run` have a new `--on-demand-compute` flag to use on-demand compute instances for copy jobs.
50
+
51
+ 6.2.0
52
+ ***********
53
+
54
+ - `Fixed` `tb push` with `SHARED_WITH` now uses the user token to fetch workspaces, avoiding failures caused by using the wrong token
55
+
46
56
  6.0.2
47
57
  ***********
48
58
 
@@ -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.1.1.dev0'
8
- __revision__ = 'cdfa249'
7
+ __version__ = '6.3.0'
8
+ __revision__ = 'b610ec3'
@@ -298,7 +298,7 @@ class EngineOption:
298
298
  self.required = required
299
299
  self.default_value = default_value
300
300
  self.is_valid = is_valid
301
- self.tb_param = tb_param if tb_param else "_".join(["engine", name])
301
+ self.tb_param = tb_param or "_".join(["engine", name])
302
302
 
303
303
 
304
304
  class EngineParam:
@@ -314,7 +314,7 @@ class EngineParam:
314
314
  self.required = required
315
315
  self.default_value = default_value
316
316
  self.is_valid = is_valid
317
- self.tb_param = tb_param if tb_param else self.build_engine_param_name(name)
317
+ self.tb_param = tb_param or self.build_engine_param_name(name)
318
318
 
319
319
  @staticmethod
320
320
  def build_engine_param_name(name: str):
@@ -118,7 +118,7 @@ class TinyB:
118
118
  ) -> Response:
119
119
  url = f"{self.host.strip('/')}/{endpoint.strip('/')}"
120
120
 
121
- token_to_use = use_token if use_token else self.token
121
+ token_to_use = use_token or self.token
122
122
  if token_to_use:
123
123
  url += ("&" if "?" in endpoint else "?") + "token=" + token_to_use
124
124
  if self.version:
@@ -191,7 +191,7 @@ class TinyB:
191
191
  use_token: Optional[str] = None,
192
192
  **kwargs,
193
193
  ):
194
- token_to_use = use_token if use_token else self.token
194
+ token_to_use = use_token or self.token
195
195
  response = await self._req_raw(endpoint, data, files, method, retries, use_token, **kwargs)
196
196
 
197
197
  if response.status_code == 403:
@@ -565,8 +565,8 @@ class TinyB:
565
565
  async def pipes(self, branch=None, dependencies: bool = False, node_attrs=None, attrs=None) -> List[Dict[str, Any]]:
566
566
  params = {
567
567
  "dependencies": "true" if dependencies else "false",
568
- "attrs": attrs if attrs else "",
569
- "node_attrs": node_attrs if node_attrs else "",
568
+ "attrs": attrs or "",
569
+ "node_attrs": node_attrs or "",
570
570
  }
571
571
  response = await self._req(f"/v0/pipes?{urlencode(params)}")
572
572
  pipes = response["pipes"]
@@ -635,11 +635,17 @@ class TinyB:
635
635
  return await self._req(f"/v0/pipes/{pipe_name_or_id}/nodes/{node_id}/copy", method="DELETE")
636
636
 
637
637
  async def pipe_run_copy(
638
- self, pipe_name_or_id: str, params: Optional[Dict[str, str]] = None, mode: Optional[str] = None
638
+ self,
639
+ pipe_name_or_id: str,
640
+ params: Optional[Dict[str, str]] = None,
641
+ mode: Optional[str] = None,
642
+ on_demand_compute: bool = False,
639
643
  ):
640
644
  params = {**params} if params else {}
641
645
  if mode:
642
646
  params["_mode"] = mode
647
+ if on_demand_compute:
648
+ params["on_demand_compute"] = "true"
643
649
  return await self._req(f"/v0/pipes/{pipe_name_or_id}/copy?{urlencode(params)}", method="POST")
644
650
 
645
651
  async def pipe_resume_copy(self, pipe_name_or_id: str):
@@ -124,7 +124,7 @@ def get_display_host(ui_host: str):
124
124
  def get_display_cloud_host(api_host: str) -> str:
125
125
  is_local = "localhost" in api_host
126
126
  if is_local:
127
- port = api_host.split(":")[-1]
127
+ port = api_host.rsplit(":", maxsplit=1)[-1]
128
128
  return f"http://cloud.tinybird.co/local/{port}"
129
129
  return CLOUD_HOSTS.get(api_host, api_host)
130
130
 
@@ -445,10 +445,12 @@ class CLIGitRelease:
445
445
  # not expected suffixes
446
446
  return list(
447
447
  filter(
448
- lambda d: d.change_type in [c.value for c in self.ChangeType]
449
- and d.a_path
450
- and d.a_path not in self.NO_DATAFILES_PATHS
451
- and Path(d.a_path).resolve().suffix in self.DATAFILES_SUFFIXES,
448
+ lambda d: (
449
+ d.change_type in [c.value for c in self.ChangeType]
450
+ and d.a_path
451
+ and d.a_path not in self.NO_DATAFILES_PATHS
452
+ and Path(d.a_path).resolve().suffix in self.DATAFILES_SUFFIXES
453
+ ),
452
454
  self.diff(current_ws_commit),
453
455
  )
454
456
  )
@@ -533,9 +535,7 @@ class CLIGitRelease:
533
535
  async def update_release(
534
536
  self, tb_client: TinyB, current_ws: Dict[str, Any], commit: Optional[str] = None
535
537
  ) -> Dict[str, Any]:
536
- release = await tb_client.workspace_commit_update(
537
- current_ws["id"], commit if commit else self.head().commit.hexsha
538
- )
538
+ release = await tb_client.workspace_commit_update(current_ws["id"], commit or self.head().commit.hexsha)
539
539
  return release
540
540
 
541
541
 
@@ -3145,24 +3145,23 @@ async def share_and_unshare_datasource(
3145
3145
  datasource_name = datasource.get("name", "")
3146
3146
  datasource_id = datasource.get("id", "")
3147
3147
  workspaces: List[Dict[str, Any]]
3148
+ # We duplicate the client to use the user_token in workspace discovery and sharing operations.
3149
+ user_client: TinyB = deepcopy(client)
3150
+ user_client.token = user_token
3148
3151
 
3149
3152
  # In case we are pushing to a branch, we don't share the datasource
3150
3153
  # FIXME: Have only once way to get the current workspace
3151
3154
  if current_ws:
3152
- # Force to get all the workspaces the user can access
3153
3155
  workspace = current_ws
3154
- workspaces = (await client.user_workspaces()).get("workspaces", [])
3155
3156
  else:
3156
3157
  workspace = await client.user_workspace_branches()
3157
- workspaces = workspace.get("workspaces", [])
3158
3158
 
3159
3159
  if workspace.get("is_branch", False):
3160
3160
  click.echo(FeedbackManager.info_skipping_sharing_datasources_branch(datasource=datasource["name"]))
3161
3161
  return
3162
3162
 
3163
- # We duplicate the client to use the user_token
3164
- user_client: TinyB = deepcopy(client)
3165
- user_client.token = user_token
3163
+ # Use the user token for workspace discovery, as workspace/admin tokens may not list all targets.
3164
+ workspaces = (await user_client.user_workspaces()).get("workspaces", [])
3166
3165
  if not workspaces_current_shared_with:
3167
3166
  for workspace_to_share in workspaces_to_share:
3168
3167
  w: Optional[Dict[str, Any]] = next((w for w in workspaces if w["name"] == workspace_to_share), None)
@@ -5029,7 +5028,7 @@ async def format_engine(
5029
5028
  file_parts.append(DATAFILE_NEW_LINE)
5030
5029
  for arg in sorted(node["engine"].get("args", [])):
5031
5030
  elem = ", ".join([x.strip() for x in arg[1].split(",")])
5032
- file_parts.append(f"ENGINE_{arg[0].upper()} {elem if elem else empty}")
5031
+ file_parts.append(f"ENGINE_{arg[0].upper()} {elem or empty}")
5033
5032
  file_parts.append(DATAFILE_NEW_LINE)
5034
5033
  file_parts.append(DATAFILE_NEW_LINE)
5035
5034
  return file_parts
@@ -257,7 +257,8 @@ def schema_to_sql_columns(schema: List[Dict[str, Any]], skip_jsonpaths: bool = F
257
257
  name = x["normalized_name"] if "normalized_name" in x else x["name"]
258
258
  if x["nullable"]:
259
259
  if (_type := try_to_fix_nullable_in_simple_aggregating_function(x["type"])) is None:
260
- _type = "Nullable(%s)" % x["type"]
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"]
261
262
  else:
262
263
  _type = x["type"]
263
264
  parts = [col_name(name, backquotes=True), _type]
@@ -821,11 +822,11 @@ def _parse_table_structure(schema: str) -> List[Dict[str, Any]]:
821
822
  nullable = column["type"].lower().startswith("nullable")
822
823
  column["type"] = column["type"] if not nullable else column["type"][len("Nullable(") : -1] # ')'
823
824
  column["nullable"] = nullable
824
- column["codec"] = column["codec"] if column["codec"] else None
825
+ column["codec"] = column["codec"] or None
825
826
  column["name"] = column["name"]
826
827
  column["normalized_name"] = column["name"]
827
- column["jsonpath"] = column["jsonpath"] if column["jsonpath"] else None
828
- default_value = column["default_value"] if column["default_value"] else None
828
+ column["jsonpath"] = column["jsonpath"] or None
829
+ default_value = column["default_value"] or None
829
830
  if nullable and default_value and default_value.lower() == "default null":
830
831
  default_value = None
831
832
  column["default_value"] = default_value
@@ -199,7 +199,7 @@ class Comment:
199
199
 
200
200
  class Placeholder:
201
201
  def __init__(self, name=None, line=None):
202
- self.name = name if name else "__no_value__"
202
+ self.name = name or "__no_value__"
203
203
  self.line = line or "unknown"
204
204
 
205
205
  def __str__(self):
@@ -3,6 +3,7 @@ import logging
3
3
  import re
4
4
  import threading
5
5
  from collections import defaultdict
6
+ from dataclasses import dataclass
6
7
  from datetime import datetime
7
8
  from functools import lru_cache
8
9
  from typing import FrozenSet, List, Optional, Set, Tuple, Union
@@ -34,7 +35,7 @@ class InvalidFunction(ValueError):
34
35
  super().__init__(self.msg)
35
36
 
36
37
 
37
- class InvalidResource(ValueError):
38
+ class MissingResource(ValueError):
38
39
  def __init__(self, database: str, table: str, default_database: str = ""):
39
40
  if default_database and database == default_database:
40
41
  database = ""
@@ -74,6 +75,54 @@ def explain_plan(sql: str) -> str:
74
75
  return chquery.explain_ast(sql)
75
76
 
76
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
+ @dataclass
92
+ class MaterializedViewTarget:
93
+ database: Optional[str]
94
+ table: str
95
+
96
+
97
+ def parse_materialized_view_target(create_table_query: str) -> Optional[MaterializedViewTarget]:
98
+ """
99
+ Extract target table info from a CREATE MATERIALIZED VIEW statement.
100
+
101
+ Parses statements like:
102
+ CREATE MATERIALIZED VIEW db.view TO target_db.target_table AS SELECT...
103
+ CREATE MATERIALIZED VIEW db.view TO `target_db`.`target_table` AS SELECT...
104
+ CREATE MATERIALIZED VIEW db.view TO `target_db`.`target_table` (col1 Type1, col2 Nullable(DateTime64(3))) AS SELECT...
105
+
106
+ Returns:
107
+ MaterializedViewTarget with database (optional) and table,
108
+ or None if the query is not a CREATE MATERIALIZED VIEW statement.
109
+ """
110
+ try:
111
+ result = chquery.parse_create_materialized_view_target_table(create_table_query)
112
+ except ValueError:
113
+ return None
114
+
115
+ return MaterializedViewTarget(
116
+ database=result["database"],
117
+ table=result["table"],
118
+ )
119
+
120
+
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
+
77
126
  def has_join(sql: str) -> bool:
78
127
  return any(line.rstrip().startswith("TableJoin") for line in explain_plan(sql).split())
79
128
 
@@ -539,7 +588,7 @@ def replace_tables(
539
588
  if (
540
589
  dependent_table[2]
541
590
  and dependent_table[2] not in _enabled_table_functions
542
- and not (dependent_table[2] in ["cluster"] and replacement[0] == VALID_REMOTE)
591
+ and not (dependent_table[2] == "cluster" and replacement[0] == VALID_REMOTE)
543
592
  ):
544
593
  raise InvalidFunction(table_function_name=dependent_table[2])
545
594
  if dependent_table[0] or dependent_table[1]:
@@ -586,7 +635,7 @@ def replace_tables(
586
635
  "valid_tables": valid_tables,
587
636
  },
588
637
  )
589
- raise InvalidResource(database, table_name, default_database=default_database)
638
+ raise MissingResource(database, table_name, default_database=default_database)
590
639
 
591
640
  if current_replacements:
592
641
  # We need to transform the dictionary into something cacheable, so a sorted tuple of tuples it is
@@ -26,6 +26,7 @@
26
26
  # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27
27
  # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
28
 
29
+ import asyncio
29
30
  import asyncio.coroutines
30
31
  import functools
31
32
  import inspect
@@ -1014,9 +1014,7 @@ async def datasource_copy_from_main(datasource_name: str, sql: str, sql_from_mai
1014
1014
 
1015
1015
  client = config.get_client()
1016
1016
 
1017
- response = await client.datasource_query_copy(
1018
- datasource_name, sql if sql else f"SELECT * FROM main.{datasource_name}"
1019
- )
1017
+ response = await client.datasource_query_copy(datasource_name, sql or f"SELECT * FROM main.{datasource_name}")
1020
1018
  if "job" not in response:
1021
1019
  raise CLIBranchException(response)
1022
1020
  job_id = response["job"]["job_id"]
@@ -405,8 +405,8 @@ async def init_cicd(
405
405
  ):
406
406
  provider = ask_provider_interactively()
407
407
  if provider:
408
- path = path if path else getcwd()
409
- data_project_dir = data_project_dir if data_project_dir else "."
408
+ path = path or getcwd()
409
+ data_project_dir = data_project_dir or "."
410
410
  generator = CICDGeneratorBase.build_generator(provider)
411
411
  workspace_info = await client.workspace_info()
412
412
  token = await client.get_token_by_name("admin token")
@@ -426,7 +426,7 @@ async def init_cicd(
426
426
 
427
427
 
428
428
  async def check_cicd_exists(path: Optional[str] = None) -> Optional[Provider]:
429
- path = path if path else getcwd()
429
+ path = path or getcwd()
430
430
  for provider in Provider:
431
431
  generator = CICDGeneratorBase.build_generator(provider.name)
432
432
  if generator.is_already_generated(path):
@@ -229,7 +229,7 @@ async def init(
229
229
  config = CLIConfig.get_project_config()
230
230
  if config.get("token") is None:
231
231
  raise AuthNoTokenException
232
- folder = folder if folder else getcwd()
232
+ folder = folder or getcwd()
233
233
 
234
234
  workspaces: List[Dict[str, Any]] = (await client.user_workspaces_and_branches()).get("workspaces", [])
235
235
  current_ws: Dict[str, Any] = next(
@@ -644,7 +644,7 @@ async def pull(ctx: Context, folder: str, auto: bool, match: Optional[str], forc
644
644
  """Retrieve latest versions for project files from Tinybird."""
645
645
 
646
646
  client = ctx.ensure_object(dict)["client"]
647
- folder = folder if folder else getcwd()
647
+ folder = folder or getcwd()
648
648
 
649
649
  return await folder_pull(client, folder, auto, match, force, fmt=fmt)
650
650
 
@@ -695,7 +695,7 @@ async def create_workspace_branch(
695
695
  if not branch_name:
696
696
  click.echo(FeedbackManager.info_workspace_branch_create_greeting())
697
697
  default_name = f"{workspace['name']}_{uuid.uuid4().hex[0:4]}"
698
- branch_name = click.prompt("\Branch name", default=default_name, err=True, type=str)
698
+ branch_name = click.prompt("\nBranch name", default=default_name, err=True, type=str)
699
699
  assert isinstance(branch_name, str)
700
700
 
701
701
  response = await config.get_client().create_workspace_branch(
@@ -1601,7 +1601,7 @@ async def wait_job(
1601
1601
  if "progress_percentage" in res:
1602
1602
  progress_bar.update(int(round(res["progress_percentage"])) - progress_bar.pos)
1603
1603
  elif res["status"] != "working":
1604
- progress_bar.update(progress_bar.length if progress_bar.length else 0)
1604
+ progress_bar.update(progress_bar.length or 0)
1605
1605
 
1606
1606
  try:
1607
1607
  # TODO: Simplify this as it's not needed to use two functions for
@@ -728,9 +728,7 @@ async def datasource_copy_from_main(
728
728
  click.echo(FeedbackManager.error_exception(error="Use --sql or --sql-from-main"))
729
729
  return
730
730
 
731
- response = await client.datasource_query_copy(
732
- datasource_name, sql if sql else f"SELECT * FROM main.{datasource_name}"
733
- )
731
+ response = await client.datasource_query_copy(datasource_name, sql or f"SELECT * FROM main.{datasource_name}")
734
732
  if "job" not in response:
735
733
  raise Exception(response)
736
734
  job_id = response["job"]["job_id"]
@@ -732,10 +732,22 @@ async def regression_test(
732
732
  default=None,
733
733
  help="Key and value of the params you want the Copy pipe to be called with. For example: tb pipe copy run <my_copy_pipe> --param foo=bar",
734
734
  )
735
+ @click.option(
736
+ "--on-demand-compute",
737
+ is_flag=True,
738
+ default=False,
739
+ help="Use on-demand compute instances for the copy job.",
740
+ )
735
741
  @click.pass_context
736
742
  @coro
737
743
  async def pipe_copy_run(
738
- ctx: click.Context, pipe_name_or_id: str, wait: bool, mode: str, yes: bool, param: Optional[Tuple[str]]
744
+ ctx: click.Context,
745
+ pipe_name_or_id: str,
746
+ wait: bool,
747
+ mode: str,
748
+ yes: bool,
749
+ param: Optional[Tuple[str]],
750
+ on_demand_compute: bool,
739
751
  ):
740
752
  """Run an on-demand copy job"""
741
753
 
@@ -746,7 +758,7 @@ async def pipe_copy_run(
746
758
  client: TinyB = ctx.ensure_object(dict)["client"]
747
759
 
748
760
  try:
749
- response = await client.pipe_run_copy(pipe_name_or_id, params, mode)
761
+ response = await client.pipe_run_copy(pipe_name_or_id, params, mode, on_demand_compute=on_demand_compute)
750
762
 
751
763
  job_id = response["job"]["job_id"]
752
764
  job_url = response["job"]["job_url"]
@@ -226,7 +226,7 @@ async def run_test_file(tb_client: TinyB, file: str) -> List[TestResult]:
226
226
  pipe = test_case.pipe.name
227
227
  params = test_case.pipe.params
228
228
  try:
229
- sql = test_case.sql if test_case.sql else None
229
+ sql = test_case.sql or None
230
230
  test_response = await tb_client.pipe_data(pipe, format="json", params=params, sql=sql)
231
231
  results.append(
232
232
  TestResult(
@@ -263,7 +263,7 @@ def test_run_summary(results: List[TestSummaryResults], only_fail: bool = False,
263
263
  total_counts[test.status] = total_counts.get(test.status, 0) + 1
264
264
 
265
265
  # Skip the PASS tests if we only want the failed ones
266
- if only_fail and test.status in [PASS]:
266
+ if only_fail and test.status == PASS:
267
267
  continue
268
268
 
269
269
  summary.append(
@@ -283,7 +283,7 @@ class Template:
283
283
  .. versionchanged:: 4.3
284
284
  Added ``whitespace`` parameter; deprecated ``compress_whitespace``.
285
285
  """
286
- name = name if name else "<string>"
286
+ name = name or "<string>"
287
287
  self.name = escape.native_str(name)
288
288
 
289
289
  if compress_whitespace is not _UNSET:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: tinybird_cli
3
- Version: 6.1.1.dev0
3
+ Version: 6.3.0
4
4
  Summary: Tinybird Command Line Tool
5
5
  Home-page: https://www.tinybird.co/docs/cli
6
6
  Author: Tinybird
@@ -43,6 +43,16 @@ The Tinybird command-line tool allows you to use all the Tinybird functionality
43
43
  Changelog
44
44
  ----------
45
45
 
46
+ 6.3.0
47
+ ***********
48
+
49
+ - `Added` `tb copy run` and `tb pipe copy run` have a new `--on-demand-compute` flag to use on-demand compute instances for copy jobs.
50
+
51
+ 6.2.0
52
+ ***********
53
+
54
+ - `Fixed` `tb push` with `SHARED_WITH` now uses the user token to fetch workspaces, avoiding failures caused by using the wrong token
55
+
46
56
  6.0.2
47
57
  ***********
48
58
 
@@ -1,5 +1,7 @@
1
+ scripts
1
2
  tests
2
3
  tests_e2e
3
4
  tests_e2e_ingestion
4
5
  tests_tb
5
6
  tinybird
7
+ tinybird_shared