tinybird 4.5.3.dev0__py3-none-any.whl → 4.5.4.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.
@@ -1394,7 +1394,8 @@ def try_to_fix_nullable_in_simple_aggregating_function(t: str) -> Optional[str]:
1394
1394
  if match := _PATTERN_SIMPLE_AGG_FUNC.search(t):
1395
1395
  fn = match.group(1)
1396
1396
  inner_type = match.group(2)
1397
- result = f"SimpleAggregateFunction({fn}, Nullable({inner_type}))"
1397
+ if "Nullable(" not in inner_type:
1398
+ result = f"SimpleAggregateFunction({fn}, Nullable({inner_type}))"
1398
1399
  return result
1399
1400
 
1400
1401
 
@@ -2366,7 +2367,7 @@ def get_project_fixtures(folder: str) -> List[str]:
2366
2367
  def has_internal_datafiles(folder: str) -> bool:
2367
2368
  folder = folder or "."
2368
2369
  filenames = get_project_filenames(folder)
2369
- return any([f for f in filenames if "spans" in str(f) and "vendor" not in str(f)])
2370
+ return any(f for f in filenames if "spans" in str(f) and "vendor" not in str(f))
2370
2371
 
2371
2372
 
2372
2373
  def peek(iterable):
tinybird/datatypes.py CHANGED
@@ -117,11 +117,11 @@ def date_test(x: str) -> bool:
117
117
 
118
118
 
119
119
  def datetime64_test(x: str) -> bool:
120
- return any([p.match(x) for p in datetime64_patterns])
120
+ return any(p.match(x) for p in datetime64_patterns)
121
121
 
122
122
 
123
123
  def datetime_test(x: str) -> bool:
124
- return any([p.match(x) for p in datetime_patterns])
124
+ return any(p.match(x) for p in datetime_patterns)
125
125
 
126
126
 
127
127
  def int_8_test(x: str) -> bool:
@@ -45,6 +45,7 @@ def get_tinybird_service_datasources() -> List[Dict[str, Any]]:
45
45
  {"name": "method", "type": "String"},
46
46
  {"name": "release", "type": "String"},
47
47
  {"name": "user_agent", "type": "Nullable(String)"},
48
+ {"name": "client_ip", "type": "Nullable(String)"},
48
49
  {"name": "resource_tags", "type": "Array(String)"},
49
50
  {"name": "memory_usage", "type": "UInt64"},
50
51
  ],
tinybird/sql.py CHANGED
@@ -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
@@ -2826,7 +2826,7 @@ def render_sql_template(
2826
2826
  return Comment("error launched")
2827
2827
 
2828
2828
  v: dict = {x["name"]: Placeholder(x["name"], x["line"]) for x in template_variables}
2829
- is_tb_secret = any([s for s in template_variables if s["name"] == "tb_secret" or s["name"] == "tb_var"])
2829
+ is_tb_secret = any(s for s in template_variables if s["name"] == "tb_secret" or s["name"] == "tb_var")
2830
2830
 
2831
2831
  if variables:
2832
2832
  v.update(variables)
tinybird/sql_toolset.py CHANGED
@@ -22,7 +22,7 @@ VALID_REMOTE = "VALID_REMOTE"
22
22
 
23
23
  class InvalidFunction(ValueError):
24
24
  def __init__(self, msg: str = "", table_function_name: str = ""):
25
- if any([fn for fn in COPY_ENABLED_TABLE_FUNCTIONS if fn in msg]):
25
+ if any(fn for fn in COPY_ENABLED_TABLE_FUNCTIONS if fn in msg):
26
26
  msg = msg.replace("is restricted", "is restricted to Copy Pipes")
27
27
 
28
28
  if table_function_name:
@@ -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
 
tinybird/tb/__cli__.py CHANGED
@@ -4,5 +4,5 @@ __description__ = 'Tinybird Command Line Tool'
4
4
  __url__ = 'https://www.tinybird.co/docs/forward/commands'
5
5
  __author__ = 'Tinybird'
6
6
  __author_email__ = 'support@tinybird.co'
7
- __version__ = '4.5.3.dev0'
8
- __revision__ = 'afc5f42'
7
+ __version__ = '4.5.4.dev0'
8
+ __revision__ = '23af8bd'
@@ -27,7 +27,6 @@ from tinybird.tb.modules.feedback_manager import FeedbackManager, get_cli_name
27
27
  @cli.group()
28
28
  def branch() -> None:
29
29
  """Branch commands. Custom branches is an experimental feature in beta."""
30
- pass
31
30
 
32
31
 
33
32
  @branch.command(name="ls")
@@ -51,9 +51,9 @@ jobs:
51
51
  - name: Install Tinybird CLI
52
52
  run: curl https://tinybird.co | sh
53
53
  - name: Build project
54
- run: tb build
54
+ run: tb --local build
55
55
  - name: Test project
56
- run: tb test run
56
+ run: tb --local test run
57
57
  - name: Deployment check
58
58
  run: tb --cloud --host ${{! env.TINYBIRD_HOST }} --token ${{! env.TINYBIRD_TOKEN }} deploy --check
59
59
  """
@@ -115,8 +115,8 @@ tinybird_ci_workflow:
115
115
  script:
116
116
  - export PATH="$HOME/.local/bin:$PATH"
117
117
  - cd $CI_PROJECT_DIR/{{ data_project_dir }}
118
- - tb build
119
- - tb test run
118
+ - tb --local build
119
+ - tb --local test run
120
120
  - tb --cloud --host "$TINYBIRD_HOST" --token "$TINYBIRD_TOKEN" deploy --check
121
121
  services:
122
122
  - name: tinybirdco/tinybird-local:latest
@@ -625,8 +625,10 @@ def cli(
625
625
  config["dev_mode"] = tinybird_dev_mode
626
626
 
627
627
  # Resolve project folder from tinybird.config.json (preferred) or legacy .tinyb cwd.
628
- folder = get_project_folder_from_tinybird_config(os.getcwd())
629
- if not folder:
628
+ folder_from_config = get_project_folder_from_tinybird_config(os.getcwd())
629
+ if folder_from_config:
630
+ folder = folder_from_config
631
+ else:
630
632
  tinyb_dir = os.path.dirname(config_temp._path) # Directory containing .tinyb file
631
633
  cwd_config = config.get("cwd", ".")
632
634
 
@@ -870,9 +872,7 @@ def sql(
870
872
  if output == "json":
871
873
  echo_json(res, indent=8)
872
874
  else:
873
- dd = []
874
- for d in res["data"]:
875
- dd.append(d.values())
875
+ dd = [d.values() for d in res["data"]]
876
876
  echo_safe_format_table(dd, columns=res["meta"])
877
877
  else:
878
878
  click.echo(FeedbackManager.info_no_rows())
@@ -699,10 +699,12 @@ def create_workspace_interactive(
699
699
  def print_data_branch_summary(client, job_id, response=None):
700
700
  response = client.job(job_id) if job_id else response or {"partitions": []}
701
701
  columns = ["Data Source", "Partition", "Status", "Error"]
702
- table = []
702
+ table: list[list] = []
703
703
  for partition in response["partitions"]:
704
- for p in partition["partitions"]:
705
- table.append([partition["datasource"]["name"], p["partition"], p["status"], p.get("error", "")])
704
+ table.extend(
705
+ [partition["datasource"]["name"], p["partition"], p["status"], p.get("error", "")]
706
+ for p in partition["partitions"]
707
+ )
706
708
  echo_safe_humanfriendly_tables_format_smart_table(table, column_names=columns)
707
709
 
708
710
 
@@ -2262,10 +2264,11 @@ def create_organization_and_add_workspaces(
2262
2264
 
2263
2265
  # Add existing orphan workspaces to the organization - this is only needed for backwards compatibility
2264
2266
  user_workspaces = client.user_workspaces_with_organization(version="v1")
2265
- workspaces_to_migrate = []
2266
- for workspace in user_workspaces["workspaces"]:
2267
- if workspace.get("organization") is None and workspace.get("role") == "admin":
2268
- workspaces_to_migrate.append(workspace["id"])
2267
+ workspaces_to_migrate = [
2268
+ workspace["id"]
2269
+ for workspace in user_workspaces["workspaces"]
2270
+ if workspace.get("organization") is None and workspace.get("role") == "admin"
2271
+ ]
2269
2272
  client.add_workspaces_to_organization(organization["id"], workspaces_to_migrate)
2270
2273
 
2271
2274
  return organization
@@ -2283,7 +2286,6 @@ def get_user_token(config: CLIConfig, user_token: Optional[str] = None) -> str:
2283
2286
  check_user_token_with_client(client, user_token)
2284
2287
  except Exception:
2285
2288
  user_token = None
2286
- pass
2287
2289
  if not user_token:
2288
2290
  user_token = ask_for_user_token("delete a workspace", ui_host)
2289
2291
  if not user_token:
@@ -293,18 +293,6 @@ def _prompt_sdk(sdk: Optional[str]) -> str:
293
293
  if sdk:
294
294
  return sdk.lower()
295
295
 
296
- click.echo(FeedbackManager.highlight(message="\n? Select project type:"))
297
- click.echo(" [1] typescript - Tinybird TypeScript SDK")
298
- click.echo(" [2] python - Tinybird Python SDK")
299
- click.echo(" [3] cli - Tinybird CLI datafiles project")
300
- choice = click.prompt("\nSelect option", default=3, type=int)
301
- if choice == 1:
302
- return "typescript"
303
- if choice == 2:
304
- return "python"
305
- if choice == 3:
306
- return "cli"
307
- click.echo(FeedbackManager.warning(message=f"Invalid option '{choice}'. Defaulting to {DEFAULT_SDK}."))
308
296
  return DEFAULT_SDK
309
297
 
310
298
 
@@ -544,7 +544,7 @@ def process(
544
544
  if (
545
545
  fork_downstream
546
546
  and r.get("resource", "") == "pipes"
547
- and any(["engine" in x.get("params", {}) for x in r.get("nodes", [])])
547
+ and any("engine" in x.get("params", {}) for x in r.get("nodes", []))
548
548
  ):
549
549
  raise click.ClickException(FeedbackManager.error_forkdownstream_pipes_with_engine(pipe=resource_name))
550
550
 
@@ -1087,7 +1087,7 @@ def process_file(
1087
1087
  deps = []
1088
1088
  nodes: List[Dict[str, Any]] = []
1089
1089
 
1090
- is_copy = any([node for node in doc.nodes if node.get("type", "standard").lower() == PipeNodeTypes.COPY])
1090
+ is_copy = any(node for node in doc.nodes if node.get("type", "standard").lower() == PipeNodeTypes.COPY)
1091
1091
  for node in doc.nodes:
1092
1092
  sql = node["sql"]
1093
1093
  node_type = node.get("type", "standard").lower()
@@ -166,7 +166,7 @@ def new_ds(
166
166
  existing = existing_ds.get("indexes", [])
167
167
  new.sort(key=lambda x: x["name"])
168
168
  existing.sort(key=lambda x: x["name"])
169
- if len(existing) != len(new) or any([(d, d2) for d, d2 in zip(new, existing) if d != d2]):
169
+ if len(existing) != len(new) or any((d, d2) for d, d2 in zip(new, existing) if d != d2):
170
170
  new_indices = ds.get("params", {}).get("indexes") or "0"
171
171
  if (
172
172
  new_description
@@ -253,7 +253,7 @@ def is_materialized(resource: Optional[Dict[str, Any]]) -> bool:
253
253
  return False
254
254
 
255
255
  is_materialized = any(
256
- [node.get("params", {}).get("type", None) == "materialized" for node in resource.get("nodes", []) or []]
256
+ node.get("params", {}).get("type", None) == "materialized" for node in resource.get("nodes", []) or []
257
257
  )
258
258
  return is_materialized
259
259
 
@@ -702,7 +702,7 @@ def process(
702
702
  if (
703
703
  fork_downstream
704
704
  and r.get("resource", "") == "pipes"
705
- and any(["engine" in x.get("params", {}) for x in r.get("nodes", [])])
705
+ and any("engine" in x.get("params", {}) for x in r.get("nodes", []))
706
706
  ):
707
707
  raise click.ClickException(FeedbackManager.error_forkdownstream_pipes_with_engine(pipe=resource_name))
708
708
 
@@ -1239,7 +1239,7 @@ def process_file(
1239
1239
  deps = []
1240
1240
  nodes: List[Dict[str, Any]] = []
1241
1241
 
1242
- is_copy = any([node for node in doc.nodes if node.get("type", "standard").lower() == PipeNodeTypes.COPY])
1242
+ is_copy = any(node for node in doc.nodes if node.get("type", "standard").lower() == PipeNodeTypes.COPY)
1243
1243
  for node in doc.nodes:
1244
1244
  sql = node["sql"]
1245
1245
  node_type = node.get("type", "standard").lower()
@@ -156,7 +156,6 @@ def deployment_group() -> None:
156
156
  """
157
157
  Deployment commands.
158
158
  """
159
- pass
160
159
 
161
160
 
162
161
  @deployment_group.command(name="create")
@@ -262,9 +261,7 @@ def deployment_ls(ctx: click.Context, include_deleted: bool) -> None:
262
261
  # Handle different output formats
263
262
  if output == "json":
264
263
  # Create JSON structure
265
- deployments_json = []
266
- for row in table:
267
- deployments_json.append({"id": row[0], "status": row[1], "created_at": row[2]})
264
+ deployments_json = [{"id": row[0], "status": row[1], "created_at": row[2]} for row in table]
268
265
  from tinybird.tb.modules.common import echo_json
269
266
 
270
267
  echo_json({"deployments": deployments_json})
@@ -590,52 +590,68 @@ def print_changes(result: dict, project: Project, output: Optional[str] = "human
590
590
  for dm in data_movements
591
591
  ]
592
592
 
593
- for ds in deployment.get("new_datasource_names", []):
594
- resources.append(["new", ds, "datasource", project.get_resource_path(ds, "datasource")])
593
+ resources.extend(
594
+ ["new", ds, "datasource", project.get_resource_path(ds, "datasource")]
595
+ for ds in deployment.get("new_datasource_names", [])
596
+ )
595
597
 
596
598
  for p in deployment.get("new_pipe_names", []):
597
599
  path = project.get_resource_path(p, "pipe")
598
600
  pipe_type = project.get_pipe_type(path)
599
601
  resources.append(["new", p, pipe_type, path])
600
602
 
601
- for dc in deployment.get("new_data_connector_names", []):
602
- resources.append(["new", dc, "connection", project.get_resource_path(dc, "connection")])
603
+ resources.extend(
604
+ ["new", dc, "connection", project.get_resource_path(dc, "connection")]
605
+ for dc in deployment.get("new_data_connector_names", [])
606
+ )
603
607
 
604
- for ds in deployment.get("changed_datasource_names", []):
605
- resources.append(["modified", ds, "datasource", project.get_resource_path(ds, "datasource")])
608
+ resources.extend(
609
+ ["modified", ds, "datasource", project.get_resource_path(ds, "datasource")]
610
+ for ds in deployment.get("changed_datasource_names", [])
611
+ )
606
612
 
607
613
  for p in deployment.get("changed_pipe_names", []):
608
614
  path = project.get_resource_path(p, "pipe")
609
615
  pipe_type = project.get_pipe_type(path)
610
616
  resources.append(["modified", p, pipe_type, path])
611
617
 
612
- for dc in deployment.get("changed_data_connector_names", []):
613
- resources.append(["modified", dc, "connection", project.get_resource_path(dc, "connection")])
618
+ resources.extend(
619
+ ["modified", dc, "connection", project.get_resource_path(dc, "connection")]
620
+ for dc in deployment.get("changed_data_connector_names", [])
621
+ )
614
622
 
615
- for ds in deployment.get("disconnected_data_source_names", []):
616
- resources.append(["modified", ds, "datasource", project.get_resource_path(ds, "datasource")])
623
+ resources.extend(
624
+ ["modified", ds, "datasource", project.get_resource_path(ds, "datasource")]
625
+ for ds in deployment.get("disconnected_data_source_names", [])
626
+ )
617
627
 
618
- for ds in deployment.get("deleted_datasource_names", []):
619
- resources.append(["deleted", ds, "datasource", project.get_resource_path(ds, "datasource")])
628
+ resources.extend(
629
+ ["deleted", ds, "datasource", project.get_resource_path(ds, "datasource")]
630
+ for ds in deployment.get("deleted_datasource_names", [])
631
+ )
620
632
 
621
633
  for p in deployment.get("deleted_pipe_names", []):
622
634
  path = project.get_resource_path(p, "pipe")
623
635
  pipe_type = project.get_pipe_type(path)
624
636
  resources.append(["deleted", p, pipe_type, path])
625
637
 
626
- for dc in deployment.get("deleted_data_connector_names", []):
627
- resources.append(["deleted", dc, "connection", project.get_resource_path(dc, "connection")])
638
+ resources.extend(
639
+ ["deleted", dc, "connection", project.get_resource_path(dc, "connection")]
640
+ for dc in deployment.get("deleted_data_connector_names", [])
641
+ )
628
642
 
629
643
  for token_change in deployment.get("token_changes", []):
630
644
  token_name = token_change.get("token_name")
631
645
  change_type = token_change.get("change_type")
632
- added_perms = []
633
- removed_perms = []
634
646
  permission_changes = token_change.get("permission_changes", {})
635
- for perm in permission_changes.get("added_permissions", []):
636
- added_perms.append(f"{perm['resource_name']}.{perm['resource_type']}:{perm['permission']}")
637
- for perm in permission_changes.get("removed_permissions", []):
638
- removed_perms.append(f"{perm['resource_name']}.{perm['resource_type']}:{perm['permission']}")
647
+ added_perms = [
648
+ f"{perm['resource_name']}.{perm['resource_type']}:{perm['permission']}"
649
+ for perm in permission_changes.get("added_permissions", [])
650
+ ]
651
+ removed_perms = [
652
+ f"{perm['resource_name']}.{perm['resource_type']}:{perm['permission']}"
653
+ for perm in permission_changes.get("removed_permissions", [])
654
+ ]
639
655
 
640
656
  tokens.append((change_type, token_name, "\n".join(added_perms), "\n".join(removed_perms)))
641
657
 
@@ -44,9 +44,7 @@ def jobs_ls(ctx: Context, status: Tuple[str, ...], kind: Tuple[str, ...]) -> Non
44
44
  jobs = client.jobs(status=status, kind=kind)
45
45
  columns = ["id", "kind", "status", "created at", "updated at", "job url"]
46
46
  click.echo(FeedbackManager.info_jobs())
47
- table = []
48
- for j in jobs:
49
- table.append([j[c.replace(" ", "_")] for c in columns])
47
+ table = [[j[c.replace(" ", "_")] for c in columns] for j in jobs]
50
48
  echo_safe_humanfriendly_tables_format_smart_table(table, column_names=columns)
51
49
  click.echo("\n")
52
50
 
@@ -154,19 +154,18 @@ def local_ls() -> None:
154
154
 
155
155
  columns = ["name", "id", "role", "plan", "current"]
156
156
  current_workspace_name = config.get("name")
157
- table = []
158
157
  click.echo(FeedbackManager.info_workspaces())
159
158
 
160
- for workspace in response.get("workspaces", []):
161
- table.append(
162
- [
163
- workspace.get("name", ""),
164
- workspace.get("id", ""),
165
- workspace.get("role", ""),
166
- _get_workspace_plan_name(workspace.get("plan")),
167
- current_workspace_name == workspace.get("name"),
168
- ]
169
- )
159
+ table = [
160
+ [
161
+ workspace.get("name", ""),
162
+ workspace.get("id", ""),
163
+ workspace.get("role", ""),
164
+ _get_workspace_plan_name(workspace.get("plan")),
165
+ current_workspace_name == workspace.get("name"),
166
+ ]
167
+ for workspace in response.get("workspaces", [])
168
+ ]
170
169
 
171
170
  echo_safe_humanfriendly_tables_format_smart_table(table, column_names=columns)
172
171
 
@@ -85,8 +85,8 @@ class AuthHandler(http.server.SimpleHTTPRequestHandler):
85
85
 
86
86
  self.server.shutdown()
87
87
 
88
- def log_message(self, format, *args):
89
- # Suppress log messages
88
+ def log_message(self, format, *args): # noqa: ARG002
89
+ # Override BaseHTTPRequestHandler.log_message to silence default stderr logging.
90
90
  return
91
91
 
92
92
 
@@ -415,8 +415,6 @@ def check_current_folder_in_sessions(ctx: Context) -> None:
415
415
  class TrackFolderCancelled(Exception):
416
416
  """Exception raised when the user cancels the folder tracking"""
417
417
 
418
- pass
419
-
420
418
 
421
419
  def check_and_warn_folder_change(cli_config: CLIConfig) -> None:
422
420
  """
@@ -6,10 +6,8 @@ from tinybird.tb.modules.table import format_table
6
6
 
7
7
 
8
8
  def print_table_formatted(res: dict, name: str):
9
- data = []
10
9
  limit = 20
11
- for d in res["data"][:limit]:
12
- data.append(d.values())
10
+ data = [d.values() for d in res["data"][:limit]]
13
11
  meta = res["meta"]
14
12
  stats = res.get("statistics", {})
15
13
  row_count = stats.get("rows_read", 0)
@@ -49,19 +49,18 @@ def workspace_ls(ctx: Context) -> None:
49
49
  raise CLIWorkspaceException(FeedbackManager.error_unable_to_identify_main_workspace())
50
50
 
51
51
  columns = ["name", "id", "role", "plan", "current"]
52
- table = []
53
52
  click.echo(FeedbackManager.info_workspaces())
54
53
 
55
- for workspace in response["workspaces"]:
56
- table.append(
57
- [
58
- workspace["name"],
59
- workspace["id"],
60
- workspace["role"],
61
- _get_workspace_plan_name(workspace["plan"]),
62
- current_main_workspace["name"] == workspace["name"],
63
- ]
64
- )
54
+ table = [
55
+ [
56
+ workspace["name"],
57
+ workspace["id"],
58
+ workspace["role"],
59
+ _get_workspace_plan_name(workspace["plan"]),
60
+ current_main_workspace["name"] == workspace["name"],
61
+ ]
62
+ for workspace in response["workspaces"]
63
+ ]
65
64
 
66
65
  echo_safe_humanfriendly_tables_format_smart_table(table, column_names=columns)
67
66
 
@@ -105,9 +105,7 @@ def list_members_in_workspace(ctx: Context) -> None:
105
105
  """List members in the current Workspace."""
106
106
 
107
107
  cmd_ctx = get_command_context(ctx)
108
- users = []
109
- for u in cmd_ctx.workspace["members"]:
110
- users.append([u["email"], u["role"]])
108
+ users = [[u["email"], u["role"]] for u in cmd_ctx.workspace["members"]]
111
109
  click.echo(FeedbackManager.info_workspace_users(workspace_name=cmd_ctx.workspace["name"]))
112
110
  echo_safe_humanfriendly_tables_format_smart_table(users, column_names=["email", "role"])
113
111
 
@@ -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:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: tinybird
3
- Version: 4.5.3.dev0
3
+ Version: 4.5.4.dev0
4
4
  Summary: Tinybird Command Line Tool
5
5
  Home-page: https://www.tinybird.co/docs/forward/commands
6
6
  Author: Tinybird
@@ -52,6 +52,12 @@ The Tinybird command-line tool allows you to use all the Tinybird functionality
52
52
  Changelog
53
53
  ----------
54
54
 
55
+ 4.5.3
56
+ *******
57
+
58
+ - `Changed` `tb init` now defaults to CLI projects when `--type` is not provided.
59
+ - `Fixed` GitHub and GitLab CI templates generated by `tb init` now run `tb --local build` and `tb --local test run`.
60
+
55
61
  4.5.2
56
62
  ***********
57
63
 
@@ -1,42 +1,42 @@
1
1
  tinybird/context.py,sha256=o4yvlXPkMLmdh-XJl3wpmqPAMeRRz5ScKzKlHHKn_I8,1201
2
- tinybird/datatypes.py,sha256=Ud_IphoDOMtTMzEsYIf2lO467EVWw_ctbsOnrEzDHvU,11359
2
+ tinybird/datatypes.py,sha256=Lr8BIaRP--qzuZFpAdv20uQaeE1BOAMmuEwWVHheFPw,11355
3
3
  tinybird/feedback_manager.py,sha256=OehfKVruCHwUNN1bHIbDICvOaIovc3hb6RjGHTyIkBc,67667
4
4
  tinybird/git_settings.py,sha256=mqWgeboOlOFsSo97qyv595UCR2R1QCAqT4GTawBNPBg,3935
5
5
  tinybird/prompts.py,sha256=F8ic_mGId50MDJmg5ec8i5BDavxz9SWtocLXqgO0IRY,45689
6
- tinybird/service_datasources.py,sha256=fpGWCHaP0zAh6IsT17KBDtCpCqLkjbFyF8uYTis2LEk,58076
7
- tinybird/sql.py,sha256=TjuJukrTgHdubY88Pt4OOGRV5IS44R4oWRwF7o5fkx0,48763
8
- tinybird/sql_template.py,sha256=65wtfKygqkYjU57MOlOTtbUk3EKc-Px7GeEfk65s0WM,128636
6
+ tinybird/service_datasources.py,sha256=G2446rKT3-jDmGlpUyq9D1kMLvDPvtMYX-5-yepSEHI,58143
7
+ tinybird/sql.py,sha256=8pvjlKwdJ-PuJkCo57W8e1gj5z0RzUP7vOnum6Pi134,48901
8
+ tinybird/sql_template.py,sha256=_J-QmHos4Sx9YS6tVsEyE68qrcFbj13VwCA1zB3Sz8Q,128634
9
9
  tinybird/sql_template_fmt.py,sha256=Ma4qcs-2r8ZXQC4GUmrCqYz34DsnGF8k5lE2Jwnr314,10638
10
- tinybird/sql_toolset.py,sha256=ATiaUuowBtF8INfVBsI4W7WTJqidB2oIcXEfNDxKif4,27695
10
+ tinybird/sql_toolset.py,sha256=xWY-EtixaZKUPuNY4WBz2fDwwNnmbgtdLsmI2wy67UA,27220
11
11
  tinybird/syncasync.py,sha256=rIPmCvygWSFqfnlVqhZH4N9gVVTvD6DEPsfoxGizYrI,27776
12
12
  tinybird/tornado_template.py,sha256=1_0nYFk_xJh_TMHh6AKkJILvnNY6xYmaM-uJ3Ofv7e8,42085
13
13
  tinybird/ch_utils/constants.py,sha256=yTNizMzgYNBzUc2EV3moBfdrDIggOe9hiuAgWF7sv2c,4333
14
14
  tinybird/ch_utils/engine.py,sha256=NzYUnmXsrJQimwXfCqdtIMyuS_Ad0OSdEnqNXzzStvY,37489
15
- tinybird/datafile/common.py,sha256=-kis_iAE4FKWqLCiHnAE0v_qvCv5TFFavmoGLZyywE0,109714
15
+ tinybird/datafile/common.py,sha256=KeEyWWlyfLGJVoTTv9L-ksnP-vvhStxRjdmxNFBbDQo,109758
16
16
  tinybird/datafile/exceptions.py,sha256=8rw2umdZjtby85QbuRKFO5ETz_eRHwUY5l7eHsy1wnI,556
17
17
  tinybird/datafile/parse_connection.py,sha256=GxmGp_XnWbDZPDbh_PBxitlIMqZRYfDwxMBw-JQBp1g,1890
18
18
  tinybird/datafile/parse_datasource.py,sha256=yd58HrUF4yNJXLn6OsvKGpZJpvrcjLGAeJG1lgBe_zk,1891
19
19
  tinybird/datafile/parse_pipe.py,sha256=-9bbgVuiWRyDYydrLVflDBt8GstZotMy6dklsrc6MUY,3859
20
- tinybird/tb/__cli__.py,sha256=fEPZtz5yQ0OXcYpjwKowucqLmPf8le00OegJ776lpAQ,245
20
+ tinybird/tb/__cli__.py,sha256=Bj2UFb9iRcdskDjnQ0RVgc6jY3sFIL9l0aTtKdH6LDA,245
21
21
  tinybird/tb/check_pypi.py,sha256=Gp0HkHHDFMSDL6nxKlOY51z7z1Uv-2LRexNTZSHHGmM,552
22
22
  tinybird/tb/cli.py,sha256=IjiGfNIpxSxi1odK1kMj9s8lEhx3sAUgGA263XdmyR0,1119
23
23
  tinybird/tb/client.py,sha256=RM8VQwPZFonfvejuTf3oT09XbJmq7pD5VLzFt4zoBGM,53910
24
24
  tinybird/tb/config.py,sha256=CGCfBbCMlmVcBHQ0IMGc2IE4O2-1tZEPyD564JZoTbw,5659
25
- tinybird/tb/modules/branch.py,sha256=Ik6KPkWcraqnBCxfZpQlMLadH_OSqGVSGdNE0eMUHp0,9318
25
+ tinybird/tb/modules/branch.py,sha256=lMxtkmtE1qWpKUO2B21-5S56-kfr03lrZnPTtds32Xw,9309
26
26
  tinybird/tb/modules/build.py,sha256=bGFoFppR_UbGUMDWFGDzfJ4nT3CGFwzCzl2o4OpwR2o,10420
27
27
  tinybird/tb/modules/build_common.py,sha256=KxAE_HCq4QupIghW4n6IVokznwgA3Lv-KZ_mE9YAUPk,23709
28
- tinybird/tb/modules/cicd.py,sha256=xGa1zop2Io0Y3YcvOea7IAboBSM_qm2zZdeBvl93KIs,8235
29
- tinybird/tb/modules/cli.py,sha256=WVk7zTQS5a2ROGXALQSM57Gpy8QCKy3fJ5tDtFfT1Ic,40701
30
- tinybird/tb/modules/common.py,sha256=j7fg_8rIQFPIzzDfs9Xuse5jU-QaomuzY-6aqXb9izo,91937
28
+ tinybird/tb/modules/cicd.py,sha256=IO4qqsoLRXcubALb7vx_QnRpg3zIIxfaVO9bGomlESY,8267
29
+ tinybird/tb/modules/cli.py,sha256=h570AFfq_mlCCdT-StAKC8v7h0P8kp7JRUddOkS3u8s,40726
30
+ tinybird/tb/modules/common.py,sha256=7gUs_yUvqhNZpUStQicgunp2vXgfy4Gk2NAp5UWfRgE,91922
31
31
  tinybird/tb/modules/config.py,sha256=Z47lMZxFeX68b62bTzgj9379zn-9eT4cPbrcMJ_xTGQ,11431
32
32
  tinybird/tb/modules/connection.py,sha256=SzTguZEjH_wrfloeFTiXT4cBSQkVsKy2gz2OWYDsPQo,15352
33
33
  tinybird/tb/modules/connection_kafka.py,sha256=Ay9VqRUehjq_5bSFCNXfYrSo-wsa2FrVpISShmFxYRs,17172
34
34
  tinybird/tb/modules/connection_s3.py,sha256=-Sj9d7xAJXbpr8JHioCqrO2hneJ1spoBE4QpYEfiyS8,16565
35
35
  tinybird/tb/modules/copy.py,sha256=Apzjxiyinp6KmgamypPKEe3BAnpxG7MwYcSYkHiG8sA,6033
36
- tinybird/tb/modules/create.py,sha256=35co8tj0_-YfdMEvPwUn65HDaF9XhknWQEaXswvXiJA,21791
36
+ tinybird/tb/modules/create.py,sha256=UsHCgWxkiz-cDBCmXp-G7zyQGHvu1r6X-ywPRz_jxgo,21231
37
37
  tinybird/tb/modules/datasource.py,sha256=rqhfg17vnivTLnbYxy3b0j3rrT4xOkDFGgN14mTaVGg,64256
38
- tinybird/tb/modules/deployment.py,sha256=xppmzItKsCCDUpi8ZDlpEAXBKJYlBrzz_0I84szO1O4,16352
39
- tinybird/tb/modules/deployment_common.py,sha256=GSwpahTgaGPV2gpRyF-pxzDUhSTqv5S1FaPD5azF_SY,28892
38
+ tinybird/tb/modules/deployment.py,sha256=0NzwntO46AsgrLSmeB8yxLFF6JoFErm2T5471ZUmstw,16296
39
+ tinybird/tb/modules/deployment_common.py,sha256=dNc3iwwRf7GvY4JyM8EM-8VJvcivx-UwF45itoMaNj0,28965
40
40
  tinybird/tb/modules/deprecations.py,sha256=XXekrzPO9v12F1ToQDUGzLYJJ2wrEUlGKOkLCSdfHiM,4935
41
41
  tinybird/tb/modules/endpoint.py,sha256=yRTh6rQFJ98LA0hSC8rPD3EcDaJj41gk9oCsgcZPu_c,12112
42
42
  tinybird/tb/modules/exceptions.py,sha256=_1BHy0OixEkeF0fOCu1_1Pj8pJS4BNfUyucqCViJGTw,5958
@@ -44,15 +44,15 @@ tinybird/tb/modules/feedback_manager.py,sha256=W7ePUzaLh37h1M-Bf_qACRcIbs2UiL8ZN
44
44
  tinybird/tb/modules/fmt.py,sha256=ejQC1-2mK42saA2R9DA-CENYgu06SUoiCoX4bCtRXT8,3734
45
45
  tinybird/tb/modules/info.py,sha256=8k3GHJByr4afc5jrNKnuuXwKFHVADcS1fTLFu1Hlo_8,11706
46
46
  tinybird/tb/modules/infra.py,sha256=J9Noe9aZo5Y9ZKAhqh9jnv8azfivXLLHQ2a9TeMWN9s,32673
47
- tinybird/tb/modules/job.py,sha256=wBsnu8UPTOha2rkLvucgmw4xYv73ubmui3eeSIF68ZM,3107
47
+ tinybird/tb/modules/job.py,sha256=gjUceRkepgseQe1Q23G8B25R06r2UfdEIIXlIaqBA5E,3079
48
48
  tinybird/tb/modules/job_common.py,sha256=3rdRH9F9kCRL_dBa5fghB27xgHqnO3oulBeIb1AcSbE,687
49
49
  tinybird/tb/modules/llm.py,sha256=fPBBCmM3KlCksLlgJkg4joDn6y3H5QjDzE-Pm4YNf7E,1782
50
50
  tinybird/tb/modules/llm_utils.py,sha256=nS9r4FAElJw8yXtmdYrx-rtI2zXR8qXfi1QqUDCfxvg,3469
51
- tinybird/tb/modules/local.py,sha256=Gack4TVqcKWd8-aGbibaCIcafdmgHIRWDNvDjezAaO8,14933
51
+ tinybird/tb/modules/local.py,sha256=1Uf1cOjVPYt3kF4cTQ8gmDsNbfTqmxv2SAfiiYYoMr4,14881
52
52
  tinybird/tb/modules/local_common.py,sha256=lioApQi3lKLCaVVK8l9e1fwE7wvfvOwH04GicPj4Mws,34306
53
53
  tinybird/tb/modules/local_logs.py,sha256=pS8_4n-HySVi3vj95jYjQ5F8L5Xgwty_OeNamezFc0o,7513
54
54
  tinybird/tb/modules/login.py,sha256=fxgcsN0fMhjzT_6C8VrwGTxnLxLVuWlZvASFr8I9yUU,1885
55
- tinybird/tb/modules/login_common.py,sha256=ZfV3bYe312jTB71M0WzLTLwU76tHwJTvzIkQOqnQnZY,21664
55
+ tinybird/tb/modules/login_common.py,sha256=9YjYWvo9nuBist4xt6IGiBejFloZODsut47ImTZke9E,21727
56
56
  tinybird/tb/modules/logout.py,sha256=sniI4JNxpTrVeRCp0oGJuQ3yRerG4hH5uz6oBmjv724,1009
57
57
  tinybird/tb/modules/logs.py,sha256=uSQi_A6QIFOjAoj1h8XOI_51AQ3uk5UEg2TjYbZxDJA,21462
58
58
  tinybird/tb/modules/materialization.py,sha256=SaomNeaAzLWtcnsZdetYBxEq0ihY1cRzh23n3Z1P_c4,5643
@@ -62,7 +62,7 @@ tinybird/tb/modules/preview.py,sha256=8GVTHTmxWPqYjTs3-6mQ1ie8XCLrAQQ8aogDhCpnWM
62
62
  tinybird/tb/modules/project.py,sha256=Js8f_VjzEI3cITaw5a_X2wgZ6kiO-VQq8LSx688ScDA,8697
63
63
  tinybird/tb/modules/project_commands.py,sha256=t7h5JJWNA5eGth_Nrj0fak5B9eyVPGeDluF-41OBkhw,1877
64
64
  tinybird/tb/modules/py_project.py,sha256=DE3N-GPhcvVulELMINZQmw0zG6xZ1YeVkyJt1vn2E4I,7799
65
- tinybird/tb/modules/query_output.py,sha256=PR4sTn0ATVnCR9yy1T-V_a_L1j8GKjLQBPS2hx3zSkI,1373
65
+ tinybird/tb/modules/query_output.py,sha256=vdsjAsWt2YPfZ9hGEokCknYCnwuzMsnSVTDoLr47bBs,1346
66
66
  tinybird/tb/modules/regions.py,sha256=QjsL5H6Kg-qr0aYVLrvb1STeJ5Sx_sjvbOYO0LrEGMk,166
67
67
  tinybird/tb/modules/secret.py,sha256=9BIdh2PZDAbY2wRbf4ZDvkEltygztz1RMxgDmY1D0LI,3521
68
68
  tinybird/tb/modules/secret_common.py,sha256=8mEXOdNFrTuVWu7rgZ5-C8v-rB1EAdZQUHIyBIgwISI,1578
@@ -74,12 +74,12 @@ tinybird/tb/modules/test_common.py,sha256=ctCGnYyvm9u8VCi0kZiKPJVrZJ5iBamfc5KhaY
74
74
  tinybird/tb/modules/token.py,sha256=fNUh2PQCMRFO1YYiWydeM9JUGJaYljCWMtU40fjw-Wc,13895
75
75
  tinybird/tb/modules/ts_project.py,sha256=XeDN-3gthT7GZYIFn0l-y5RJaofB_2T-I8gq6dL-a-A,7428
76
76
  tinybird/tb/modules/watch.py,sha256=sXv-Un5d9S_p4P_I4aQe_StRJCXR_NloAxFFRPDs6Hw,8734
77
- tinybird/tb/modules/workspace.py,sha256=6tg8zz0gjhef6U3XdYSBqjlbcjUWJl-itYCnEDZBLbg,8991
78
- tinybird/tb/modules/workspace_members.py,sha256=D0PEKATUsVFXRkm2uHuYoVn4HJL7xdS6oI-rP0T7-Kk,9514
79
- tinybird/tb/modules/datafile/build.py,sha256=_pBoyPhqeH_ryA0m-RNZTLIh0t-cVqzUTLbJWjF6UQw,50109
77
+ tinybird/tb/modules/workspace.py,sha256=7shWyyZD9IT32EeP0aEgsZGk0U1ZdT6hWnVfctRoIJk,8939
78
+ tinybird/tb/modules/workspace_members.py,sha256=8oQLTczEh9cIdD3iF-N3SJuqLtKdF-_g-YzeVaqKGP0,9486
79
+ tinybird/tb/modules/datafile/build.py,sha256=YRmTfxzkXlmXYsJLsQxMoI2BtgBcE0jG3g9F2dZWQ0k,50105
80
80
  tinybird/tb/modules/datafile/build_common.py,sha256=2yNdxe49IMA9wNvl25NemY2Iaz8L66snjOdT64dm1is,4511
81
- tinybird/tb/modules/datafile/build_datasource.py,sha256=PcejBESPfJwgEv2A0Eb9J-UbQIrH1DC5UJ9OLRoi4AY,16916
82
- tinybird/tb/modules/datafile/build_pipe.py,sha256=QLjZP3LSLxItwqtQRcZP-SALg2h2nxhG9cprXDRBASk,11227
81
+ tinybird/tb/modules/datafile/build_datasource.py,sha256=keAx2lchYoxEDXQfTBtAe94Oyr4zfw3kWMumBFNtYso,16914
82
+ tinybird/tb/modules/datafile/build_pipe.py,sha256=VYrQULliQ2fs4rLvRvwJwI0L45zrWa15CP_09CLP03Y,11225
83
83
  tinybird/tb/modules/datafile/diff.py,sha256=bwVh15E_lio78QDUFQ4ozAEMF7rfUBm2c77JQkL33ZA,6712
84
84
  tinybird/tb/modules/datafile/fixture.py,sha256=gftYWeweeQDFK3cHgUmSOfltNjZVOuMt8v7WTMLhGBw,1579
85
85
  tinybird/tb/modules/datafile/format_common.py,sha256=pLB508Akui1G1-X2mNirv2WvjZ_VSgSoPWvyw5Cwkus,4150
@@ -87,18 +87,18 @@ tinybird/tb/modules/datafile/format_connection.py,sha256=ieD9Jb6In6PmZqIysZ1tD1J
87
87
  tinybird/tb/modules/datafile/format_datasource.py,sha256=rqA0Mr_3x5vaXe5vXWs7Fe2XmgVWn4niSlpvLnZh4vg,5325
88
88
  tinybird/tb/modules/datafile/format_pipe.py,sha256=9UuUsl5DjV4tJIRr6Whuv-AtIM9Gx7R3396AbKjHaJ4,7724
89
89
  tinybird/tb/modules/datafile/pipe_checker.py,sha256=dxsCQoA6ruxg1fvF6sMLFowpjaqws8lUQcM7XyhgZPE,24609
90
- tinybird/tb/modules/datafile/playground.py,sha256=EMVelqhRlXkcpt0jTcPSWsqtzNQyrZ37eIs-Qrcwi0M,55273
90
+ tinybird/tb/modules/datafile/playground.py,sha256=TB4ebscUaNPqYzn0zBWqFBtK6TyzhcenHeTf62ac-2E,55269
91
91
  tinybird/tb/modules/datafile/pull.py,sha256=vzJ8_rixPRyuv3x_Ah8v5iHr1cM2LUGrSX54Opg4lGI,5073
92
92
  tinybird/tb/modules/tinyunit/tinyunit.py,sha256=GHdp_6uDcCO2C-Z5bXu4OJQGXMm9Y_yjrcLVYdWy65o,11238
93
93
  tinybird/tb/modules/tinyunit/tinyunit_lib.py,sha256=NHoXcCHPDcKWYLzgP3NViho3Ey-6RV-ynPDzySPrTPE,1817
94
94
  tinybird/tb_cli_modules/cicd.py,sha256=i2Mw8AbmEVNBcEPYdio7liy3PGqh1ezVFZ0OmJ9ww5o,13809
95
- tinybird/tb_cli_modules/common.py,sha256=LUMfZyC8zeLYnkAS8KmxyW7TAzjTwG1Hgok-CFLf1zA,77259
95
+ tinybird/tb_cli_modules/common.py,sha256=9jAFQXOEiS5oomC5Im4ALcqgRld7A6mnOWDfoWI4OKc,77242
96
96
  tinybird/tb_cli_modules/config.py,sha256=0kFDmsDcjKon32rgFGMHHKSbv4j5dOrXtVOlyuAyEkk,11510
97
97
  tinybird/tb_cli_modules/exceptions.py,sha256=pmucP4kTF4irIt7dXiG-FcnI-o3mvDusPmch1L8RCWk,3367
98
98
  tinybird/tb_cli_modules/regions.py,sha256=QjsL5H6Kg-qr0aYVLrvb1STeJ5Sx_sjvbOYO0LrEGMk,166
99
99
  tinybird/tb_cli_modules/telemetry.py,sha256=W098H6jmS4kpE7hN3tadaREBTf7oMocel-lkKWN0pU8,10466
100
- tinybird-4.5.3.dev0.dist-info/METADATA,sha256=3fpTA7h3b5AZYOU74U4xT0CGMET3aG6Mx3_ajbBabEk,10012
101
- tinybird-4.5.3.dev0.dist-info/WHEEL,sha256=beeZ86-EfXScwlR_HKu4SllMC9wUEj_8Z_4FJ3egI2w,91
102
- tinybird-4.5.3.dev0.dist-info/entry_points.txt,sha256=LwdHU6TfKx4Qs7BqqtaczEZbImgU7Abe9Lp920zb_fo,43
103
- tinybird-4.5.3.dev0.dist-info/top_level.txt,sha256=ZIQJTPCzMqnfDzM_hEGZrJqDSEcKnIK_49T86DGWpyQ,78
104
- tinybird-4.5.3.dev0.dist-info/RECORD,,
100
+ tinybird-4.5.4.dev0.dist-info/METADATA,sha256=-Qshz86elfLGq3ZGokR18KVkN6r0YLpaF13n8g-8sd4,10228
101
+ tinybird-4.5.4.dev0.dist-info/WHEEL,sha256=beeZ86-EfXScwlR_HKu4SllMC9wUEj_8Z_4FJ3egI2w,91
102
+ tinybird-4.5.4.dev0.dist-info/entry_points.txt,sha256=LwdHU6TfKx4Qs7BqqtaczEZbImgU7Abe9Lp920zb_fo,43
103
+ tinybird-4.5.4.dev0.dist-info/top_level.txt,sha256=ZIQJTPCzMqnfDzM_hEGZrJqDSEcKnIK_49T86DGWpyQ,78
104
+ tinybird-4.5.4.dev0.dist-info/RECORD,,