tinybird 4.6.19.dev0__tar.gz → 4.6.20.dev0__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 (127) hide show
  1. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/PKG-INFO +6 -1
  2. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/sql.py +3 -4
  3. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/sql_template.py +52 -71
  4. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/sql_template_fmt.py +8 -13
  5. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/sql_toolset.py +3 -5
  6. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/syncasync.py +9 -11
  7. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/__cli__.py +2 -2
  8. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/client.py +14 -23
  9. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/cicd.py +1 -0
  10. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/cli.py +4 -2
  11. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/common.py +8 -12
  12. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/config.py +1 -2
  13. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/connection_kafka.py +1 -2
  14. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/connection_s3.py +13 -18
  15. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/create.py +3 -6
  16. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/datafile/build.py +2 -3
  17. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/datafile/build_pipe.py +1 -4
  18. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/datafile/diff.py +2 -1
  19. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/datafile/format_common.py +9 -10
  20. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/datafile/format_connection.py +1 -2
  21. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/datafile/format_datasource.py +1 -2
  22. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/datafile/format_pipe.py +1 -2
  23. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/datafile/pipe_checker.py +6 -7
  24. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/datafile/playground.py +3 -4
  25. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/datasource.py +7 -8
  26. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/deployment_common.py +3 -5
  27. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird.egg-info/PKG-INFO +6 -1
  28. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/setup.cfg +0 -0
  29. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/__cli__.py +0 -0
  30. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/ch_utils/constants.py +0 -0
  31. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/ch_utils/engine.py +0 -0
  32. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/check_pypi.py +0 -0
  33. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/client.py +0 -0
  34. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/config.py +0 -0
  35. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/context.py +0 -0
  36. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/datafile/common.py +0 -0
  37. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/datafile/exceptions.py +0 -0
  38. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/datafile/parse_connection.py +0 -0
  39. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/datafile/parse_datasource.py +0 -0
  40. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/datafile/parse_pipe.py +0 -0
  41. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/datatypes.py +0 -0
  42. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/feedback_manager.py +0 -0
  43. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/git_settings.py +0 -0
  44. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/iterating/__init__.py +0 -0
  45. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/iterating/data_branch_modes.py +0 -0
  46. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/prompts.py +0 -0
  47. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/service_datasources.py +0 -0
  48. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/check_pypi.py +0 -0
  49. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/cli.py +0 -0
  50. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/config.py +0 -0
  51. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/branch.py +0 -0
  52. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/build.py +0 -0
  53. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/build_common.py +0 -0
  54. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/connection.py +0 -0
  55. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/connection_dynamodb.py +0 -0
  56. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/copy.py +0 -0
  57. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/datafile/build_common.py +0 -0
  58. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/datafile/build_datasource.py +0 -0
  59. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/datafile/fixture.py +0 -0
  60. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/datafile/pull.py +0 -0
  61. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/deployment.py +0 -0
  62. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/deprecations.py +0 -0
  63. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/endpoint.py +0 -0
  64. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/exceptions.py +0 -0
  65. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/feedback_manager.py +0 -0
  66. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/fmt.py +0 -0
  67. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/info.py +0 -0
  68. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/infra.py +0 -0
  69. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/job.py +0 -0
  70. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/job_common.py +0 -0
  71. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/llm.py +0 -0
  72. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/llm_utils.py +0 -0
  73. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/local.py +0 -0
  74. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/local_common.py +0 -0
  75. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/local_logs.py +0 -0
  76. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/login.py +0 -0
  77. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/login_common.py +0 -0
  78. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/logout.py +0 -0
  79. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/logs.py +0 -0
  80. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/materialization.py +0 -0
  81. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/open.py +0 -0
  82. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/pipe.py +0 -0
  83. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/preview.py +0 -0
  84. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/project.py +0 -0
  85. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/project_commands.py +0 -0
  86. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/py_project.py +0 -0
  87. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/query_output.py +0 -0
  88. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/regions.py +0 -0
  89. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/secret.py +0 -0
  90. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/secret_common.py +0 -0
  91. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/sink.py +0 -0
  92. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/table.py +0 -0
  93. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/telemetry.py +0 -0
  94. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/test.py +0 -0
  95. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/test_common.py +0 -0
  96. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/tinyunit/tinyunit.py +0 -0
  97. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/token.py +0 -0
  98. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/ts_project.py +0 -0
  99. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/watch.py +0 -0
  100. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/workspace.py +0 -0
  101. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb/modules/workspace_members.py +0 -0
  102. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb_cli.py +0 -0
  103. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb_cli_modules/auth.py +0 -0
  104. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb_cli_modules/branch.py +0 -0
  105. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb_cli_modules/cicd.py +0 -0
  106. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb_cli_modules/cli.py +0 -0
  107. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb_cli_modules/common.py +0 -0
  108. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb_cli_modules/config.py +0 -0
  109. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb_cli_modules/connection.py +0 -0
  110. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb_cli_modules/datasource.py +0 -0
  111. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb_cli_modules/exceptions.py +0 -0
  112. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb_cli_modules/fmt.py +0 -0
  113. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb_cli_modules/job.py +0 -0
  114. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb_cli_modules/pipe.py +0 -0
  115. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb_cli_modules/regions.py +0 -0
  116. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb_cli_modules/tag.py +0 -0
  117. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb_cli_modules/telemetry.py +0 -0
  118. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb_cli_modules/test.py +0 -0
  119. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb_cli_modules/tinyunit/tinyunit.py +0 -0
  120. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb_cli_modules/workspace.py +0 -0
  121. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tb_cli_modules/workspace_members.py +0 -0
  122. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird/tornado_template.py +0 -0
  123. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird.egg-info/SOURCES.txt +0 -0
  124. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird.egg-info/dependency_links.txt +0 -0
  125. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird.egg-info/entry_points.txt +0 -0
  126. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird.egg-info/requires.txt +0 -0
  127. {tinybird-4.6.19.dev0 → tinybird-4.6.20.dev0}/tinybird.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: tinybird
3
- Version: 4.6.19.dev0
3
+ Version: 4.6.20.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,11 @@ The Tinybird command-line tool allows you to use all the Tinybird functionality
52
52
  Changelog
53
53
  ----------
54
54
 
55
+ 4.6.18
56
+ ********
57
+
58
+ - `Fixed` CLI commands (for example ``tb migrate-to-forward``) no longer abort on startup when the best-effort refresh of the cached workspace id/name fails for a reason other than authentication.
59
+
55
60
  4.6.17
56
61
  ********
57
62
 
@@ -675,10 +675,9 @@ def _parse_table_structure(schema: str) -> List[Dict[str, Any]]:
675
675
  )
676
676
  advance_single_char()
677
677
  return schema[begin:i]
678
- else:
679
- # backticked name
680
- advance_single_char()
681
- return get_backticked()
678
+ # backticked name
679
+ advance_single_char()
680
+ return get_backticked()
682
681
 
683
682
  def parse_expr(lookup: Iterable[SyntaxExpr]) -> str:
684
683
  nonlocal i, line, pos
@@ -142,8 +142,7 @@ def transform_type(
142
142
  if x is None:
143
143
  if defined:
144
144
  raise SQLTemplateException(REQUIRED_PARAM_NOT_DEFINED, documentation="/cli/advanced-templates.html")
145
- else:
146
- return None
145
+ return None
147
146
  if tester == "String":
148
147
  if x is not None:
149
148
  return transform(x)
@@ -167,12 +166,11 @@ def _and(*args, **kwargs):
167
166
  tk = k.rsplit("__", 1)
168
167
  if len(tk) == 1:
169
168
  return "="
170
- else:
171
- if tk[1] in operands:
172
- return operands[tk[1]]
173
- raise SQLTemplateException(
174
- f"operand {tk[1]} not supported", documentation="/cli/advanced-templates.html#sql_and"
175
- )
169
+ if tk[1] in operands:
170
+ return operands[tk[1]]
171
+ raise SQLTemplateException(
172
+ f"operand {tk[1]} not supported", documentation="/cli/advanced-templates.html#sql_and"
173
+ )
176
174
 
177
175
  return Expression(
178
176
  " and ".join([f"{_name(k)} {_op(k)} {expression_wrapper(v, k)}" for k, v in kwargs.items() if v is not None])
@@ -277,8 +275,7 @@ def columns(x, default=None, fn=None):
277
275
 
278
276
  if fn:
279
277
  return Expression(",".join(f"{fn}({str(column(c, c))}) as {c}" for c in _columns))
280
- else:
281
- return Expression(",".join(str(column(c, c)) for c in _columns))
278
+ return Expression(",".join(str(column(c, c)) for c in _columns))
282
279
 
283
280
 
284
281
  def column(x, default=None):
@@ -368,11 +365,10 @@ def boolean(x, default=None):
368
365
  if default is None:
369
366
  return 0
370
367
  return boolean(default)
371
- elif isinstance(x, Placeholder):
368
+ if isinstance(x, Placeholder):
372
369
  return boolean(default)
373
- elif isinstance(x, str):
374
- if x == "0" or x.lower() == "false":
375
- return 0
370
+ if isinstance(x, str) and (x == "0" or x.lower() == "false"):
371
+ return 0
376
372
 
377
373
  return int(bool(x))
378
374
 
@@ -403,8 +399,7 @@ def array_type(types):
403
399
  raise SQLTemplateException(
404
400
  REQUIRED_PARAM_NOT_DEFINED, documentation="/cli/advanced-templates.html"
405
401
  )
406
- else:
407
- return None
402
+ return None
408
403
  values = []
409
404
  list_values = x if type(x) == list else x.split(",") # noqa: E721
410
405
  for i, t in enumerate(list_values):
@@ -744,12 +739,11 @@ def __date_diff(
744
739
 
745
740
  if unit == "days":
746
741
  return int(diff / 86400)
747
- elif unit == "hours":
742
+ if unit == "hours":
748
743
  return int(diff / 3600)
749
- elif unit == "minutes":
744
+ if unit == "minutes":
750
745
  return int(diff / 60)
751
- else:
752
- return int(diff)
746
+ return int(diff)
753
747
  except Exception:
754
748
  if none_if_error:
755
749
  return None
@@ -1348,9 +1342,9 @@ def escape_single_quote_str(s):
1348
1342
  def expression_wrapper(x, name, escape_arrays: bool = False):
1349
1343
  if type(x) in (unicode_type, bytes, str):
1350
1344
  return "'" + sqlescape_for_string_expression(x) + "'"
1351
- elif isinstance(x, Placeholder):
1345
+ if isinstance(x, Placeholder):
1352
1346
  return "'__no_value__'"
1353
- elif isinstance(x, Comment):
1347
+ if isinstance(x, Comment):
1354
1348
  return "-- {x} \n"
1355
1349
  if x is None:
1356
1350
  truncated_name = name[:20] + "..." if len(name) > 20 else name
@@ -1361,10 +1355,7 @@ def expression_wrapper(x, name, escape_arrays: bool = False):
1361
1355
  logging.warning(f"expression_wrapper -> list :{x}:")
1362
1356
 
1363
1357
  try:
1364
- result = (
1365
- f"[{','.join(escape_single_quote_str(item) if isinstance(item, str) else str(item) for item in x)}]"
1366
- )
1367
- return result
1358
+ return f"[{','.join(escape_single_quote_str(item) if isinstance(item, str) else str(item) for item in x)}]"
1368
1359
  except Exception as e:
1369
1360
  logging.error(f"Error escaping array: {e}")
1370
1361
  return x
@@ -1425,20 +1416,18 @@ def generate(self, **kwargs) -> Tuple[str, TemplateExecutionResults]:
1425
1416
  # secret available: Always use workspace secret regardless of test mode
1426
1417
  template_execution_results.add_ch_param(x)
1427
1418
  return Symbol("{" + sqlescape(x) + ": String}")
1428
- else:
1429
- # secret not available: Check test mode and defaults
1430
- is_test_mode = TB_SECRET_IN_TEST_MODE in template_execution_results
1431
- if default is not None:
1432
- # Use provided default value
1433
- return default
1434
- elif is_test_mode:
1435
- # In test mode without default - return placeholder
1436
- return Symbol("{" + sqlescape(x) + ": String}")
1437
- else:
1438
- # Not in test mode, no secret, no default - raise error
1439
- raise SQLTemplateException(
1440
- f"Cannot access secret '{x}'. Check the secret exists in the Workspace and the token has the required scope."
1441
- )
1419
+ # secret not available: Check test mode and defaults
1420
+ is_test_mode = TB_SECRET_IN_TEST_MODE in template_execution_results
1421
+ if default is not None:
1422
+ # Use provided default value
1423
+ return default
1424
+ if is_test_mode:
1425
+ # In test mode without default - return placeholder
1426
+ return Symbol("{" + sqlescape(x) + ": String}")
1427
+ # Not in test mode, no secret, no default - raise error
1428
+ raise SQLTemplateException(
1429
+ f"Cannot access secret '{x}'. Check the secret exists in the Workspace and the token has the required scope."
1430
+ )
1442
1431
  except Exception:
1443
1432
  raise SQLTemplateException(
1444
1433
  f"Cannot access secret '{x}'. Check the secret exists in the Workspace and the token has the required scope."
@@ -1522,14 +1511,12 @@ def generate(self, **kwargs) -> Tuple[str, TemplateExecutionResults]:
1522
1511
 
1523
1512
  if line:
1524
1513
  raise SQLTemplateException(f"{message.strip()} line {line[0]}")
1525
- else:
1526
- raise SQLTemplateException(f"{message.strip()}")
1514
+ raise SQLTemplateException(f"{message.strip()}")
1527
1515
  except Exception as e:
1528
1516
  if isinstance(e, SQLTemplateException):
1529
1517
  raise e
1530
- else:
1531
- logging.exception(f"Error on unbound local error: {e}")
1532
- raise ValueError(str(e))
1518
+ logging.exception(f"Error on unbound local error: {e}")
1519
+ raise ValueError(str(e))
1533
1520
  except TypeError as e:
1534
1521
  error = str(e)
1535
1522
  if "not supported between instances of 'Placeholder' and " in str(e):
@@ -1960,29 +1947,27 @@ def get_var_data(content, node_id=None):
1960
1947
  # ast.Constant nodes, so .value holds str/bytes/int/float/bool/None.
1961
1948
  if type(x) == ast.Constant: # noqa: E721
1962
1949
  return x.value
1963
- elif type(x) == ast.Name: # noqa: E721
1950
+ if type(x) == ast.Name: # noqa: E721
1964
1951
  return x.id
1965
- elif type(x) == ast.List: # noqa: E721
1952
+ if type(x) == ast.List: # noqa: E721
1966
1953
  # List can hold different types
1967
1954
  return _get_list_var_data(x)
1968
- elif type(x) == ast.BinOp: # noqa: E721
1955
+ if type(x) == ast.BinOp: # noqa: E721
1969
1956
  # in this case there could be several variables
1970
1957
  # if that's the case the left one is the main
1971
1958
  r = node_to_value(x.left)
1972
1959
  if not r:
1973
1960
  r = node_to_value(x.right)
1974
1961
  return r
1975
- elif type(x) == ast.UnaryOp and type(x.operand) == ast.Constant: # noqa: E721
1962
+ if type(x) == ast.UnaryOp and type(x.operand) == ast.Constant: # noqa: E721
1976
1963
  if type(x.op) == ast.USub: # noqa: E721
1977
1964
  return x.operand.value * -1
1978
- else:
1979
- return x.operand.value
1980
- else:
1981
- try:
1982
- return x.id
1983
- except Exception:
1984
- # don't let this ruin the parsing
1985
- pass
1965
+ return x.operand.value
1966
+ try:
1967
+ return x.id
1968
+ except Exception:
1969
+ # don't let this ruin the parsing
1970
+ pass
1986
1971
  return None
1987
1972
 
1988
1973
  def _get_list_var_data(x):
@@ -1994,7 +1979,7 @@ def get_var_data(content, node_id=None):
1994
1979
  # ast.Bytes/ast.Str/ast.Num/ast.NameConstant (removed in 3.14).
1995
1980
  if type(first_elem) == ast.Constant: # noqa: E721
1996
1981
  return [elem.value for elem in x.elts]
1997
- elif type(first_elem) == ast.Name: # noqa: E721
1982
+ if type(first_elem) == ast.Name: # noqa: E721
1998
1983
  return [elem.id for elem in x.elts]
1999
1984
 
2000
1985
  return []
@@ -2013,8 +1998,7 @@ def get_var_data(content, node_id=None):
2013
1998
  # to just `ast.parse(content)` once we confirm all environments use patched Python versions.
2014
1999
  def parse_content(content, retries=0):
2015
2000
  try:
2016
- parsed = ast.parse(content)
2017
- return parsed
2001
+ return ast.parse(content)
2018
2002
  except Exception as e:
2019
2003
  if "AST constructor recursion depth mismatch" not in str(e):
2020
2004
  raise e
@@ -2042,8 +2026,7 @@ def get_var_data(content, node_id=None):
2042
2026
  if type(x) == ast.Call: # noqa: E721
2043
2027
  # Nested calls are traversed via `ast.iter_child_nodes` below.
2044
2028
  continue
2045
- else:
2046
- args.append(node_to_value(x))
2029
+ args.append(node_to_value(x))
2047
2030
 
2048
2031
  kwargs = {}
2049
2032
  for x in node.keywords:
@@ -2531,8 +2514,7 @@ def format_SQLTemplateException_message(e: SQLTemplateException, vars_and_types:
2531
2514
  f"{REQUIRED_PARAM_NOT_DEFINED}. Check the parameters {join_with_different_last_separator(vars_with_default_none)}. Please provide a value or set a default value in the pipe code.",
2532
2515
  e.documentation,
2533
2516
  )
2534
- else:
2535
- raise e
2517
+ raise e
2536
2518
 
2537
2519
 
2538
2520
  def render_sql_template(
@@ -2913,12 +2895,12 @@ def render_sql_template(
2913
2895
  # https://gitlab.com/tinybird/analytics/-/issues/943
2914
2896
  if "length" in v and not v["length"]:
2915
2897
  raise SQLTemplateException("length cannot be used as a variable name or as a function inside of a template")
2916
- elif "missing 1 required positional argument" in str(e):
2898
+ if "missing 1 required positional argument" in str(e):
2917
2899
  raise SQLTemplateException(
2918
2900
  "one of the transform type functions is missing an argument",
2919
2901
  documentation="/cli/advanced-templates.html#transform-types-functions",
2920
2902
  )
2921
- elif "not callable" in str(e) or "unhashable type" in str(e):
2903
+ if "not callable" in str(e) or "unhashable type" in str(e):
2922
2904
  raise SQLTemplateException(
2923
2905
  "wrong syntax, you might be using a not valid function inside a control block",
2924
2906
  documentation="/cli/advanced-templates.html",
@@ -3063,7 +3045,7 @@ def render_template_with_secrets(
3063
3045
  )
3064
3046
  return '""'
3065
3047
  return value
3066
- elif default is not None:
3048
+ if default is not None:
3067
3049
  if isinstance(default, str) and len(default) == 0:
3068
3050
  if empty_secret_raises:
3069
3051
  raise SQLTemplateException(
@@ -3071,10 +3053,9 @@ def render_template_with_secrets(
3071
3053
  )
3072
3054
  return '""'
3073
3055
  return default
3074
- else:
3075
- raise SQLTemplateException(
3076
- f"Cannot access secret '{secret_name}'. Check the secret exists in the Workspace and the token has the required scope."
3077
- )
3056
+ raise SQLTemplateException(
3057
+ f"Cannot access secret '{secret_name}'. Check the secret exists in the Workspace and the token has the required scope."
3058
+ )
3078
3059
 
3079
3060
  # Create the template
3080
3061
  t = Template(content, name=name, autoescape=None)
@@ -33,8 +33,7 @@ class TBLine(Line):
33
33
  Tinybird => This is overriden from the base Line because we want SQL inside a template to be indented
34
34
  https://github.com/tconbeer/sqlfmt/blob/c11775b92d8a45f0e91d871b81a88a894d620bec/src/sqlfmt/line.py#L92
35
35
  """
36
- prefix = INDENT * (self.depth[0] + self.depth[1])
37
- return prefix
36
+ return INDENT * (self.depth[0] + self.depth[1])
38
37
 
39
38
 
40
39
  def from_nodes(
@@ -106,8 +105,7 @@ def _format_jinja_node(self, node: Node, max_length: int) -> bool:
106
105
 
107
106
  return tag.is_blackened
108
107
 
109
- else:
110
- return False
108
+ return False
111
109
 
112
110
 
113
111
  # Some monkey patching
@@ -158,8 +156,7 @@ class TinybirdNodeManager(NodeManager):
158
156
  TokenType.SET_OPERATOR,
159
157
  ):
160
158
  return " ".join(token.token.split())
161
- else:
162
- return token.token
159
+ return token.token
163
160
 
164
161
 
165
162
  class TinybirdDialect(ClickHouse):
@@ -230,12 +227,11 @@ class TinybirdDialect(ClickHouse):
230
227
  Creates and returns an analyzer that uses the Dialect's rules for lexing
231
228
  Custom NodeManager for Tinybird
232
229
  """
233
- analyzer = Analyzer(
230
+ return Analyzer(
234
231
  line_length=line_length,
235
232
  rules=self.get_rules(),
236
233
  node_manager=TinybirdNodeManager(self.case_sensitive_names, self.lower_keywords),
237
234
  )
238
- return analyzer
239
235
 
240
236
 
241
237
  @dataclass
@@ -253,11 +249,10 @@ class TBMode(Mode):
253
249
  def _calc_str(self) -> str:
254
250
  if self.is_multiline:
255
251
  return self.token.token + "\n"
256
- else:
257
- marker, comment_text = self._comment_parts()
258
- if comment_text == "":
259
- return marker + "\n"
260
- return marker + " " + comment_text + "\n"
252
+ marker, comment_text = self._comment_parts()
253
+ if comment_text == "":
254
+ return marker + "\n"
255
+ return marker + " " + comment_text + "\n"
261
256
 
262
257
 
263
258
  Comment._calc_str = property(_calc_str)
@@ -314,9 +314,9 @@ def _sql_get_used_tables_impl(
314
314
  msg = str(e)
315
315
  if "is restricted. Contact support@tinybird.co" in msg:
316
316
  raise InvalidFunction(msg=msg) from e
317
- elif "Unknown function tb_secret" in msg:
317
+ if "Unknown function tb_secret" in msg:
318
318
  raise InvalidFunction(msg="Unknown function tb_secret. Usage: {{tb_secret('secret_name')}}") from e
319
- elif "Unknown function tb_var" in msg:
319
+ if "Unknown function tb_var" in msg:
320
320
  raise InvalidFunction(msg="Unknown function tb_var. Usage: {{tb_var('var_name')}}") from e
321
321
  raise
322
322
  # Do not cache this fallback result: the returned sql string can contain
@@ -660,9 +660,7 @@ def replace_tables(
660
660
 
661
661
  # Fix for empty database names in JOINs - remove empty backticks like ``.table_name
662
662
  # that are generated when chquery.replace_tables processes tuples with empty database names
663
- sql = sql.replace("``.", "")
664
-
665
- return sql
663
+ return sql.replace("``.", "")
666
664
 
667
665
 
668
666
  def is_invalid_resource(
@@ -142,8 +142,7 @@ class Local:
142
142
  storage = self._get_storage()
143
143
  if key in storage:
144
144
  return storage[key]
145
- else:
146
- raise AttributeError(f"{self!r} object has no attribute {key!r}")
145
+ raise AttributeError(f"{self!r} object has no attribute {key!r}")
147
146
 
148
147
  def __setattr__(self, key, value):
149
148
  if key in ("_context_refs", "_thread_critical", "_thread_lock", "_attr_name"):
@@ -151,6 +150,7 @@ class Local:
151
150
  with self._thread_lock:
152
151
  storage = self._get_storage()
153
152
  storage[key] = value
153
+ return None
154
154
 
155
155
  def __delattr__(self, key):
156
156
  with self._thread_lock:
@@ -252,13 +252,12 @@ def _iscoroutinefunction_or_partial(func: Any) -> bool:
252
252
  # this to exist. Code taken from CPython.
253
253
  if sys.version_info >= (3, 8):
254
254
  return asyncio.iscoroutinefunction(func)
255
- else:
256
- while inspect.ismethod(func):
257
- func = func.__func__
258
- while isinstance(func, functools.partial):
259
- func = func.func
255
+ while inspect.ismethod(func):
256
+ func = func.__func__
257
+ while isinstance(func, functools.partial):
258
+ func = func.func
260
259
 
261
- return asyncio.iscoroutinefunction(func)
260
+ return asyncio.iscoroutinefunction(func)
262
261
 
263
262
 
264
263
  class ThreadSensitiveContext:
@@ -660,9 +659,8 @@ class SyncToAsync:
660
659
  if hasattr(asyncio, "current_task"):
661
660
  # Python 3.7 and up
662
661
  return asyncio.current_task()
663
- else:
664
- # Python 3.6
665
- return asyncio.Task.current_task()
662
+ # Python 3.6
663
+ return asyncio.Task.current_task()
666
664
  except RuntimeError:
667
665
  return None
668
666
 
@@ -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.6.19.dev0'
8
- __revision__ = '92350bf'
7
+ __version__ = '4.6.20.dev0'
8
+ __revision__ = 'fee8cae'
@@ -272,14 +272,13 @@ class TinyB:
272
272
 
273
273
  def alter_tokens(self, name: str, scopes: List[str]):
274
274
  if not scopes:
275
- return
275
+ return None
276
276
  scopes_url: str = "&".join([f"scope={scope}" for scope in scopes])
277
277
  url = f"/v0/tokens/{name}"
278
278
  if len(url + "?" + scopes_url) > TinyB.MAX_GET_LENGTH:
279
279
  return self._req(url, method="PUT", data=scopes_url)
280
- else:
281
- url = url + "?" + scopes_url
282
- return self._req(url, method="PUT", data="")
280
+ url = url + "?" + scopes_url
281
+ return self._req(url, method="PUT", data="")
283
282
 
284
283
  def datasources(self, branch: Optional[str] = None, attrs: Optional[str] = None) -> List[Dict[str, Any]]:
285
284
  params = {}
@@ -300,16 +299,13 @@ class TinyB:
300
299
  return self._req(f"/v0/variables/{name}")
301
300
 
302
301
  def create_secret(self, name: str, value: str):
303
- response = self._req("/v0/variables", method="POST", data={"name": name, "value": value})
304
- return response
302
+ return self._req("/v0/variables", method="POST", data={"name": name, "value": value})
305
303
 
306
304
  def update_secret(self, name: str, value: str):
307
- response = self._req(f"/v0/variables/{name}", method="PUT", data={"value": value})
308
- return response
305
+ return self._req(f"/v0/variables/{name}", method="PUT", data={"value": value})
309
306
 
310
307
  def delete_secret(self, name: str):
311
- response = self._req(f"/v0/variables/{name}", method="DELETE")
312
- return response
308
+ return self._req(f"/v0/variables/{name}", method="DELETE")
313
309
 
314
310
  def get_connections(self, service: Optional[str] = None):
315
311
  params = {}
@@ -537,7 +533,7 @@ class TinyB:
537
533
  if projection_mode:
538
534
  params["projection_mode"] = projection_mode
539
535
  return self._req(f"/v1/datasources/{datasource_name}/delete", method="POST", data=params)
540
- elif dry_run:
536
+ if dry_run:
541
537
  params["dry_run"] = "true"
542
538
  return self._req(f"/v0/datasources/{datasource_name}/delete", method="POST", data=params)
543
539
 
@@ -635,8 +631,7 @@ class TinyB:
635
631
  node_name = node["params"]["name"] if node.get("params", None) else node["name"]
636
632
  if datasource_name:
637
633
  params["datasource"] = datasource_name
638
- response = self._req(f"/v0/pipes/{pipe_name}/nodes/{node_name}/analysis?{urlencode(params)}")
639
- return response
634
+ return self._req(f"/v0/pipes/{pipe_name}/nodes/{node_name}/analysis?{urlencode(params)}")
640
635
 
641
636
  def populate_node(
642
637
  self,
@@ -654,8 +649,7 @@ class TinyB:
654
649
  params.update({"populate_condition": populate_condition})
655
650
  if on_demand_compute:
656
651
  params.update({"on_demand_compute": "true"})
657
- response = self._req(f"/v0/pipes/{pipe_name}/nodes/{node_name}/population?{urlencode(params)}", method="POST")
658
- return response
652
+ return self._req(f"/v0/pipes/{pipe_name}/nodes/{node_name}/population?{urlencode(params)}", method="POST")
659
653
 
660
654
  def pipes(self, branch=None, dependencies: bool = False, node_attrs=None, attrs=None) -> List[Dict[str, Any]]:
661
655
  params = {
@@ -687,9 +681,8 @@ class TinyB:
687
681
  query_string = urlencode(params)
688
682
  if len(url + "?" + query_string) > TinyB.MAX_GET_LENGTH:
689
683
  return self._req(f"/v0/pipes/{pipe_name_or_uid}.{format}", method="POST", data=params)
690
- else:
691
- url = url + "?" + query_string
692
- return self._req(url)
684
+ url = url + "?" + query_string
685
+ return self._req(url)
693
686
 
694
687
  def pipe_create(self, pipe_name: str, sql: str):
695
688
  return self._req(f"/v0/pipes?name={pipe_name}&sql={quote(sql, safe='')}", method="POST", data=sql.encode())
@@ -777,8 +770,7 @@ class TinyB:
777
770
 
778
771
  if len(sql) > TinyB.MAX_GET_LENGTH:
779
772
  return self._req(f"/v0/sql?{urlencode(params)}", data=sql, method="POST")
780
- else:
781
- return self._req(f"/v0/sql?q={quote(sql, safe='')}&{urlencode(params)}")
773
+ return self._req(f"/v0/sql?q={quote(sql, safe='')}&{urlencode(params)}")
782
774
 
783
775
  def jobs(
784
776
  self, status: Optional[Tuple[str, ...]] = None, kind: Optional[Tuple[str, ...]] = None
@@ -1006,7 +998,7 @@ class TinyB:
1006
998
 
1007
999
  def add_workspaces_to_organization(self, organization_id: str, workspace_ids: List[str]):
1008
1000
  if not workspace_ids:
1009
- return
1001
+ return None
1010
1002
  return self._req(
1011
1003
  f"/v0/organizations/{organization_id}/workspaces",
1012
1004
  method="PUT",
@@ -1344,8 +1336,7 @@ class TinyB:
1344
1336
  return next((connector for connector in result["connectors"] if connector_equals(connector, kwargs)), None)
1345
1337
 
1346
1338
  def regions(self):
1347
- regions = self._req("/v0/regions")
1348
- return regions
1339
+ return self._req("/v0/regions")
1349
1340
 
1350
1341
  def datasource_query_copy(self, datasource_name: str, sql_query: str):
1351
1342
  params = {"copy_to": datasource_name}
@@ -182,6 +182,7 @@ class CICDGeneratorBase:
182
182
  return FeedbackManager.warning_for_cicd_file(
183
183
  file_name=cicd_file.file_name, warning_message=cicd_file.warning_message.format(**params)
184
184
  )
185
+ return None
185
186
 
186
187
  def is_already_generated(self, path: str) -> bool:
187
188
  for cicd_file in self.cicd_files:
@@ -25,7 +25,6 @@ from tinybird.iterating.data_branch_modes import DataBranchMode
25
25
  from tinybird.tb import __cli__
26
26
  from tinybird.tb.check_pypi import CheckPypi
27
27
  from tinybird.tb.client import (
28
- AuthException,
29
28
  AuthNoTokenException,
30
29
  TinyB,
31
30
  )
@@ -734,12 +733,15 @@ def cli(
734
733
  # Keep config path pointing to the user project root even when using a virtual SDK project.
735
734
  config["path"] = folder if sdk_virtual_project else str(project.path)
736
735
  # If they have passed a token or host as parameter and it's different that record in .tinyb, refresh the workspace id
736
+ # Best-effort only: e.g. a still-Classic workspace makes this v1 (Forward-only)
737
+ # call fail with a non-auth error, and that must not block the CLI from
738
+ # starting up (commands like `migrate-to-forward` are meant to run on it).
737
739
  if token or host:
738
740
  try:
739
741
  workspace = client.workspace_info(version="v1")
740
742
  config["id"] = workspace.get("id", "")
741
743
  config["name"] = workspace.get("name", "")
742
- except (AuthNoTokenException, AuthException):
744
+ except Exception:
743
745
  pass
744
746
 
745
747
  ctx.ensure_object(dict)["config"] = config
@@ -223,6 +223,7 @@ class AliasedGroup(click.Group):
223
223
  cm = click.Group.get_command(self, ctx, cmd_name)
224
224
  if cm is not None:
225
225
  return cm
226
+ return None
226
227
 
227
228
  def resolve_command(self, ctx, args):
228
229
  # always return the command's name, not the alias
@@ -449,7 +450,7 @@ def analyze_file(filename: str, client: TinyB, format: str):
449
450
  meta, data = _analyze(filename, client, format)
450
451
  schema = meta["analysis"]["schema"]
451
452
  schema = schema.replace(", ", ",\n ")
452
- content = f"""DESCRIPTION >
453
+ return f"""DESCRIPTION >
453
454
  Generated from {filename}
454
455
 
455
456
  SCHEMA >
@@ -459,7 +460,6 @@ ENGINE "MergeTree"
459
460
  # ENGINE_SORTING_KEY "user_id, timestamp"
460
461
  # ENGINE_TTL "timestamp + toIntervalDay(60)"
461
462
  # Learn more at https://www.tinybird.co/docs/forward/dev-reference/datafiles/datasource-files"""
462
- return content
463
463
 
464
464
 
465
465
  def _generate_datafile(
@@ -516,6 +516,7 @@ def ask_for_region_interactively(regions):
516
516
  available_options = ", ".join(map(str, range(1, len(regions) + 1)))
517
517
  click.echo(FeedbackManager.error_region_index(host_index=region_index, available_options=available_options))
518
518
  region_index = -1
519
+ return None
519
520
 
520
521
 
521
522
  def get_region_info(ctx, region=None):
@@ -711,10 +712,8 @@ def print_branch_regression_tests_summary(client, job_id, host, response=None):
711
712
  if isinstance(metric, float):
712
713
  if is_percentage:
713
714
  return f"{round(metric, 3):+} %"
714
- else:
715
- return f"{round(metric, 3)} seconds"
716
- else:
717
- return metric
715
+ return f"{round(metric, 3)} seconds"
716
+ return metric
718
717
 
719
718
  failed = False
720
719
  response = client.job(job_id) if job_id else response or {"progress": []}
@@ -2087,9 +2086,7 @@ def get_gcs_svc_account_creds() -> str:
2087
2086
  )
2088
2087
 
2089
2088
  assert isinstance(creds, str)
2090
- creds_without_new_lines = creds.replace("\n", "")
2091
-
2092
- return creds_without_new_lines
2089
+ return creds.replace("\n", "")
2093
2090
 
2094
2091
 
2095
2092
  class DataConnectorType(str, Enum):
@@ -2478,9 +2475,8 @@ def create_workspace_branch(
2478
2475
  job_response = wait_job(config.get_client(), job_id, job_url, "Environment creation")
2479
2476
  if job_response is None:
2480
2477
  raise CLIException(f"Empty job API response (job_id: {job_id}, job_url: {job_url})")
2481
- else:
2482
- response = job_response.get("result", {})
2483
- is_summary = "partitions" in response
2478
+ response = job_response.get("result", {})
2479
+ is_summary = "partitions" in response
2484
2480
 
2485
2481
  except Exception as e:
2486
2482
  raise CLIException(FeedbackManager.error_exception(error=str(e)))