tinybird-cli 3.8.2.dev1__tar.gz → 3.9.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. {tinybird-cli-3.8.2.dev1 → tinybird-cli-3.9.0}/PKG-INFO +5 -3
  2. {tinybird-cli-3.8.2.dev1 → tinybird-cli-3.9.0}/tinybird/__cli__.py +2 -2
  3. {tinybird-cli-3.8.2.dev1 → tinybird-cli-3.9.0}/tinybird/client.py +5 -2
  4. {tinybird-cli-3.8.2.dev1 → tinybird-cli-3.9.0}/tinybird/feedback_manager.py +1 -1
  5. {tinybird-cli-3.8.2.dev1 → tinybird-cli-3.9.0}/tinybird/sql_template.py +89 -2
  6. {tinybird-cli-3.8.2.dev1 → tinybird-cli-3.9.0}/tinybird/sql_toolset.py +1 -0
  7. {tinybird-cli-3.8.2.dev1 → tinybird-cli-3.9.0}/tinybird/tb_cli_modules/connection.py +27 -6
  8. {tinybird-cli-3.8.2.dev1 → tinybird-cli-3.9.0}/tinybird_cli.egg-info/PKG-INFO +5 -3
  9. {tinybird-cli-3.8.2.dev1 → tinybird-cli-3.9.0}/setup.cfg +0 -0
  10. {tinybird-cli-3.8.2.dev1 → tinybird-cli-3.9.0}/tinybird/ch_utils/constants.py +0 -0
  11. {tinybird-cli-3.8.2.dev1 → tinybird-cli-3.9.0}/tinybird/ch_utils/engine.py +0 -0
  12. {tinybird-cli-3.8.2.dev1 → tinybird-cli-3.9.0}/tinybird/check_pypi.py +0 -0
  13. {tinybird-cli-3.8.2.dev1 → tinybird-cli-3.9.0}/tinybird/config.py +0 -0
  14. {tinybird-cli-3.8.2.dev1 → tinybird-cli-3.9.0}/tinybird/connectors.py +0 -0
  15. {tinybird-cli-3.8.2.dev1 → tinybird-cli-3.9.0}/tinybird/context.py +0 -0
  16. {tinybird-cli-3.8.2.dev1 → tinybird-cli-3.9.0}/tinybird/datafile.py +0 -0
  17. {tinybird-cli-3.8.2.dev1 → tinybird-cli-3.9.0}/tinybird/datatypes.py +0 -0
  18. {tinybird-cli-3.8.2.dev1 → tinybird-cli-3.9.0}/tinybird/git_settings.py +0 -0
  19. {tinybird-cli-3.8.2.dev1 → tinybird-cli-3.9.0}/tinybird/sql.py +0 -0
  20. {tinybird-cli-3.8.2.dev1 → tinybird-cli-3.9.0}/tinybird/sql_template_fmt.py +0 -0
  21. {tinybird-cli-3.8.2.dev1 → tinybird-cli-3.9.0}/tinybird/syncasync.py +0 -0
  22. {tinybird-cli-3.8.2.dev1 → tinybird-cli-3.9.0}/tinybird/tb_cli.py +0 -0
  23. {tinybird-cli-3.8.2.dev1 → tinybird-cli-3.9.0}/tinybird/tb_cli_modules/auth.py +0 -0
  24. {tinybird-cli-3.8.2.dev1 → tinybird-cli-3.9.0}/tinybird/tb_cli_modules/branch.py +0 -0
  25. {tinybird-cli-3.8.2.dev1 → tinybird-cli-3.9.0}/tinybird/tb_cli_modules/cicd.py +0 -0
  26. {tinybird-cli-3.8.2.dev1 → tinybird-cli-3.9.0}/tinybird/tb_cli_modules/cli.py +0 -0
  27. {tinybird-cli-3.8.2.dev1 → tinybird-cli-3.9.0}/tinybird/tb_cli_modules/common.py +0 -0
  28. {tinybird-cli-3.8.2.dev1 → tinybird-cli-3.9.0}/tinybird/tb_cli_modules/config.py +0 -0
  29. {tinybird-cli-3.8.2.dev1 → tinybird-cli-3.9.0}/tinybird/tb_cli_modules/datasource.py +0 -0
  30. {tinybird-cli-3.8.2.dev1 → tinybird-cli-3.9.0}/tinybird/tb_cli_modules/exceptions.py +0 -0
  31. {tinybird-cli-3.8.2.dev1 → tinybird-cli-3.9.0}/tinybird/tb_cli_modules/job.py +0 -0
  32. {tinybird-cli-3.8.2.dev1 → tinybird-cli-3.9.0}/tinybird/tb_cli_modules/pipe.py +0 -0
  33. {tinybird-cli-3.8.2.dev1 → tinybird-cli-3.9.0}/tinybird/tb_cli_modules/regions.py +0 -0
  34. {tinybird-cli-3.8.2.dev1 → tinybird-cli-3.9.0}/tinybird/tb_cli_modules/telemetry.py +0 -0
  35. {tinybird-cli-3.8.2.dev1 → tinybird-cli-3.9.0}/tinybird/tb_cli_modules/test.py +0 -0
  36. {tinybird-cli-3.8.2.dev1 → tinybird-cli-3.9.0}/tinybird/tb_cli_modules/tinyunit/tinyunit.py +0 -0
  37. {tinybird-cli-3.8.2.dev1 → tinybird-cli-3.9.0}/tinybird/tb_cli_modules/tinyunit/tinyunit_lib.py +0 -0
  38. {tinybird-cli-3.8.2.dev1 → tinybird-cli-3.9.0}/tinybird/tb_cli_modules/token.py +0 -0
  39. {tinybird-cli-3.8.2.dev1 → tinybird-cli-3.9.0}/tinybird/tb_cli_modules/workspace.py +0 -0
  40. {tinybird-cli-3.8.2.dev1 → tinybird-cli-3.9.0}/tinybird/tb_cli_modules/workspace_members.py +0 -0
  41. {tinybird-cli-3.8.2.dev1 → tinybird-cli-3.9.0}/tinybird/tornado_template.py +0 -0
  42. {tinybird-cli-3.8.2.dev1 → tinybird-cli-3.9.0}/tinybird_cli.egg-info/SOURCES.txt +0 -0
  43. {tinybird-cli-3.8.2.dev1 → tinybird-cli-3.9.0}/tinybird_cli.egg-info/dependency_links.txt +0 -0
  44. {tinybird-cli-3.8.2.dev1 → tinybird-cli-3.9.0}/tinybird_cli.egg-info/entry_points.txt +0 -0
  45. {tinybird-cli-3.8.2.dev1 → tinybird-cli-3.9.0}/tinybird_cli.egg-info/requires.txt +0 -0
  46. {tinybird-cli-3.8.2.dev1 → tinybird-cli-3.9.0}/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: 3.8.2.dev1
3
+ Version: 3.9.0
4
4
  Summary: Tinybird Command Line Tool
5
5
  Home-page: https://www.tinybird.co/docs/cli/introduction.html
6
6
  Author: Tinybird
@@ -16,15 +16,17 @@ Tinybird CLI
16
16
  The Tinybird command-line tool allows you to use all the Tinybird functionality directly from the command line. Additionally, it includes several functions to create and manage data projects easily.
17
17
 
18
18
  Changelog
19
-
20
19
  ---------
21
20
 
22
- 3.8.1
21
+ 3.9.0
23
22
  ************
24
23
 
24
+ - `Added` Support for connection names when doing `tb connection rm`
25
+ - `Added` New `--policy` option for `create s3_iamrole` command that will generate different hints depending on the case
25
26
  - `Fixed` Avoid system vars evaluation when doing `tb fmt`
26
27
  - `Fixed` environment variables substitution for Data Source engine parameters.
27
28
 
29
+
28
30
  3.8.0
29
31
  ************
30
32
 
@@ -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__ = '3.8.2.dev1'
8
- __revision__ = 'f993ee3'
7
+ __version__ = '3.9.0'
8
+ __revision__ = 'e7e3bee'
@@ -997,8 +997,11 @@ class TinyB(object):
997
997
  async def get_s3_trust_policy(self) -> Dict[str, Any]:
998
998
  return await self._req("/v0/integrations/s3/policies/trust-policy")
999
999
 
1000
- async def get_s3_access_policy(self) -> Dict[str, Any]:
1001
- return await self._req("/v0/integrations/s3/policies/access-policy")
1000
+ async def get_s3_access_write_policy(self) -> Dict[str, Any]:
1001
+ return await self._req("/v0/integrations/s3/policies/write-access-policy")
1002
+
1003
+ async def get_s3_access_read_policy(self) -> Dict[str, Any]:
1004
+ return await self._req("/v0/integrations/s3/policies/read-access-policy")
1002
1005
 
1003
1006
  async def sql_get_format(self, sql: str, with_clickhouse_format: bool = False) -> str:
1004
1007
  try:
@@ -205,7 +205,7 @@ class FeedbackManager:
205
205
  error_connection_already_exists = error_message(
206
206
  "Connection {name} already exists. Use 'tb connection ls' to list your connections"
207
207
  )
208
- error_connection_does_not_exists = error_message("Connection {connection} does not exist")
208
+ error_connection_does_not_exists = error_message("Connection {connection_id} does not exist")
209
209
  error_connection_create = error_message("Connection {connection_name} could not be created: {error}")
210
210
  error_connection_integration_not_available = error_message("Connection could not be created: {error}")
211
211
  error_workspace = error_message("Workspace {workspace} not found. use 'tb workspace ls' to list your workspaces")
@@ -1562,7 +1562,20 @@ def get_var_data(content, node_id=None): # noqa: C901
1562
1562
 
1563
1563
  return vars
1564
1564
 
1565
- parsed = ast.parse(content)
1565
+ def parse_content(content, retries=0):
1566
+ try:
1567
+ parsed = ast.parse(content)
1568
+ return parsed
1569
+ except Exception as e:
1570
+ if "AST constructor recursion depth mismatch" not in str(e):
1571
+ raise e
1572
+ retries += 1
1573
+ if retries > 3:
1574
+ raise e
1575
+ return parse_content(content, retries)
1576
+
1577
+ parsed = parse_content(content)
1578
+
1566
1579
  # calculate parents for each node for later checks
1567
1580
  for node in ast.walk(parsed):
1568
1581
  for child in ast.iter_child_nodes(node):
@@ -1655,6 +1668,11 @@ def get_var_names_and_types(t, node_id=None): # noqa: C901
1655
1668
  raise SQLTemplateException(e)
1656
1669
 
1657
1670
 
1671
+ @lru_cache(maxsize=256)
1672
+ def get_var_names_and_types_cached(t: Template):
1673
+ return get_var_names_and_types(t)
1674
+
1675
+
1658
1676
  def wrap_vars(t):
1659
1677
  def _n(chunks, v):
1660
1678
  for x in chunks:
@@ -1746,8 +1764,55 @@ def get_template_and_variables(sql: str, name: Optional[str]):
1746
1764
  raise SQLTemplateException(e)
1747
1765
 
1748
1766
 
1767
+ def preprocess_variables(variables: dict, t: Template):
1768
+ """
1769
+ >>> preprocess_variables({"test": "1,2"}, Template("select * from table where f = {{Array(test, 'String')}}"))
1770
+ {'test': ['1', '2']}
1771
+ >>> preprocess_variables({"test": ['1', '2']}, Template("select * from table where f = {{Array(test, 'String')}}"))
1772
+ {'test': ['1', '2']}
1773
+ >>> preprocess_variables({"test": [1,2]}, Template("select * from table where f = {{Array(test, 'String')}}"))
1774
+ {'test': ['1', '2']}
1775
+ >>> preprocess_variables({"test": '24'}, Template("select * from table where f = {{Int32(test)}}"))
1776
+ {}
1777
+ >>> preprocess_variables({"test": "1,2,3"}, Template("select * from table where f = {{Array(test,'Int32')}}"))
1778
+ {'test': [1, 2, 3]}
1779
+ >>> preprocess_variables({"test": "1,2,msg"}, Template("select * from table where f = {{Array(test,'Int32')}}"))
1780
+ {}
1781
+ """
1782
+ template_variables = get_var_names_and_types_cached(t)
1783
+ processed_variables = {}
1784
+ for variable, value in variables.items():
1785
+ try:
1786
+ template_vars = [t_var for t_var in template_variables if t_var["name"] == variable] or None
1787
+ if template_vars is None or value is None:
1788
+ continue
1789
+
1790
+ t_var = template_vars[0]
1791
+ var_type = t_var.get("type")
1792
+ if var_type is None:
1793
+ continue
1794
+
1795
+ # For now, we only preprocess Array types
1796
+ match = re.match(r"Array\((\w+)\)", var_type)
1797
+ if match is None:
1798
+ continue
1799
+
1800
+ array_type = match.group(1)
1801
+ array_fn = type_fns.get("Array")
1802
+ parsed_exp = array_fn(value, array_type)
1803
+ processed_variables[variable] = ast.literal_eval(parsed_exp)
1804
+ except Exception:
1805
+ continue
1806
+
1807
+ return processed_variables
1808
+
1809
+
1749
1810
  def render_sql_template(
1750
- sql: str, variables: Optional[dict] = None, test_mode: bool = False, name: Optional[str] = None
1811
+ sql: str,
1812
+ variables: Optional[dict] = None,
1813
+ test_mode: bool = False,
1814
+ name: Optional[str] = None,
1815
+ preprocess_variables_flag: bool = False,
1751
1816
  ) -> Tuple[str, dict]:
1752
1817
  """
1753
1818
  >>> render_sql_template("select * from table where f = {{Float32(foo)}}", { 'foo': -1 })
@@ -1934,10 +1999,32 @@ def render_sql_template(
1934
1999
  Traceback (most recent call last):
1935
2000
  ...
1936
2001
  tinybird.sql_template.SQLTemplateException: Template Syntax Error: Error parsing JSON: '' - Expecting value: line 1 column 1 (char 0)
2002
+ >>> render_sql_template("% {% if defined(test) %}{% set _groupByCSV = ','.join(test) %} SELECT test as aa, {{Array(test, 'String')}} as test, {{_groupByCSV}} as a {% end %}", {"test": "1,2"}, preprocess_variables_flag=True)
2003
+ ("% SELECT test as aa, ['1','2'] as test, '1,2' as a ", {})
2004
+ >>> render_sql_template("% {% if defined(test) %}{% set _groupByCSV = ','.join(test) %} SELECT test as aa, {{Array(test, 'String')}} as test, {{_groupByCSV}} as a {% end %}", {"test": ["1","2"]}, preprocess_variables_flag=True)
2005
+ ("% SELECT test as aa, ['1','2'] as test, '1,2' as a ", {})
2006
+ >>> render_sql_template("% {% if defined(test) %}{% set _total = sum(test) %} SELECT test as aa, {{Array(test, 'Int32')}} as test, {{_total}} as a {% end %}", {"test": "1,2"}, preprocess_variables_flag=True)
2007
+ ('% SELECT test as aa, [1,2] as test, 3 as a ', {})
2008
+ >>> render_sql_template("% {% if defined(test) %}{% set _groupByCSV = ','.join(test) %} SELECT test as aa, {{Array(test, 'String')}} as test, {{_groupByCSV}} as a {% end %}", {"test": "1,2"})
2009
+ ("% SELECT test as aa, ['1','2'] as test, '1,,,2' as a ", {})
2010
+ >>> render_sql_template("% {% if defined(test) %}{% set _groupByCSV = ','.join(test) %} SELECT test as aa, {{Array(test, 'String')}} as test, {{_groupByCSV}} as a {% end %}", {"test": ["1","2"]})
2011
+ ("% SELECT test as aa, ['1','2'] as test, '1,2' as a ", {})
2012
+ >>> render_sql_template("% {% if defined(test) %}{% set _total = sum(test) %} SELECT test as aa, {{Array(test, 'Int32')}} as test, {{_total}} as a {% end %}", {"test": "1,2"})
2013
+ Traceback (most recent call last):
2014
+ ...
2015
+ ValueError: unsupported operand type(s) for +: 'int' and 'str'
2016
+ >>> render_sql_template("% SELECT {% if defined(x) %} x, 1", preprocess_variables_flag=True)
2017
+ Traceback (most recent call last):
2018
+ ...
2019
+ tinybird.tornado_template.UnClosedIfError: Missing {% end %} block for if at line 1
1937
2020
  """
1938
2021
 
1939
2022
  t, template_variables = get_template_and_variables(sql, name)
1940
2023
 
2024
+ if preprocess_variables_flag and variables is not None:
2025
+ processed_variables = preprocess_variables(variables, t)
2026
+ variables.update(processed_variables)
2027
+
1941
2028
  if test_mode:
1942
2029
 
1943
2030
  def dummy(*args, **kwargs):
@@ -164,6 +164,7 @@ def replace_tables(
164
164
  valid_tables: Optional[Set[Tuple[str, str]]] = None,
165
165
  output_one_line: bool = False,
166
166
  timestamp: Optional[datetime] = None,
167
+ preprocess_variables_flag: bool = False,
167
168
  ) -> str:
168
169
  """
169
170
  Given a query and a list of table replacements, returns the query after applying the table replacements.
@@ -394,25 +394,39 @@ async def connection_create_bigquery(ctx: Context, no_validate: bool) -> None:
394
394
 
395
395
 
396
396
  @connection.command(name="rm")
397
- @click.argument("connection_id")
397
+ @click.argument("connection_id_or_name")
398
398
  @click.option(
399
399
  "--force", default=False, help="Force connection removal even if there are datasources currently using it"
400
400
  )
401
401
  @click.pass_context
402
402
  @coro
403
- async def connection_rm(ctx: Context, connection_id: str, force: bool) -> None:
403
+ async def connection_rm(ctx: Context, connection_id_or_name: str, force: bool) -> None:
404
404
  """Remove a connection."""
405
405
 
406
406
  obj: Dict[str, Any] = ctx.ensure_object(dict)
407
407
  client: TinyB = obj["client"]
408
408
 
409
409
  try:
410
- await client.connector_delete(connection_id)
410
+ await client.connector_delete(connection_id_or_name)
411
411
  except DoesNotExistException:
412
- raise CLIConnectionException(FeedbackManager.error_connection_does_not_exists(connection_id=connection_id))
412
+ connections = await client.connections()
413
+ connection = next(
414
+ (connection for connection in connections if connection["name"] == connection_id_or_name), None
415
+ )
416
+ if connection:
417
+ try:
418
+ await client.connector_delete(connection["id"])
419
+ except DoesNotExistException:
420
+ raise CLIConnectionException(
421
+ FeedbackManager.error_connection_does_not_exists(connection_id=connection_id_or_name)
422
+ )
423
+ else:
424
+ raise CLIConnectionException(
425
+ FeedbackManager.error_connection_does_not_exists(connection_id=connection_id_or_name)
426
+ )
413
427
  except Exception as e:
414
428
  raise CLIConnectionException(FeedbackManager.error_exception(error=e))
415
- click.echo(FeedbackManager.success_delete_connection(connection_id=connection_id))
429
+ click.echo(FeedbackManager.success_delete_connection(connection_id=connection_id_or_name))
416
430
 
417
431
 
418
432
  @connection.command(name="ls")
@@ -698,6 +712,7 @@ async def connection_create_gcs(
698
712
  @click.option("--connection-name", default=None, help="The name of the connection to identify it in Tinybird")
699
713
  @click.option("--role-arn", default=None, help="The ARN of the IAM role to use for the connection")
700
714
  @click.option("--region", default=None, help="The Amazon S3 region where the bucket is located")
715
+ @click.option("--policy", default="write", help="The Amazon S3 access policy: write or read")
701
716
  @click.option(
702
717
  "--no-validate", is_flag=True, default=False, help="Do not validate S3 permissions during connection creation"
703
718
  )
@@ -708,6 +723,7 @@ async def connection_create_s3_iamrole(
708
723
  connection_name: Optional[str] = "",
709
724
  role_arn: Optional[str] = "",
710
725
  region: Optional[str] = "",
726
+ policy: str = "write",
711
727
  no_validate: Optional[bool] = False,
712
728
  ) -> None:
713
729
  """
@@ -723,7 +739,12 @@ async def connection_create_s3_iamrole(
723
739
  async def get_s3_policies():
724
740
  s3_access_policy: Dict[str, Any] = {}
725
741
  try:
726
- s3_access_policy = await client.get_s3_access_policy()
742
+ if policy == "write":
743
+ s3_access_policy = await client.get_s3_access_write_policy()
744
+ elif policy == "read":
745
+ s3_access_policy = await client.get_s3_access_read_policy()
746
+ else:
747
+ raise Exception(f"Access policy {policy} not supported. Choose from 'read' or 'write'")
727
748
  if not len(s3_access_policy) > 0:
728
749
  raise Exception("S3 Integration not supported in this region")
729
750
  except Exception as e:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: tinybird-cli
3
- Version: 3.8.2.dev1
3
+ Version: 3.9.0
4
4
  Summary: Tinybird Command Line Tool
5
5
  Home-page: https://www.tinybird.co/docs/cli/introduction.html
6
6
  Author: Tinybird
@@ -16,15 +16,17 @@ Tinybird CLI
16
16
  The Tinybird command-line tool allows you to use all the Tinybird functionality directly from the command line. Additionally, it includes several functions to create and manage data projects easily.
17
17
 
18
18
  Changelog
19
-
20
19
  ---------
21
20
 
22
- 3.8.1
21
+ 3.9.0
23
22
  ************
24
23
 
24
+ - `Added` Support for connection names when doing `tb connection rm`
25
+ - `Added` New `--policy` option for `create s3_iamrole` command that will generate different hints depending on the case
25
26
  - `Fixed` Avoid system vars evaluation when doing `tb fmt`
26
27
  - `Fixed` environment variables substitution for Data Source engine parameters.
27
28
 
29
+
28
30
  3.8.0
29
31
  ************
30
32