tinybird-cli 5.13.1.dev0__tar.gz → 5.13.1.dev2__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 (48) hide show
  1. {tinybird-cli-5.13.1.dev0 → tinybird-cli-5.13.1.dev2}/PKG-INFO +12 -2
  2. {tinybird-cli-5.13.1.dev0 → tinybird-cli-5.13.1.dev2}/tinybird/__cli__.py +2 -2
  3. {tinybird-cli-5.13.1.dev0 → tinybird-cli-5.13.1.dev2}/tinybird/client.py +1 -1
  4. {tinybird-cli-5.13.1.dev0 → tinybird-cli-5.13.1.dev2}/tinybird/datafile.py +16 -9
  5. {tinybird-cli-5.13.1.dev0 → tinybird-cli-5.13.1.dev2}/tinybird/datatypes.py +1 -1
  6. {tinybird-cli-5.13.1.dev0 → tinybird-cli-5.13.1.dev2}/tinybird/sql.py +3 -13
  7. {tinybird-cli-5.13.1.dev0 → tinybird-cli-5.13.1.dev2}/tinybird/sql_template.py +47 -4
  8. {tinybird-cli-5.13.1.dev0 → tinybird-cli-5.13.1.dev2}/tinybird/tb_cli_modules/cli.py +1 -1
  9. {tinybird-cli-5.13.1.dev0 → tinybird-cli-5.13.1.dev2}/tinybird/tornado_template.py +12 -20
  10. {tinybird-cli-5.13.1.dev0 → tinybird-cli-5.13.1.dev2}/tinybird_cli.egg-info/PKG-INFO +12 -2
  11. {tinybird-cli-5.13.1.dev0 → tinybird-cli-5.13.1.dev2}/setup.cfg +0 -0
  12. {tinybird-cli-5.13.1.dev0 → tinybird-cli-5.13.1.dev2}/tinybird/ch_utils/constants.py +0 -0
  13. {tinybird-cli-5.13.1.dev0 → tinybird-cli-5.13.1.dev2}/tinybird/ch_utils/engine.py +0 -0
  14. {tinybird-cli-5.13.1.dev0 → tinybird-cli-5.13.1.dev2}/tinybird/check_pypi.py +0 -0
  15. {tinybird-cli-5.13.1.dev0 → tinybird-cli-5.13.1.dev2}/tinybird/config.py +0 -0
  16. {tinybird-cli-5.13.1.dev0 → tinybird-cli-5.13.1.dev2}/tinybird/connectors.py +0 -0
  17. {tinybird-cli-5.13.1.dev0 → tinybird-cli-5.13.1.dev2}/tinybird/context.py +0 -0
  18. {tinybird-cli-5.13.1.dev0 → tinybird-cli-5.13.1.dev2}/tinybird/feedback_manager.py +0 -0
  19. {tinybird-cli-5.13.1.dev0 → tinybird-cli-5.13.1.dev2}/tinybird/git_settings.py +0 -0
  20. {tinybird-cli-5.13.1.dev0 → tinybird-cli-5.13.1.dev2}/tinybird/sql_template_fmt.py +0 -0
  21. {tinybird-cli-5.13.1.dev0 → tinybird-cli-5.13.1.dev2}/tinybird/sql_toolset.py +0 -0
  22. {tinybird-cli-5.13.1.dev0 → tinybird-cli-5.13.1.dev2}/tinybird/syncasync.py +0 -0
  23. {tinybird-cli-5.13.1.dev0 → tinybird-cli-5.13.1.dev2}/tinybird/tb_cli.py +0 -0
  24. {tinybird-cli-5.13.1.dev0 → tinybird-cli-5.13.1.dev2}/tinybird/tb_cli_modules/auth.py +0 -0
  25. {tinybird-cli-5.13.1.dev0 → tinybird-cli-5.13.1.dev2}/tinybird/tb_cli_modules/branch.py +0 -0
  26. {tinybird-cli-5.13.1.dev0 → tinybird-cli-5.13.1.dev2}/tinybird/tb_cli_modules/cicd.py +0 -0
  27. {tinybird-cli-5.13.1.dev0 → tinybird-cli-5.13.1.dev2}/tinybird/tb_cli_modules/common.py +0 -0
  28. {tinybird-cli-5.13.1.dev0 → tinybird-cli-5.13.1.dev2}/tinybird/tb_cli_modules/config.py +0 -0
  29. {tinybird-cli-5.13.1.dev0 → tinybird-cli-5.13.1.dev2}/tinybird/tb_cli_modules/connection.py +0 -0
  30. {tinybird-cli-5.13.1.dev0 → tinybird-cli-5.13.1.dev2}/tinybird/tb_cli_modules/datasource.py +0 -0
  31. {tinybird-cli-5.13.1.dev0 → tinybird-cli-5.13.1.dev2}/tinybird/tb_cli_modules/exceptions.py +0 -0
  32. {tinybird-cli-5.13.1.dev0 → tinybird-cli-5.13.1.dev2}/tinybird/tb_cli_modules/fmt.py +0 -0
  33. {tinybird-cli-5.13.1.dev0 → tinybird-cli-5.13.1.dev2}/tinybird/tb_cli_modules/job.py +0 -0
  34. {tinybird-cli-5.13.1.dev0 → tinybird-cli-5.13.1.dev2}/tinybird/tb_cli_modules/pipe.py +0 -0
  35. {tinybird-cli-5.13.1.dev0 → tinybird-cli-5.13.1.dev2}/tinybird/tb_cli_modules/regions.py +0 -0
  36. {tinybird-cli-5.13.1.dev0 → tinybird-cli-5.13.1.dev2}/tinybird/tb_cli_modules/tag.py +0 -0
  37. {tinybird-cli-5.13.1.dev0 → tinybird-cli-5.13.1.dev2}/tinybird/tb_cli_modules/telemetry.py +0 -0
  38. {tinybird-cli-5.13.1.dev0 → tinybird-cli-5.13.1.dev2}/tinybird/tb_cli_modules/test.py +0 -0
  39. {tinybird-cli-5.13.1.dev0 → tinybird-cli-5.13.1.dev2}/tinybird/tb_cli_modules/tinyunit/tinyunit.py +0 -0
  40. {tinybird-cli-5.13.1.dev0 → tinybird-cli-5.13.1.dev2}/tinybird/tb_cli_modules/tinyunit/tinyunit_lib.py +0 -0
  41. {tinybird-cli-5.13.1.dev0 → tinybird-cli-5.13.1.dev2}/tinybird/tb_cli_modules/token.py +0 -0
  42. {tinybird-cli-5.13.1.dev0 → tinybird-cli-5.13.1.dev2}/tinybird/tb_cli_modules/workspace.py +0 -0
  43. {tinybird-cli-5.13.1.dev0 → tinybird-cli-5.13.1.dev2}/tinybird/tb_cli_modules/workspace_members.py +0 -0
  44. {tinybird-cli-5.13.1.dev0 → tinybird-cli-5.13.1.dev2}/tinybird_cli.egg-info/SOURCES.txt +0 -0
  45. {tinybird-cli-5.13.1.dev0 → tinybird-cli-5.13.1.dev2}/tinybird_cli.egg-info/dependency_links.txt +0 -0
  46. {tinybird-cli-5.13.1.dev0 → tinybird-cli-5.13.1.dev2}/tinybird_cli.egg-info/entry_points.txt +0 -0
  47. {tinybird-cli-5.13.1.dev0 → tinybird-cli-5.13.1.dev2}/tinybird_cli.egg-info/requires.txt +0 -0
  48. {tinybird-cli-5.13.1.dev0 → tinybird-cli-5.13.1.dev2}/tinybird_cli.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: tinybird-cli
3
- Version: 5.13.1.dev0
3
+ Version: 5.13.1.dev2
4
4
  Summary: Tinybird Command Line Tool
5
5
  Home-page: https://www.tinybird.co/docs/cli/introduction.html
6
6
  Author: Tinybird
@@ -18,10 +18,20 @@ The Tinybird command-line tool allows you to use all the Tinybird functionality
18
18
  Changelog
19
19
  ----------
20
20
 
21
+ 5.13.1.dev2
22
+ ***********
23
+
24
+ - `Added` support for `EXPORT_WRITE_STRATEGY` parameter for sinks, in `.pipe` files.
25
+
26
+ 5.13.1.dev1
27
+ ***********
28
+
29
+ - `Changed` environment variables are now evaluated in the SQL nodes.
30
+
21
31
  5.13.0
22
32
  ***********
23
33
 
24
- - `Addded` additional metadata to `tb branch ls` command
34
+ - `Added` additional metadata to `tb branch ls` command
25
35
  - `Changed` dependencies to be less restrictive:
26
36
  - cryptography: from ``>=41.0.0`` to ``~=41.0.0``
27
37
  - snowflake-connector-python: from ``==3.12.3`` to ``~=3.12.3``
@@ -4,5 +4,5 @@ __description__ = 'Tinybird Command Line Tool'
4
4
  __url__ = 'https://www.tinybird.co/docs/cli/introduction.html'
5
5
  __author__ = 'Tinybird'
6
6
  __author_email__ = 'support@tinybird.co'
7
- __version__ = '5.13.1.dev0'
8
- __revision__ = '086d6df'
7
+ __version__ = '5.13.1.dev2'
8
+ __revision__ = '5103f8f'
@@ -80,7 +80,7 @@ def parse_error_response(response: Response) -> str:
80
80
  return f"Server error, cannot parse response. {response.content.decode('utf-8')}"
81
81
 
82
82
 
83
- class TinyB(object):
83
+ class TinyB:
84
84
  MAX_GET_LENGTH = 4096
85
85
 
86
86
  def __init__(
@@ -212,6 +212,7 @@ class ExportReplacements:
212
212
  ("export_file_template", "file_template", None),
213
213
  ("export_format", "format", "csv"),
214
214
  ("export_compression", "compression", None),
215
+ ("export_write_strategy", "write_strategy", None),
215
216
  ("export_strategy", "strategy", "@new"),
216
217
  ("export_kafka_topic", "kafka_topic", None),
217
218
  ("kafka_connection_name", "connection", None),
@@ -1051,9 +1052,9 @@ def parse(
1051
1052
  def _f(sql: str, **kwargs: Any) -> None:
1052
1053
  if not parser_state.current_node:
1053
1054
  raise ParseException("SQL must be called after a NODE command")
1054
- parser_state.current_node[var_name] = (
1055
- textwrap.dedent(sql).rstrip() if "%" not in sql.strip()[0] else sql.strip()
1056
- )
1055
+ _sql = textwrap.dedent(sql).rstrip() if "%" not in sql.strip()[0] else sql.strip()
1056
+ _sql = eval_var(_sql)
1057
+ parser_state.current_node[var_name] = _sql
1057
1058
 
1058
1059
  # HACK this cast is needed because Mypy
1059
1060
  return cast(Callable[[str, KwArg(Any)], None], _f)
@@ -1239,6 +1240,7 @@ def parse(
1239
1240
  "export_format": assign_var("export_format"),
1240
1241
  "export_strategy": assign_var("export_strategy"),
1241
1242
  "export_compression": assign_var("export_compression"),
1243
+ "export_write_strategy": assign_var("export_write_strategy"),
1242
1244
  "export_kafka_topic": assign_var("export_kafka_topic"),
1243
1245
  }
1244
1246
 
@@ -2255,16 +2257,16 @@ class PipeCheckerRunner:
2255
2257
  class PipeCheckerTextTestResult(unittest.TextTestResult):
2256
2258
  def __init__(self, *args: Any, **kwargs: Any) -> None:
2257
2259
  self.custom_output = kwargs.pop("custom_output", False)
2258
- super(PipeCheckerTextTestResult, self).__init__(*args, **kwargs)
2260
+ super().__init__(*args, **kwargs)
2259
2261
  self.success: List[PipeChecker] = []
2260
2262
 
2261
2263
  def addSuccess(self, test: PipeChecker): # type: ignore
2262
- super(PipeCheckerTextTestResult, self).addSuccess(test)
2264
+ super().addSuccess(test)
2263
2265
  self.success.append(test)
2264
2266
 
2265
2267
  def startTest(self, test):
2266
2268
  if not self.custom_output:
2267
- super(PipeCheckerTextTestResult, self).startTest(test)
2269
+ super().startTest(test)
2268
2270
  else:
2269
2271
  super(unittest.TextTestResult, self).startTest(test)
2270
2272
 
@@ -3352,7 +3354,12 @@ async def new_ds(
3352
3354
  existing.sort(key=lambda x: x["name"])
3353
3355
  if len(existing) != len(new) or any([(d, d2) for d, d2 in zip(new, existing) if d != d2]):
3354
3356
  new_indices = ds.get("params", {}).get("indexes") or "0"
3355
- if new_description or new_schema or new_ttl or (new_indices is not None) and (not fork_downstream or not fork):
3357
+ if (
3358
+ new_description
3359
+ or new_schema
3360
+ or new_ttl
3361
+ or ((new_indices is not None) and (not fork_downstream or not fork))
3362
+ ):
3356
3363
  alter_response = await client.alter_datasource(
3357
3364
  ds_name,
3358
3365
  new_schema=new_schema,
@@ -4866,7 +4873,7 @@ async def format_datasource(
4866
4873
  format_tags(file_parts, doc)
4867
4874
  format_schema(file_parts, doc.nodes[0])
4868
4875
  format_indices(file_parts, doc.nodes[0])
4869
- await format_engine(file_parts, doc.nodes[0], only_ttl=True if not for_deploy_diff else False, client=client)
4876
+ await format_engine(file_parts, doc.nodes[0], only_ttl=bool(not for_deploy_diff), client=client)
4870
4877
  if for_deploy_diff:
4871
4878
  format_import_settings(file_parts, doc.nodes[0])
4872
4879
  format_shared_with(file_parts, doc)
@@ -5136,7 +5143,7 @@ async def format_pipe(
5136
5143
  doc.includes,
5137
5144
  line_length=line_length,
5138
5145
  unroll_includes=unroll_includes,
5139
- lower_keywords=True if for_deploy_diff else False,
5146
+ lower_keywords=bool(for_deploy_diff),
5140
5147
  )
5141
5148
 
5142
5149
  if not unroll_includes:
@@ -429,6 +429,6 @@ def get_decimal_limits(p, s):
429
429
  (Decimal('-99999999999999999999999999999999999999.99999999999999999999999999999999999999'), Decimal('99999999999999999999999999999999999999.99999999999999999999999999999999999999'))
430
430
  """
431
431
  with decimal.localcontext(prec=p + 2):
432
- max_value = Decimal(((10**p) - 1)) / (10**s)
432
+ max_value = Decimal((10**p) - 1) / (10**s)
433
433
  min_value = -max_value
434
434
  return min_value, max_value
@@ -605,11 +605,7 @@ def _parse_table_structure(schema: str) -> List[Dict[str, Any]]:
605
605
  context = context_stack[-1]
606
606
  c = schema[i]
607
607
 
608
- if context == "'" and c == "'":
609
- context_stack.pop()
610
- elif context == '"' and c == '"':
611
- context_stack.pop()
612
- elif context == "(" and c == ")":
608
+ if (context == "'" and c == "'") or (context == '"' and c == '"') or (context == "(" and c == ")"):
613
609
  context_stack.pop()
614
610
  elif c == "'" and (context is None or context == "("):
615
611
  context_stack.append("'")
@@ -619,11 +615,7 @@ def _parse_table_structure(schema: str) -> List[Dict[str, Any]]:
619
615
  context_stack.append("(")
620
616
  elif context is None and lookahead_matches(lookup):
621
617
  return schema[begin:i].strip(" \t\r\n")
622
- elif context is None and c not in valid_chars_fn:
623
- raise ValueError(
624
- format_parse_error(schema, i, pos, "wrong value, please check the schema syntax", line=line)
625
- )
626
- elif context == "(" and c not in valid_chars_fn:
618
+ elif (context is None and c not in valid_chars_fn) or (context == "(" and c not in valid_chars_fn):
627
619
  raise ValueError(
628
620
  format_parse_error(schema, i, pos, "wrong value, please check the schema syntax", line=line)
629
621
  )
@@ -738,9 +730,7 @@ def _parse_table_structure(schema: str) -> List[Dict[str, Any]]:
738
730
  advance(",")
739
731
  valid_next = []
740
732
  add_column("COMMA")
741
- elif found == NEW_LINE:
742
- i += 1
743
- elif name == "INDEX" and not found:
733
+ elif found == NEW_LINE or (name == "INDEX" and not found):
744
734
  i += 1
745
735
  else:
746
736
  raise ValueError(
@@ -7,7 +7,7 @@ from datetime import datetime
7
7
  from functools import lru_cache
8
8
  from io import StringIO
9
9
  from json import loads
10
- from typing import Any, List, Optional, Tuple, Union
10
+ from typing import Any, Dict, List, Optional, Tuple, Union
11
11
 
12
12
  from tornado import escape
13
13
  from tornado.util import ObjectDict, exec_in, unicode_type
@@ -33,7 +33,7 @@ def is_secret_template_key(key: str) -> bool:
33
33
 
34
34
  class TemplateExecutionResults(dict):
35
35
  def __init__(self, *args, **kwargs):
36
- super(TemplateExecutionResults, self).__init__(*args, **kwargs)
36
+ super().__init__(*args, **kwargs)
37
37
  self.template_params = set()
38
38
  self.ch_params = set()
39
39
 
@@ -1383,6 +1383,7 @@ def generate(self, **kwargs) -> Tuple[str, TemplateExecutionResults]:
1383
1383
  for key in kwargs.get("tb_secrets", []):
1384
1384
  if is_secret_template_key(key):
1385
1385
  template_execution_results.add_template_param(key)
1386
+
1386
1387
  if TB_SECRET_IN_TEST_MODE in kwargs:
1387
1388
  template_execution_results[TB_SECRET_IN_TEST_MODE] = None
1388
1389
 
@@ -1493,7 +1494,7 @@ def generate(self, **kwargs) -> Tuple[str, TemplateExecutionResults]:
1493
1494
  raise e
1494
1495
 
1495
1496
 
1496
- class CodeWriter(object):
1497
+ class CodeWriter:
1497
1498
  def __init__(self, file, template):
1498
1499
  self.file = file
1499
1500
  self.current_template = template
@@ -1504,7 +1505,7 @@ class CodeWriter(object):
1504
1505
  return self._indent
1505
1506
 
1506
1507
  def indent(self):
1507
- class Indenter(object):
1508
+ class Indenter:
1508
1509
  def __enter__(_):
1509
1510
  self._indent += 1
1510
1511
  return self
@@ -1989,6 +1990,7 @@ def render_sql_template(
1989
1990
  secrets: Optional[List[str]] = None,
1990
1991
  test_mode: bool = False,
1991
1992
  name: Optional[str] = None,
1993
+ local_variables: Optional[dict] = None,
1992
1994
  ) -> Tuple[str, TemplateExecutionResults, list]:
1993
1995
  """
1994
1996
  >>> render_sql_template("select * from table where f = {{Float32(foo)}}", { 'foo': -1 })
@@ -2260,6 +2262,9 @@ def render_sql_template(
2260
2262
  }
2261
2263
  )
2262
2264
 
2265
+ if local_variables:
2266
+ v.update(local_variables)
2267
+
2263
2268
  else:
2264
2269
  v = {x["name"]: None for x in template_variables}
2265
2270
  if variables:
@@ -2267,8 +2272,12 @@ def render_sql_template(
2267
2272
 
2268
2273
  if secrets:
2269
2274
  v.update({"tb_secrets": secrets})
2275
+
2270
2276
  v.update(type_fns)
2271
2277
 
2278
+ if local_variables:
2279
+ v.update(local_variables)
2280
+
2272
2281
  try:
2273
2282
  sql, template_execution_results = generate(t, **v)
2274
2283
  try:
@@ -2298,3 +2307,37 @@ def render_sql_template(
2298
2307
  documentation="/cli/advanced-templates.html",
2299
2308
  )
2300
2309
  raise e
2310
+
2311
+
2312
+ def extract_variables_from_sql(sql: str, params: List[Dict[str, Any]]) -> Dict[str, Any]:
2313
+ sql = sql[1:] if sql[0] == "%" else sql
2314
+ defaults = {}
2315
+ mock_data = {}
2316
+ try:
2317
+ for param in params:
2318
+ mock_data[param["name"]] = "__NO__VALUE__DEFINED__"
2319
+ # Initialize a dictionary to track variables
2320
+ variable_tracker = {}
2321
+
2322
+ # Wrapper function to capture variable assignments
2323
+ def capture_variable(name, value):
2324
+ variable_tracker[name] = value
2325
+ return value
2326
+
2327
+ # Modify the template by adding capture hooks
2328
+ tracked_template_string = sql
2329
+ for var_name in mock_data.keys():
2330
+ tracked_template_string += f"{{% set __ = capture_variable('{var_name}', {var_name}) %}}"
2331
+
2332
+ # Define the modified template with tracking
2333
+ template = Template(tracked_template_string)
2334
+ type_fns = get_transform_types()
2335
+ template.generate(**mock_data, **type_fns, capture_variable=capture_variable)
2336
+ for var_name, value in variable_tracker.items():
2337
+ if value != "__NO__VALUE__DEFINED__":
2338
+ defaults[var_name] = value
2339
+ except Exception as e:
2340
+ logging.error(f"Error extracting variables from sql: {e}")
2341
+ return {}
2342
+
2343
+ return defaults
@@ -1128,7 +1128,7 @@ async def materialize(
1128
1128
  click.echo(FeedbackManager.info_materialize_push_pipe_override(name=f_pipe.name))
1129
1129
  option = click.prompt(FeedbackManager.prompt_choose(), default=1)
1130
1130
  force = True
1131
- check = True if option == 1 else False
1131
+ check = option == 1
1132
1132
 
1133
1133
  filename = str(f_pipe.absolute())
1134
1134
  to_run = await folder_push(
@@ -242,7 +242,7 @@ def filter_whitespace(mode, text):
242
242
  raise Exception("invalid whitespace mode %s" % mode)
243
243
 
244
244
 
245
- class Template(object):
245
+ class Template:
246
246
  """A compiled template.
247
247
 
248
248
  We compile into Python from the given template_string. You can generate
@@ -378,7 +378,7 @@ class Template(object):
378
378
  return ancestors
379
379
 
380
380
 
381
- class BaseLoader(object):
381
+ class BaseLoader:
382
382
  """Base class for template loaders.
383
383
 
384
384
  You must use a template loader to use template constructs like
@@ -438,7 +438,7 @@ class Loader(BaseLoader):
438
438
  """A template loader that loads from a single root directory."""
439
439
 
440
440
  def __init__(self, root_directory, **kwargs):
441
- super(Loader, self).__init__(**kwargs)
441
+ super().__init__(**kwargs)
442
442
  self.root = os.path.abspath(root_directory)
443
443
 
444
444
  def resolve_path(self, name, parent_path=None):
@@ -466,7 +466,7 @@ class DictLoader(BaseLoader):
466
466
  """A template loader that loads from a dictionary."""
467
467
 
468
468
  def __init__(self, dict, **kwargs):
469
- super(DictLoader, self).__init__(**kwargs)
469
+ super().__init__(**kwargs)
470
470
  self.dict = dict
471
471
 
472
472
  def resolve_path(self, name, parent_path=None):
@@ -484,7 +484,7 @@ class DictLoader(BaseLoader):
484
484
  return Template(self.dict[name], name=name, loader=self)
485
485
 
486
486
 
487
- class _Node(object):
487
+ class _Node:
488
488
  def each_child(self):
489
489
  return ()
490
490
 
@@ -646,7 +646,7 @@ class _Expression(_Node):
646
646
 
647
647
  class _Module(_Expression):
648
648
  def __init__(self, expression, line):
649
- super(_Module, self).__init__("_tt_modules." + expression, line, raw=True)
649
+ super().__init__("_tt_modules." + expression, line, raw=True)
650
650
 
651
651
 
652
652
  class _Text(_Node):
@@ -713,7 +713,7 @@ class ParseError(Exception):
713
713
  return "%s at %s:%d" % (self.message, self.filename, self.lineno)
714
714
 
715
715
 
716
- class _CodeWriter(object):
716
+ class _CodeWriter:
717
717
  def __init__(self, file, named_blocks, loader, current_template):
718
718
  self.file = file
719
719
  self.named_blocks = named_blocks
@@ -727,7 +727,7 @@ class _CodeWriter(object):
727
727
  return self._indent
728
728
 
729
729
  def indent(self):
730
- class Indenter(object):
730
+ class Indenter:
731
731
  def __enter__(_):
732
732
  self._indent += 1
733
733
  return self
@@ -742,7 +742,7 @@ class _CodeWriter(object):
742
742
  self.include_stack.append((self.current_template, line))
743
743
  self.current_template = template
744
744
 
745
- class IncludeTemplate(object):
745
+ class IncludeTemplate:
746
746
  def __enter__(_):
747
747
  return self
748
748
 
@@ -761,7 +761,7 @@ class _CodeWriter(object):
761
761
  print(" " * indent + line + line_comment, file=self.file)
762
762
 
763
763
 
764
- class _TemplateReader(object):
764
+ class _TemplateReader:
765
765
  def __init__(self, name, text, whitespace):
766
766
  self.name = name
767
767
  self.text = text
@@ -1169,9 +1169,7 @@ def check_valid_expr(expr):
1169
1169
  check_valid_expr(expr.slice.lower)
1170
1170
  if expr.slice.upper is not None:
1171
1171
  check_valid_expr(expr.slice.upper)
1172
- elif isinstance(expr.slice, ast.Constant):
1173
- check_valid_expr(expr.slice)
1174
- elif isinstance(expr.slice, ast.Subscript):
1172
+ elif isinstance(expr.slice, ast.Constant) or isinstance(expr.slice, ast.Subscript):
1175
1173
  check_valid_expr(expr.slice)
1176
1174
  else:
1177
1175
  raise SecurityException(f"Invalid Slice expression: {ast.dump(expr.slice)}")
@@ -1180,13 +1178,7 @@ def check_valid_expr(expr):
1180
1178
  check_valid_expr(key)
1181
1179
  for value in expr.values:
1182
1180
  check_valid_expr(value)
1183
- elif isinstance(expr, ast.Tuple):
1184
- for x in expr.elts:
1185
- check_valid_expr(x)
1186
- elif isinstance(expr, ast.List):
1187
- for x in expr.elts:
1188
- check_valid_expr(x)
1189
- elif isinstance(expr, ast.Set):
1181
+ elif isinstance(expr, ast.Tuple) or isinstance(expr, ast.List) or isinstance(expr, ast.Set):
1190
1182
  for x in expr.elts:
1191
1183
  check_valid_expr(x)
1192
1184
  elif isinstance(expr, ast.JoinedStr):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: tinybird-cli
3
- Version: 5.13.1.dev0
3
+ Version: 5.13.1.dev2
4
4
  Summary: Tinybird Command Line Tool
5
5
  Home-page: https://www.tinybird.co/docs/cli/introduction.html
6
6
  Author: Tinybird
@@ -18,10 +18,20 @@ The Tinybird command-line tool allows you to use all the Tinybird functionality
18
18
  Changelog
19
19
  ----------
20
20
 
21
+ 5.13.1.dev2
22
+ ***********
23
+
24
+ - `Added` support for `EXPORT_WRITE_STRATEGY` parameter for sinks, in `.pipe` files.
25
+
26
+ 5.13.1.dev1
27
+ ***********
28
+
29
+ - `Changed` environment variables are now evaluated in the SQL nodes.
30
+
21
31
  5.13.0
22
32
  ***********
23
33
 
24
- - `Addded` additional metadata to `tb branch ls` command
34
+ - `Added` additional metadata to `tb branch ls` command
25
35
  - `Changed` dependencies to be less restrictive:
26
36
  - cryptography: from ``>=41.0.0`` to ``~=41.0.0``
27
37
  - snowflake-connector-python: from ``==3.12.3`` to ``~=3.12.3``