tinybird-cli 6.5.7.dev0__py3-none-any.whl → 6.5.8.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.
@@ -6,11 +6,10 @@ import pyperclip
6
6
  from click import Context
7
7
  from humanfriendly import parse_timespan
8
8
 
9
- from tinybird.client import AuthNoTokenException, TinyB
9
+ from tinybird.client import AuthNoTokenException, DoesNotExistException, TinyB
10
10
  from tinybird.feedback_manager import FeedbackManager
11
11
  from tinybird.tb_cli_modules.cli import cli
12
12
  from tinybird.tb_cli_modules.common import (
13
- DoesNotExistException,
14
13
  coro,
15
14
  echo_safe_humanfriendly_tables_format_smart_table,
16
15
  )
@@ -115,17 +115,16 @@ async def clear_workspace(ctx: Context, yes: bool, dry_run: bool) -> None:
115
115
  if config["id"] == workspace["id"]:
116
116
  if workspace.get("is_branch"):
117
117
  raise CLIWorkspaceException(FeedbackManager.error_not_allowed_in_branch(cli="tb"))
118
- else:
119
- click.echo(FeedbackManager.info_current_workspace())
120
- table.append(
121
- [
122
- workspace["name"],
123
- workspace["id"],
124
- workspace["role"],
125
- _get_workspace_plan_name(workspace["plan"]),
126
- True,
127
- ]
128
- )
118
+ click.echo(FeedbackManager.info_current_workspace())
119
+ table.append(
120
+ [
121
+ workspace["name"],
122
+ workspace["id"],
123
+ workspace["role"],
124
+ _get_workspace_plan_name(workspace["plan"]),
125
+ True,
126
+ ]
127
+ )
129
128
  break
130
129
 
131
130
  echo_safe_humanfriendly_tables_format_smart_table(table, column_names=columns)
@@ -178,8 +177,7 @@ async def clear_workspace(ctx: Context, yes: bool, dry_run: bool) -> None:
178
177
  except Exception as e:
179
178
  if "is a Shared Data Source" in str(e):
180
179
  raise CLIWorkspaceException(FeedbackManager.error_operation_can_not_be_performed(error=e))
181
- else:
182
- raise CLIWorkspaceException(FeedbackManager.error_exception(error=e))
180
+ raise CLIWorkspaceException(FeedbackManager.error_exception(error=e))
183
181
  else:
184
182
  click.echo(FeedbackManager.info_dry_removing_datasource(datasource=ds_name))
185
183
 
@@ -196,13 +196,14 @@ if you need to include a literal ``{{``, ``{%``, or ``{#`` in the output.
196
196
  """
197
197
 
198
198
  import ast
199
+ import builtins
199
200
  import datetime
200
201
  import linecache
201
202
  import os.path
202
- import posixpath
203
203
  import re
204
204
  import threading
205
205
  from io import StringIO
206
+ from types import MappingProxyType
206
207
  from typing import Dict, Union
207
208
 
208
209
  from tornado import escape
@@ -235,14 +236,12 @@ def filter_whitespace(mode, text):
235
236
  """
236
237
  if mode == "all":
237
238
  return text
238
- elif mode == "single":
239
+ if mode == "single":
239
240
  text = _PATTERN_TAB_SPACE.sub(" ", text)
240
- text = _PATTERN_NEWLINE_SPACE.sub("\n", text)
241
- return text
242
- elif mode == "oneline":
241
+ return _PATTERN_NEWLINE_SPACE.sub("\n", text)
242
+ if mode == "oneline":
243
243
  return _PATTERN_ALL_WHITESPACE.sub(" ", text)
244
- else:
245
- raise Exception("invalid whitespace mode %s" % mode)
244
+ raise Exception("invalid whitespace mode %s" % mode)
246
245
 
247
246
 
248
247
  class Template:
@@ -346,6 +345,8 @@ class Template:
346
345
  }
347
346
  namespace.update(self.namespace)
348
347
  namespace.update(kwargs)
348
+ # Set last so neither the loader namespace nor kwargs can swap in the real builtins
349
+ namespace["__builtins__"] = SAFE_BUILTINS
349
350
  exec_in(self.compiled, namespace)
350
351
  execute = namespace["_tt_execute"]
351
352
  # Clear the traceback module's cache of source data now that
@@ -460,30 +461,7 @@ class Loader(BaseLoader):
460
461
  def _create_template(self, name):
461
462
  path = os.path.join(self.root, name)
462
463
  with open(path, "rb") as f:
463
- template = Template(f.read(), name=name, loader=self)
464
- return template
465
-
466
-
467
- class DictLoader(BaseLoader):
468
- """A template loader that loads from a dictionary."""
469
-
470
- def __init__(self, dict, **kwargs):
471
- super().__init__(**kwargs)
472
- self.dict = dict
473
-
474
- def resolve_path(self, name, parent_path=None):
475
- if (
476
- parent_path
477
- and not parent_path.startswith("<")
478
- and not parent_path.startswith("/")
479
- and not name.startswith("/")
480
- ):
481
- file_dir = posixpath.dirname(parent_path)
482
- name = posixpath.normpath(posixpath.join(file_dir, name))
483
- return name
484
-
485
- def _create_template(self, name):
486
- return Template(self.dict[name], name=name, loader=self)
464
+ return Template(f.read(), name=name, loader=self)
487
465
 
488
466
 
489
467
  class _Node:
@@ -553,45 +531,10 @@ class _ExtendsBlock(_Node):
553
531
  self.name = name
554
532
 
555
533
 
556
- class _IncludeBlock(_Node):
557
- def __init__(self, name, reader, line):
558
- self.name = name
559
- self.template_name = reader.name
560
- self.line = line
561
-
562
- def find_named_blocks(self, loader, named_blocks):
563
- included = loader.load(self.name, self.template_name)
564
- included.file.find_named_blocks(loader, named_blocks)
565
-
566
- def generate(self, writer):
567
- included = writer.loader.load(self.name, self.template_name)
568
- with writer.include(included, self.line):
569
- included.file.body.generate(writer)
570
-
571
-
572
- class _ApplyBlock(_Node):
573
- def __init__(self, method, line, body=None):
574
- self.method = method
575
- self.line = line
576
- self.body = body
577
-
578
- def each_child(self):
579
- return (self.body,)
580
-
581
- def generate(self, writer):
582
- method_name = "_tt_apply%d" % writer.apply_counter
583
- writer.apply_counter += 1
584
- writer.write_line("def %s():" % method_name, self.line)
585
- with writer.indent():
586
- writer.write_line("_tt_buffer = []", self.line)
587
- writer.write_line("_tt_append = _tt_buffer.append", self.line)
588
- self.body.generate(writer)
589
- writer.write_line("return _tt_utf8('').join(_tt_buffer)", self.line)
590
- writer.write_line("_tt_append(_tt_utf8(%s(%s())))" % (self.method, method_name), self.line)
591
-
592
-
593
534
  class _ControlBlock(_Node):
594
535
  def __init__(self, statement, line, body=None):
536
+ if not disable_template_security_validation.get(False):
537
+ check_valid_control_statement(statement)
595
538
  self.statement = statement
596
539
  self.line = line
597
540
  self.body = body
@@ -609,6 +552,8 @@ class _ControlBlock(_Node):
609
552
 
610
553
  class _IntermediateControlBlock(_Node):
611
554
  def __init__(self, statement, line):
555
+ if not disable_template_security_validation.get(False):
556
+ check_valid_control_statement(statement)
612
557
  self.statement = statement
613
558
  self.line = line
614
559
 
@@ -620,6 +565,9 @@ class _IntermediateControlBlock(_Node):
620
565
 
621
566
  class _Statement(_Node):
622
567
  def __init__(self, statement, line):
568
+ # Backs both {% set %} (statement is the part after "set") and {% break/continue %}
569
+ if not disable_template_security_validation.get(False) and statement not in ("break", "continue"):
570
+ check_valid_set_statement(statement)
623
571
  self.statement = statement
624
572
  self.line = line
625
573
 
@@ -646,11 +594,6 @@ class _Expression(_Node):
646
594
  writer.write_line("_tt_append(_tt_tmp)", self.line)
647
595
 
648
596
 
649
- class _Module(_Expression):
650
- def __init__(self, expression, line):
651
- super().__init__("_tt_modules." + expression, line, raw=True)
652
-
653
-
654
597
  class _Text(_Node):
655
598
  def __init__(self, value, line, whitespace):
656
599
  self.value = value
@@ -811,10 +754,9 @@ class _TemplateReader:
811
754
  if stop is not None:
812
755
  stop += self.pos
813
756
  return self.text[slice(start, stop, step)]
814
- elif key < 0:
757
+ if key < 0:
815
758
  return self.text[key]
816
- else:
817
- return self.text[self.pos + key]
759
+ return self.text[self.pos + key]
818
760
 
819
761
  def __str__(self):
820
762
  return self.text[self.pos :]
@@ -933,12 +875,12 @@ def _parse(reader: _TemplateReader, template, in_block=None, in_loop=None):
933
875
  continue
934
876
 
935
877
  # End tag
936
- elif operator == "end":
878
+ if operator == "end":
937
879
  if not in_block:
938
880
  reader.raise_parse_error("Extra {% end %} block")
939
881
  return body
940
882
 
941
- elif operator in (
883
+ if operator in (
942
884
  "extends",
943
885
  "include",
944
886
  "set",
@@ -977,7 +919,7 @@ def _parse(reader: _TemplateReader, template, in_block=None, in_loop=None):
977
919
  body.chunks.append(block)
978
920
  continue
979
921
 
980
- elif operator in ("apply", "block", "try", "if", "for", "while"):
922
+ if operator in ("apply", "block", "try", "if", "for", "while"):
981
923
  # parse inner body recursively
982
924
  if operator in ("for", "while"):
983
925
  block_body = _parse(reader, template, operator, operator)
@@ -995,14 +937,13 @@ def _parse(reader: _TemplateReader, template, in_block=None, in_loop=None):
995
937
  body.chunks.append(block)
996
938
  continue
997
939
 
998
- elif operator in ("break", "continue"):
940
+ if operator in ("break", "continue"):
999
941
  if not in_loop:
1000
942
  reader.raise_parse_error("%s outside %s block" % (operator, {"for", "while"}))
1001
943
  body.chunks.append(_Statement(contents, line))
1002
944
  continue
1003
945
 
1004
- else:
1005
- reader.raise_parse_error("unknown operator: %r" % operator)
946
+ reader.raise_parse_error("unknown operator: %r" % operator)
1006
947
 
1007
948
 
1008
949
  VALID_CUSTOM_FUNCTION_NAMES = {
@@ -1119,6 +1060,10 @@ def check_valid_code(code):
1119
1060
  Traceback (most recent call last):
1120
1061
  ...
1121
1062
  tinybird.tornado_template.SecurityException: Invalid expression type: Attribute(value=Name(id='len', ctx=Load()), attr='__self__', ctx=Load())
1063
+ >>> check_valid_code('''String(__builtins__)''')
1064
+ Traceback (most recent call last):
1065
+ ...
1066
+ tinybird.tornado_template.SecurityException: Invalid name: __builtins__
1122
1067
  """
1123
1068
  body = ast.parse(code).body
1124
1069
  for expr in body:
@@ -1142,6 +1087,11 @@ def check_valid_expr(expr):
1142
1087
  raise SecurityException(f"Invalid function: {ast.dump(expr)}")
1143
1088
  for subexpr in expr.args:
1144
1089
  check_valid_expr(subexpr)
1090
+ # Keyword values never went through the allowlist and customers call functions it doesn't list
1091
+ # there, so they only get the checks that block reaching interpreter internals
1092
+ for keyword in expr.keywords:
1093
+ for node in ast.walk(keyword.value):
1094
+ _check_node_is_safe(node)
1145
1095
  elif isinstance(expr, ast.Constant):
1146
1096
  if isinstance(expr.value, int):
1147
1097
  return
@@ -1153,7 +1103,9 @@ def check_valid_expr(expr):
1153
1103
  return
1154
1104
  raise SecurityException(f"Invalid Constant: {ast.dump(expr)}")
1155
1105
  elif isinstance(expr, ast.Name):
1156
- return
1106
+ # Same rule as control blocks: dunder names such as __builtins__ only reach interpreter internals
1107
+ if _is_dunder(expr.id):
1108
+ raise SecurityException(f"Invalid name: {expr.id}")
1157
1109
  elif isinstance(expr, ast.UnaryOp):
1158
1110
  if not isinstance(expr.op, ast.USub) and not isinstance(expr.op, ast.Not):
1159
1111
  raise SecurityException(f"Invalid UnaryOp: {ast.dump(expr.op)}")
@@ -1173,6 +1125,8 @@ def check_valid_expr(expr):
1173
1125
  check_valid_expr(expr.slice.lower)
1174
1126
  if expr.slice.upper is not None:
1175
1127
  check_valid_expr(expr.slice.upper)
1128
+ if expr.slice.step is not None:
1129
+ check_valid_expr(expr.slice.step)
1176
1130
  elif isinstance(expr.slice, (ast.Constant, ast.Subscript)):
1177
1131
  check_valid_expr(expr.slice)
1178
1132
  else:
@@ -1190,9 +1144,199 @@ def check_valid_expr(expr):
1190
1144
  check_valid_expr(x)
1191
1145
  elif isinstance(expr, ast.FormattedValue):
1192
1146
  check_valid_expr(expr.value)
1147
+ # f"{x:{expr}}" nests expressions in the format spec, which JoinedStr.values doesn't include
1148
+ if expr.format_spec is not None:
1149
+ check_valid_expr(expr.format_spec)
1193
1150
  elif isinstance(expr, ast.IfExp):
1194
1151
  check_valid_expr(expr.test)
1195
1152
  check_valid_expr(expr.body)
1196
1153
  check_valid_expr(expr.orelse)
1197
1154
  else:
1198
1155
  raise SecurityException(f"Invalid expression type: {ast.dump(expr)}")
1156
+
1157
+
1158
+ # Names that allow evaluating code, reflection or reading attributes by string. They are not part of
1159
+ # SAFE_BUILTINS, so they can only resolve to template parameters (customers use `type`, `dir`, `property`
1160
+ # or `object` as parameter names). Reading them is allowed; calling them is rejected while parsing to give
1161
+ # a clear error instead of a NameError or "'str' object is not callable".
1162
+ UNSAFE_NAMES = frozenset(
1163
+ {
1164
+ "eval",
1165
+ "exec",
1166
+ "compile",
1167
+ "getattr",
1168
+ "setattr",
1169
+ "delattr",
1170
+ "globals",
1171
+ "locals",
1172
+ "vars",
1173
+ "dir",
1174
+ "breakpoint",
1175
+ "help",
1176
+ "memoryview",
1177
+ "type",
1178
+ "object",
1179
+ "super",
1180
+ "classmethod",
1181
+ "staticmethod",
1182
+ "property",
1183
+ "exit",
1184
+ "quit",
1185
+ }
1186
+ )
1187
+
1188
+ # Attributes that reach interpreter internals without a leading underscore (frames and code objects of
1189
+ # generators, coroutines and tracebacks) or that read arbitrary attributes through a format string
1190
+ # ("{0.__class__}".format(x)). Any attribute starting with "_" is rejected too. Same idea as jinja2.sandbox.
1191
+ UNSAFE_ATTRIBUTE_NAMES = frozenset(
1192
+ {
1193
+ "format",
1194
+ "format_map",
1195
+ "mro",
1196
+ "gi_frame",
1197
+ "gi_code",
1198
+ "gi_yieldfrom",
1199
+ "cr_frame",
1200
+ "cr_code",
1201
+ "cr_await",
1202
+ "cr_origin",
1203
+ "ag_frame",
1204
+ "ag_code",
1205
+ "ag_await",
1206
+ "f_globals",
1207
+ "f_locals",
1208
+ "f_builtins",
1209
+ "f_back",
1210
+ "f_code",
1211
+ "tb_frame",
1212
+ "tb_next",
1213
+ }
1214
+ )
1215
+
1216
+ # Builtins exposed to compiled templates. Without an explicit "__builtins__" in the exec namespace
1217
+ # Python injects the real builtins module, so anything that slips through validation could reach them.
1218
+ # Private names are dropped because __loader__ and __spec__ can load the builtins module back. It is
1219
+ # read-only because the same mapping is shared by every render in the process.
1220
+ SAFE_BUILTINS = MappingProxyType(
1221
+ {
1222
+ name: value
1223
+ for name, value in vars(builtins).items()
1224
+ if not name.startswith("_")
1225
+ and name not in UNSAFE_NAMES
1226
+ # site's interactive helpers: license() pages its text through input()
1227
+ and name not in ("open", "input", "print", "copyright", "credits", "license")
1228
+ }
1229
+ )
1230
+
1231
+ # Number of statements (including the "pass" bodies we add) each control block must parse to. Anything
1232
+ # else means the block smuggled extra statements, e.g. "{% if x: pass\nimport os\nif y %}" or "a = 1; b = 2".
1233
+ _CONTROL_BLOCK_SOURCES = {
1234
+ "if": ("{statement}:\n pass", 2),
1235
+ "while": ("{statement}:\n pass", 2),
1236
+ "for": ("{statement}:\n pass", 2),
1237
+ "elif": ("if 0:\n pass\n{statement}:\n pass", 4),
1238
+ "except": ("try:\n pass\n{statement}:\n pass", 3),
1239
+ }
1240
+
1241
+
1242
+ def _is_dunder(name: str) -> bool:
1243
+ return len(name) > 4 and name.startswith("__") and name.endswith("__")
1244
+
1245
+
1246
+ def _check_node_is_safe(node: ast.AST) -> None:
1247
+ if isinstance(node, ast.Call) and isinstance(node.func, ast.Name) and node.func.id in UNSAFE_NAMES:
1248
+ raise SecurityException(f"Invalid function name: {node.func.id}")
1249
+ if isinstance(node, ast.Attribute):
1250
+ if node.attr.startswith("_") or node.attr in UNSAFE_ATTRIBUTE_NAMES:
1251
+ raise SecurityException(f"Invalid attribute: {node.attr}")
1252
+ elif isinstance(node, ast.Name):
1253
+ if _is_dunder(node.id):
1254
+ raise SecurityException(f"Invalid name: {node.id}")
1255
+ elif isinstance(node, ast.ExceptHandler):
1256
+ if node.name and _is_dunder(node.name):
1257
+ raise SecurityException(f"Invalid name: {node.name}")
1258
+ elif isinstance(node, (ast.Yield, ast.YieldFrom, ast.Await)):
1259
+ raise SecurityException(f"Invalid expression type: {ast.dump(node)}")
1260
+
1261
+
1262
+ def _check_valid_statement_tree(tree: ast.Module, expected_statements: int, statement: str) -> None:
1263
+ statements = 0
1264
+ for node in ast.walk(tree):
1265
+ if isinstance(node, ast.stmt):
1266
+ statements += 1
1267
+ _check_node_is_safe(node)
1268
+ if statements != expected_statements:
1269
+ raise SecurityException(f"Invalid control block: {statement}")
1270
+
1271
+
1272
+ def check_valid_control_statement(statement: str) -> None:
1273
+ """Validate the Python code a {% if/elif/for/while/try/else/except/finally %} block emits verbatim.
1274
+
1275
+ Control blocks accept a wider set of expressions than {{ }} (comparisons, boolean operators, any
1276
+ method), so instead of an allowlist they reject what gives access to the interpreter: dunder names
1277
+ and attributes, frame/code attributes, eval-like builtins and extra smuggled statements.
1278
+
1279
+ >>> check_valid_control_statement("if defined(x) and x.lower() in ('a', 'b')")
1280
+ >>> check_valid_control_statement("for i, item in enumerate(split_to_array(x, ''))")
1281
+ >>> check_valid_control_statement("except Exception as e")
1282
+ >>> check_valid_control_statement("if ().__class__.__base__.__subclasses__()")
1283
+ Traceback (most recent call last):
1284
+ ...
1285
+ tinybird.tornado_template.SecurityException: Invalid attribute: __subclasses__
1286
+ >>> check_valid_control_statement("elif eval('1')")
1287
+ Traceback (most recent call last):
1288
+ ...
1289
+ tinybird.tornado_template.SecurityException: Invalid function name: eval
1290
+ >>> check_valid_control_statement("if defined(property) and object != 'x'")
1291
+ >>> check_valid_control_statement("for x in (i for i in [1]).gi_frame.f_builtins")
1292
+ Traceback (most recent call last):
1293
+ ...
1294
+ tinybird.tornado_template.SecurityException: Invalid attribute: f_builtins
1295
+ >>> check_valid_control_statement("else if 1")
1296
+ Traceback (most recent call last):
1297
+ ...
1298
+ tinybird.tornado_template.SecurityException: Invalid control block: else if 1
1299
+ """
1300
+ operator = statement.partition(" ")[0]
1301
+ if operator in ("try", "else", "finally"):
1302
+ if statement != operator:
1303
+ raise SecurityException(f"Invalid control block: {statement}")
1304
+ return
1305
+ if operator not in _CONTROL_BLOCK_SOURCES:
1306
+ raise SecurityException(f"Invalid control block: {statement}")
1307
+ source, expected_statements = _CONTROL_BLOCK_SOURCES[operator]
1308
+ try:
1309
+ tree = ast.parse(source.format(statement=statement))
1310
+ except SyntaxError:
1311
+ # Compiling the generated template fails too, so nothing gets executed. Let that error surface
1312
+ # since its message includes the template line number users rely on.
1313
+ return
1314
+ _check_valid_statement_tree(tree, expected_statements, statement)
1315
+
1316
+
1317
+ def check_valid_set_statement(statement: str) -> None:
1318
+ """Validate the Python code a {% set %} block emits verbatim (``statement`` excludes "set").
1319
+
1320
+ >>> check_valid_set_statement("total = a + b * c")
1321
+ >>> check_valid_set_statement("csv = ','.join(test)")
1322
+ >>> check_valid_set_statement("x = ().__class__")
1323
+ Traceback (most recent call last):
1324
+ ...
1325
+ tinybird.tornado_template.SecurityException: Invalid attribute: __class__
1326
+ >>> check_valid_set_statement("x = 1; y = 2")
1327
+ Traceback (most recent call last):
1328
+ ...
1329
+ tinybird.tornado_template.SecurityException: Invalid set statement: x = 1; y = 2
1330
+ >>> check_valid_set_statement("import os")
1331
+ Traceback (most recent call last):
1332
+ ...
1333
+ tinybird.tornado_template.SecurityException: Invalid set statement: import os
1334
+ """
1335
+ try:
1336
+ tree = ast.parse(statement)
1337
+ except SyntaxError:
1338
+ # Same as in check_valid_control_statement: compiling the template raises a better error
1339
+ return
1340
+ if len(tree.body) != 1 or not isinstance(tree.body[0], (ast.Assign, ast.AugAssign, ast.Expr)):
1341
+ raise SecurityException(f"Invalid set statement: {statement}")
1342
+ _check_valid_statement_tree(tree, 1, statement)
File without changes
@@ -0,0 +1,19 @@
1
+ TRUE_VALUES = {"y", "yes", "t", "true", "on", "1"}
2
+ FALSE_VALUES = {"n", "no", "f", "false", "off", "0"}
3
+
4
+
5
+ def parse_bool(value: object) -> bool:
6
+ normalized_value = str(value).strip().lower()
7
+ if normalized_value in TRUE_VALUES:
8
+ return True
9
+ if normalized_value in FALSE_VALUES:
10
+ return False
11
+ raise ValueError(f"invalid truth value {value!r}")
12
+
13
+
14
+ def parse_optional_bool(value: object, default: bool = False) -> bool:
15
+ if value is None or value == "":
16
+ return default
17
+ if isinstance(value, bool):
18
+ return value
19
+ return parse_bool(value)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: tinybird_cli
3
- Version: 6.5.7.dev0
3
+ Version: 6.5.8.dev0
4
4
  Summary: Tinybird Command Line Tool
5
5
  Home-page: https://www.tinybird.co/docs/cli
6
6
  Author: Tinybird
@@ -44,6 +44,14 @@ Changelog
44
44
  ----------
45
45
 
46
46
 
47
+ 6.5.7
48
+ ***********
49
+
50
+ - `Added` ``Markdown`` is now a valid ``FORMAT`` for SQL queries (for example ``tb sql "SELECT 1 FORMAT Markdown"``), returned as a Markdown table.
51
+ - `Changed` ``tb datasource append`` and ``tb datasource replace`` submit remote CSV, NDJSON, and Parquet URLs through the v1 import endpoint by default.
52
+ - `Added` Copy pipes accept ``ON_DEMAND_COMPUTE true`` so scheduled runs use on-demand compute. Manual ``tb pipe copy run`` still requires ``--on-demand-compute``, and asks for confirmation when the local datafile does not enable it (skip it with ``--yes``).
53
+ - `Removed` The ``--main`` flag from ``tb branch regression-tests``. It ran the comparison against the main Branch for a custom CI workflow.
54
+
47
55
  6.5.6
48
56
  ***********
49
57
 
@@ -0,0 +1,45 @@
1
+ tinybird/__cli__.py,sha256=F7AVtKOPyaRjDZePzmuvAXP6pEkgj0y6o0A2lTpF_l8,237
2
+ tinybird/check_pypi.py,sha256=_4NkharLyR_ELrAdit-ftqIWvOf7jZNPt3i76frlo9g,975
3
+ tinybird/client.py,sha256=DwU-NJxwCMqlCB3NusERk2xqR8YrmtXkb84glKbGCzE,53506
4
+ tinybird/config.py,sha256=noWhuwgbiuhw9ebB3UL0_8k44ODmo2neNaaI_Ah5zrQ,6806
5
+ tinybird/context.py,sha256=o4yvlXPkMLmdh-XJl3wpmqPAMeRRz5ScKzKlHHKn_I8,1201
6
+ tinybird/datafile_common.py,sha256=w_MvfS8nmLuXTZy4u_xluzJK-nN_x80soTZsuiHXNXg,239278
7
+ tinybird/datatypes.py,sha256=3MMHtTYE0TGjNDE1xFyZlQx3SsUMrbnRUEkxVrJyAE8,12009
8
+ tinybird/feedback_manager.py,sha256=voAqH_isSrFzMcs2Bc8HfOMeePzjpgRD5OWqsNTOplc,68667
9
+ tinybird/git_settings.py,sha256=ETrcrvnhi3I9W-cGOwILXTBBNVFm5lYbeDdgTFaxT48,3898
10
+ tinybird/sql.py,sha256=V807-z7tlZWMowr2ezYhsxjFWm-rWejaQ1mlr0ZXtPU,47173
11
+ tinybird/sql_template.py,sha256=wxVDNCd0T5vmU4m3zM4HDORr8KVj6zZNnkK0FSF5icg,134046
12
+ tinybird/sql_template_fmt.py,sha256=cOhhkA9h_M8II-06Qx5pt3E7IXUgzvrzfWGMtWdtkUM,10525
13
+ tinybird/sql_toolset.py,sha256=ZLJUkqYxrvZOeKAoiItMYDjRat4qs9IH8CenbnglF1U,29926
14
+ tinybird/syncasync.py,sha256=fFQ5GPPnUlTJ-J3VTw9AZCNXtp5IUiP9Q22sZXcixU4,26352
15
+ tinybird/tb_cli.py,sha256=q1LGAsBVVMJsjR2HK62Pu6vpVtLzNmH8wHrEVUUdVkU,744
16
+ tinybird/tornado_template.py,sha256=mTQMTywuqTfk9xX737mT5Fv_o-PNHJxpgTa4LF-edCE,49079
17
+ tinybird/ch_utils/constants.py,sha256=8q5SH-0uBpYsBzmPpYsyhy-wcqvLgK1hmPr_NsKKoi8,4349
18
+ tinybird/ch_utils/engine.py,sha256=UAa7lFnAN5Wu25I-XyHKh23yGSQ7Ji4M_TvxpWv8q5o,38813
19
+ tinybird/tb_cli_modules/auth.py,sha256=3xu8STgouOgLkqlBf9LWFg9Oto_NyuDKsUWF95-zGwI,8741
20
+ tinybird/tb_cli_modules/branch.py,sha256=qGwOgqk91up1IjbfvjctCGXi_fqPQ6IO_mbm_u7ekb0,38642
21
+ tinybird/tb_cli_modules/cicd.py,sha256=T5GBMRmxLeblNKV9XICGNA37mJVtUHx2sex463-QLsk,13845
22
+ tinybird/tb_cli_modules/cli.py,sha256=UfrPkkvvvviJh3UTK9l24FgwIptotAWKytQj0zBgRD4,60156
23
+ tinybird/tb_cli_modules/common.py,sha256=7PCdcyK1pEyDwTUjNk7-DmSaitTIqAkVx_XYC-nIqLA,76015
24
+ tinybird/tb_cli_modules/config.py,sha256=xF4L95Flpfg-TzGkfgpYmiiDbDjxoSWJLfYt1dCdFNQ,11164
25
+ tinybird/tb_cli_modules/connection.py,sha256=JO2v2xTGoiMeThzCncvxMFUHLghyyGXNF0FcAvi5ZOk,24543
26
+ tinybird/tb_cli_modules/datasource.py,sha256=OnhbCeJtvhrVvMRqP7owA_rCZQXz-1j1l45H_NB4ALU,36467
27
+ tinybird/tb_cli_modules/exceptions.py,sha256=pmucP4kTF4irIt7dXiG-FcnI-o3mvDusPmch1L8RCWk,3367
28
+ tinybird/tb_cli_modules/fmt.py,sha256=edQap4tAqWMWogSIx5zriT75naLi73XTB3NwatmcrFw,3518
29
+ tinybird/tb_cli_modules/job.py,sha256=BxiqChDYa_4W4tFXHIUz2bOzkx8WMFywynjiPWgDlnE,2936
30
+ tinybird/tb_cli_modules/pipe.py,sha256=ybcMd29IOPtFamujuGa8yKkXJpmJhI_QQCumseulxag,31901
31
+ tinybird/tb_cli_modules/regions.py,sha256=QjsL5H6Kg-qr0aYVLrvb1STeJ5Sx_sjvbOYO0LrEGMk,166
32
+ tinybird/tb_cli_modules/tag.py,sha256=S5x8uDW0rsJEI5lKH2mefro7tEzcHyDNWrV_ZWNyS1E,3483
33
+ tinybird/tb_cli_modules/telemetry.py,sha256=W098H6jmS4kpE7hN3tadaREBTf7oMocel-lkKWN0pU8,10466
34
+ tinybird/tb_cli_modules/test.py,sha256=Vf8oK96V81HdKGsT79y6MUz6oz_VrYIwTbRnzzJs4rQ,4350
35
+ tinybird/tb_cli_modules/token.py,sha256=Tt9ijCrlahQsU2gFKmFI8bJFNSJGhY88wAAWCLiVxCM,13789
36
+ tinybird/tb_cli_modules/workspace.py,sha256=AWzz4qbj7LaFL2PaZohWtKKyjCMX-LPup867eRMlWVE,12621
37
+ tinybird/tb_cli_modules/workspace_members.py,sha256=ksXsjd233y9-sNlz4Qb-meZbX4zn1B84e_bSm2i8rhg,8731
38
+ tinybird/tb_cli_modules/tinyunit/tinyunit.py,sha256=IyyTdwnmF-fgEnJD3d8ntszYSFPJ5BqL6iTHqyLilkk,11055
39
+ tinybird/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
40
+ tinybird/utils/bools.py,sha256=Rwuh_R6j62JVkcxKskLQao7DE2Jj6TQzsbgvtCsLHP4,587
41
+ tinybird_cli-6.5.8.dev0.dist-info/METADATA,sha256=STqCtqkTFw7URbtT7AXxqfvCVb8G59BawS5dyjEijTs,84811
42
+ tinybird_cli-6.5.8.dev0.dist-info/WHEEL,sha256=beeZ86-EfXScwlR_HKu4SllMC9wUEj_8Z_4FJ3egI2w,91
43
+ tinybird_cli-6.5.8.dev0.dist-info/entry_points.txt,sha256=PKPKuPmA4IfJYnCFHHUiw-aAWZuBomFvwCklv1OyCjE,43
44
+ tinybird_cli-6.5.8.dev0.dist-info/top_level.txt,sha256=ZIQJTPCzMqnfDzM_hEGZrJqDSEcKnIK_49T86DGWpyQ,78
45
+ tinybird_cli-6.5.8.dev0.dist-info/RECORD,,
@@ -1,43 +0,0 @@
1
- tinybird/__cli__.py,sha256=i_-xpNFaP_CRKjaWjkZKj-4VaBC1PpNyvC6YTjFn8XM,236
2
- tinybird/check_pypi.py,sha256=_4NkharLyR_ELrAdit-ftqIWvOf7jZNPt3i76frlo9g,975
3
- tinybird/client.py,sha256=av3w9-lKfDWe-rKFE-V05-D4Ytr90kf2BCqu6z7XXH0,52753
4
- tinybird/config.py,sha256=ltL9qIfY-tnFXeivrLLi7apS_OkzEJT_jRXAJu0SRQg,7164
5
- tinybird/context.py,sha256=o4yvlXPkMLmdh-XJl3wpmqPAMeRRz5ScKzKlHHKn_I8,1201
6
- tinybird/datafile_common.py,sha256=q9rK4MfbfbDTus4jcYNI2Kfq8K3rlN344wM17MDVdCs,237658
7
- tinybird/datatypes.py,sha256=DRScTI1-lgYjbC7u_4qxOLFleWL-vief_eBg_9WU37U,11304
8
- tinybird/feedback_manager.py,sha256=2n0s6AMbjtFsELJN9pJnG5JjhM3lUueXBgF4mZ0WiCw,68318
9
- tinybird/git_settings.py,sha256=mqWgeboOlOFsSo97qyv595UCR2R1QCAqT4GTawBNPBg,3935
10
- tinybird/sql.py,sha256=L3tTmm62iTrf6WQdcV8mmwrCMitLFUvJ3OfDzyRTMGw,47385
11
- tinybird/sql_template.py,sha256=BLX_qAmnCrRouLAcpqey32-Ctub2ZaXSvpvgyadGHvM,132236
12
- tinybird/sql_template_fmt.py,sha256=Ma4qcs-2r8ZXQC4GUmrCqYz34DsnGF8k5lE2Jwnr314,10638
13
- tinybird/sql_toolset.py,sha256=qNKHPGKTR4MrMHKkj1qHqmlB1kPqnbT-SozyzUBeD6s,28498
14
- tinybird/syncasync.py,sha256=rIPmCvygWSFqfnlVqhZH4N9gVVTvD6DEPsfoxGizYrI,27776
15
- tinybird/tb_cli.py,sha256=q1LGAsBVVMJsjR2HK62Pu6vpVtLzNmH8wHrEVUUdVkU,744
16
- tinybird/tornado_template.py,sha256=8e0ipnBQI4-RO5sFokybDNwkd1CBxGqvnnyJ5kjs5XU,42060
17
- tinybird/ch_utils/constants.py,sha256=yTNizMzgYNBzUc2EV3moBfdrDIggOe9hiuAgWF7sv2c,4333
18
- tinybird/ch_utils/engine.py,sha256=OkR86FOOUPRpCsRxlR-kunykg-K4Daqo3uS06X1uvYQ,36887
19
- tinybird/tb_cli_modules/auth.py,sha256=3xu8STgouOgLkqlBf9LWFg9Oto_NyuDKsUWF95-zGwI,8741
20
- tinybird/tb_cli_modules/branch.py,sha256=vbjLxIHz5XOqfMGwjyEZ7gji8Jw3PWPKKrPXViWdiIc,39348
21
- tinybird/tb_cli_modules/cicd.py,sha256=i2Mw8AbmEVNBcEPYdio7liy3PGqh1ezVFZ0OmJ9ww5o,13809
22
- tinybird/tb_cli_modules/cli.py,sha256=KvgZfdYRvgKG5QAiFmlfLbVGI-xmuxbs-EdkAslqrEQ,60242
23
- tinybird/tb_cli_modules/common.py,sha256=tgiEmN0dAIrYYVMO-ezwiDAS3mGghCMB0ijD-gcZEtE,79524
24
- tinybird/tb_cli_modules/config.py,sha256=lukiJeihI09blGeeS0FWeqVbKWhqzZmyoaJDIL0NkyQ,11182
25
- tinybird/tb_cli_modules/connection.py,sha256=JO2v2xTGoiMeThzCncvxMFUHLghyyGXNF0FcAvi5ZOk,24543
26
- tinybird/tb_cli_modules/datasource.py,sha256=4tkdj17-GrZ7VbsnDQ8KUA761BmSeD3XQOOqbZjUko0,36483
27
- tinybird/tb_cli_modules/exceptions.py,sha256=pmucP4kTF4irIt7dXiG-FcnI-o3mvDusPmch1L8RCWk,3367
28
- tinybird/tb_cli_modules/fmt.py,sha256=edQap4tAqWMWogSIx5zriT75naLi73XTB3NwatmcrFw,3518
29
- tinybird/tb_cli_modules/job.py,sha256=BxiqChDYa_4W4tFXHIUz2bOzkx8WMFywynjiPWgDlnE,2936
30
- tinybird/tb_cli_modules/pipe.py,sha256=hIvkiGv9Zvm2KgJjFzGKjc7HQZdEmEt61yFwngXhEJ8,31672
31
- tinybird/tb_cli_modules/regions.py,sha256=QjsL5H6Kg-qr0aYVLrvb1STeJ5Sx_sjvbOYO0LrEGMk,166
32
- tinybird/tb_cli_modules/tag.py,sha256=S5x8uDW0rsJEI5lKH2mefro7tEzcHyDNWrV_ZWNyS1E,3483
33
- tinybird/tb_cli_modules/telemetry.py,sha256=W098H6jmS4kpE7hN3tadaREBTf7oMocel-lkKWN0pU8,10466
34
- tinybird/tb_cli_modules/test.py,sha256=Vf8oK96V81HdKGsT79y6MUz6oz_VrYIwTbRnzzJs4rQ,4350
35
- tinybird/tb_cli_modules/token.py,sha256=JXATKTlbXohP9ZDZjlz8E4VYG6zrknKZhuz_wh1zBBc,13793
36
- tinybird/tb_cli_modules/workspace.py,sha256=-GW_XZHPc5atVFUkYpXsl1G8t36jZyZ1B0UWAZYwSaA,12709
37
- tinybird/tb_cli_modules/workspace_members.py,sha256=ksXsjd233y9-sNlz4Qb-meZbX4zn1B84e_bSm2i8rhg,8731
38
- tinybird/tb_cli_modules/tinyunit/tinyunit.py,sha256=JrZQTb9NA-1kcL2Vm0JuDRNFdH8jcYYOM3ovJDVs28E,11063
39
- tinybird_cli-6.5.7.dev0.dist-info/METADATA,sha256=yzq4mC9BBAK4Q_zIsrc0h4kpR-OZzBR8PffM31ydFvQ,84089
40
- tinybird_cli-6.5.7.dev0.dist-info/WHEEL,sha256=beeZ86-EfXScwlR_HKu4SllMC9wUEj_8Z_4FJ3egI2w,91
41
- tinybird_cli-6.5.7.dev0.dist-info/entry_points.txt,sha256=PKPKuPmA4IfJYnCFHHUiw-aAWZuBomFvwCklv1OyCjE,43
42
- tinybird_cli-6.5.7.dev0.dist-info/top_level.txt,sha256=ZIQJTPCzMqnfDzM_hEGZrJqDSEcKnIK_49T86DGWpyQ,78
43
- tinybird_cli-6.5.7.dev0.dist-info/RECORD,,