tinybird-cli 3.8.1.dev1__tar.gz → 3.8.2.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.
- {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/PKG-INFO +18 -2
- {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/__cli__.py +2 -2
- {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/client.py +5 -2
- {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/datafile.py +28 -14
- {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/feedback_manager.py +1 -1
- {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/sql_template.py +71 -1
- {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/sql_toolset.py +1 -0
- {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/tb_cli_modules/cli.py +2 -2
- {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/tb_cli_modules/connection.py +27 -6
- {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird_cli.egg-info/PKG-INFO +18 -2
- {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/setup.cfg +0 -0
- {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/ch_utils/constants.py +0 -0
- {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/ch_utils/engine.py +0 -0
- {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/check_pypi.py +0 -0
- {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/config.py +0 -0
- {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/connectors.py +0 -0
- {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/context.py +0 -0
- {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/datatypes.py +0 -0
- {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/git_settings.py +0 -0
- {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/sql.py +0 -0
- {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/sql_template_fmt.py +0 -0
- {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/syncasync.py +0 -0
- {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/tb_cli.py +0 -0
- {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/tb_cli_modules/auth.py +0 -0
- {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/tb_cli_modules/branch.py +0 -0
- {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/tb_cli_modules/cicd.py +0 -0
- {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/tb_cli_modules/common.py +0 -0
- {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/tb_cli_modules/config.py +0 -0
- {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/tb_cli_modules/datasource.py +0 -0
- {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/tb_cli_modules/exceptions.py +0 -0
- {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/tb_cli_modules/job.py +0 -0
- {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/tb_cli_modules/pipe.py +0 -0
- {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/tb_cli_modules/regions.py +0 -0
- {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/tb_cli_modules/telemetry.py +0 -0
- {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/tb_cli_modules/test.py +0 -0
- {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/tb_cli_modules/tinyunit/tinyunit.py +0 -0
- {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/tb_cli_modules/tinyunit/tinyunit_lib.py +0 -0
- {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/tb_cli_modules/token.py +0 -0
- {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/tb_cli_modules/workspace.py +0 -0
- {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/tb_cli_modules/workspace_members.py +0 -0
- {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/tornado_template.py +0 -0
- {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird_cli.egg-info/SOURCES.txt +0 -0
- {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird_cli.egg-info/dependency_links.txt +0 -0
- {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird_cli.egg-info/entry_points.txt +0 -0
- {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird_cli.egg-info/requires.txt +0 -0
- {tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.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: 3.8.
|
|
3
|
+
Version: 3.8.2.dev2
|
|
4
4
|
Summary: Tinybird Command Line Tool
|
|
5
5
|
Home-page: https://www.tinybird.co/docs/cli/introduction.html
|
|
6
6
|
Author: Tinybird
|
|
@@ -16,9 +16,25 @@ 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
|
|
|
21
|
+
3.8.2.dev2
|
|
22
|
+
************
|
|
23
|
+
|
|
24
|
+
- `Added` New `--policy` option for `create s3_iamrole` command that will generate different hints depending on the case
|
|
25
|
+
|
|
26
|
+
3.8.2.dev1
|
|
27
|
+
************
|
|
28
|
+
|
|
29
|
+
- `Added` Support for connection names when doing `tb connection rm`
|
|
30
|
+
|
|
31
|
+
3.8.1
|
|
32
|
+
************
|
|
33
|
+
|
|
34
|
+
- `Fixed` Avoid system vars evaluation when doing `tb fmt`
|
|
35
|
+
- `Fixed` environment variables substitution for Data Source engine parameters.
|
|
36
|
+
|
|
37
|
+
|
|
22
38
|
3.8.0
|
|
23
39
|
************
|
|
24
40
|
|
|
@@ -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.
|
|
8
|
-
__revision__ = '
|
|
7
|
+
__version__ = '3.8.2.dev2'
|
|
8
|
+
__revision__ = 'b88d054'
|
|
@@ -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
|
|
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:
|
|
@@ -727,7 +727,9 @@ class Datafile:
|
|
|
727
727
|
return True
|
|
728
728
|
|
|
729
729
|
|
|
730
|
-
def parse_datasource(
|
|
730
|
+
def parse_datasource(
|
|
731
|
+
filename: str, replace_includes: bool = True, content: Optional[str] = None, skip_eval: bool = False
|
|
732
|
+
) -> Datafile:
|
|
731
733
|
basepath = ""
|
|
732
734
|
if not content:
|
|
733
735
|
with open(filename) as file:
|
|
@@ -737,7 +739,7 @@ def parse_datasource(filename: str, replace_includes: bool = True, content: Opti
|
|
|
737
739
|
s = content
|
|
738
740
|
|
|
739
741
|
try:
|
|
740
|
-
doc = parse(s, "default", basepath, replace_includes=replace_includes)
|
|
742
|
+
doc = parse(s, "default", basepath, replace_includes=replace_includes, skip_eval=skip_eval)
|
|
741
743
|
except ParseException as e:
|
|
742
744
|
raise click.ClickException(
|
|
743
745
|
FeedbackManager.error_parsing_file(filename=filename, lineno=e.lineno, error=e)
|
|
@@ -749,7 +751,9 @@ def parse_datasource(filename: str, replace_includes: bool = True, content: Opti
|
|
|
749
751
|
return doc
|
|
750
752
|
|
|
751
753
|
|
|
752
|
-
def parse_pipe(
|
|
754
|
+
def parse_pipe(
|
|
755
|
+
filename: str, replace_includes: bool = True, content: Optional[str] = None, skip_eval: bool = False
|
|
756
|
+
) -> Datafile:
|
|
753
757
|
basepath = ""
|
|
754
758
|
if not content:
|
|
755
759
|
with open(filename) as file:
|
|
@@ -760,7 +764,7 @@ def parse_pipe(filename: str, replace_includes: bool = True, content: Optional[s
|
|
|
760
764
|
|
|
761
765
|
try:
|
|
762
766
|
sql = ""
|
|
763
|
-
doc = parse(s, basepath=basepath, replace_includes=replace_includes)
|
|
767
|
+
doc = parse(s, basepath=basepath, replace_includes=replace_includes, skip_eval=skip_eval)
|
|
764
768
|
for node in doc.nodes:
|
|
765
769
|
sql = node.get("sql", "")
|
|
766
770
|
if sql.strip()[0] == "%":
|
|
@@ -795,7 +799,9 @@ def parse_pipe(filename: str, replace_includes: bool = True, content: Optional[s
|
|
|
795
799
|
return doc
|
|
796
800
|
|
|
797
801
|
|
|
798
|
-
def parse_token(
|
|
802
|
+
def parse_token(
|
|
803
|
+
filename: str, replace_includes: bool = True, content: Optional[str] = None, skip_eval: bool = False
|
|
804
|
+
) -> Datafile:
|
|
799
805
|
if not content:
|
|
800
806
|
with open(filename) as file:
|
|
801
807
|
s = file.read()
|
|
@@ -805,7 +811,7 @@ def parse_token(filename: str, replace_includes: bool = True, content: Optional[
|
|
|
805
811
|
|
|
806
812
|
try:
|
|
807
813
|
sql = ""
|
|
808
|
-
doc = parse(s, basepath=basepath, replace_includes=replace_includes)
|
|
814
|
+
doc = parse(s, basepath=basepath, replace_includes=replace_includes, skip_eval=skip_eval)
|
|
809
815
|
except ParseException as e:
|
|
810
816
|
raise click.ClickException(
|
|
811
817
|
FeedbackManager.error_parsing_file(
|
|
@@ -835,14 +841,20 @@ def _unquote(x: str):
|
|
|
835
841
|
return x
|
|
836
842
|
|
|
837
843
|
|
|
838
|
-
def eval_var(s: str) -> str:
|
|
844
|
+
def eval_var(s: str, skip: bool = False) -> str:
|
|
845
|
+
if skip:
|
|
846
|
+
return s
|
|
839
847
|
# replace ENV variables
|
|
840
848
|
# it's probably a bad idea to allow to get any env var
|
|
841
849
|
return Template(s).safe_substitute(os.environ)
|
|
842
850
|
|
|
843
851
|
|
|
844
852
|
def parse(
|
|
845
|
-
s: str,
|
|
853
|
+
s: str,
|
|
854
|
+
default_node: Optional[str] = None,
|
|
855
|
+
basepath: str = ".",
|
|
856
|
+
replace_includes: bool = True,
|
|
857
|
+
skip_eval: bool = False,
|
|
846
858
|
) -> Datafile: # noqa: C901
|
|
847
859
|
"""
|
|
848
860
|
Parses `s` string into a document
|
|
@@ -898,7 +910,7 @@ def parse(
|
|
|
898
910
|
def assign_var(v: str) -> Callable[[VarArg(str), KwArg(Any)], None]:
|
|
899
911
|
def _f(*args: str, **kwargs: Any):
|
|
900
912
|
s = _unquote((" ".join(args)).strip())
|
|
901
|
-
parser_state.current_node[v.lower()] = eval_var(s)
|
|
913
|
+
parser_state.current_node[v.lower()] = eval_var(s, skip=skip_eval)
|
|
902
914
|
|
|
903
915
|
return _f
|
|
904
916
|
|
|
@@ -1029,12 +1041,12 @@ def parse(
|
|
|
1029
1041
|
def set_engine(*args: str, **kwargs: Any) -> None:
|
|
1030
1042
|
__init_engine("ENGINE")
|
|
1031
1043
|
engine_type = _unquote((" ".join(args)).strip())
|
|
1032
|
-
parser_state.current_node["engine"]["type"] = engine_type
|
|
1044
|
+
parser_state.current_node["engine"]["type"] = eval_var(engine_type, skip=skip_eval)
|
|
1033
1045
|
|
|
1034
1046
|
def add_engine_var(v: str) -> Callable[[VarArg(str), KwArg(Any)], None]:
|
|
1035
1047
|
def _f(*args: str, **kwargs: Any):
|
|
1036
1048
|
__init_engine(f"ENGINE_{v}".upper())
|
|
1037
|
-
engine_arg = _unquote((" ".join(args)).strip())
|
|
1049
|
+
engine_arg = eval_var(_unquote((" ".join(args)).strip()), skip=skip_eval)
|
|
1038
1050
|
parser_state.current_node["engine"]["args"].append((v, engine_arg))
|
|
1039
1051
|
|
|
1040
1052
|
return _f
|
|
@@ -4470,11 +4482,12 @@ async def format_datasource(
|
|
|
4470
4482
|
replace_includes: bool = False,
|
|
4471
4483
|
datafile: Optional[Datafile] = None,
|
|
4472
4484
|
for_deploy_diff: bool = False,
|
|
4485
|
+
skip_eval: bool = False,
|
|
4473
4486
|
) -> str:
|
|
4474
4487
|
if datafile:
|
|
4475
4488
|
doc = datafile
|
|
4476
4489
|
else:
|
|
4477
|
-
doc = parse_datasource(filename, replace_includes=replace_includes)
|
|
4490
|
+
doc = parse_datasource(filename, replace_includes=replace_includes, skip_eval=skip_eval)
|
|
4478
4491
|
|
|
4479
4492
|
file_parts: List[str] = []
|
|
4480
4493
|
if for_diff:
|
|
@@ -4707,11 +4720,12 @@ async def format_pipe(
|
|
|
4707
4720
|
replace_includes: bool = False,
|
|
4708
4721
|
datafile: Optional[Datafile] = None,
|
|
4709
4722
|
for_deploy_diff: bool = False,
|
|
4723
|
+
skip_eval: bool = False,
|
|
4710
4724
|
) -> str:
|
|
4711
4725
|
if datafile:
|
|
4712
4726
|
doc = datafile
|
|
4713
4727
|
else:
|
|
4714
|
-
doc = parse_pipe(filename, replace_includes=replace_includes)
|
|
4728
|
+
doc = parse_pipe(filename, replace_includes=replace_includes, skip_eval=skip_eval)
|
|
4715
4729
|
|
|
4716
4730
|
file_parts: List[str] = []
|
|
4717
4731
|
format_sources(file_parts, doc)
|
|
@@ -4738,7 +4752,7 @@ async def format_pipe(
|
|
|
4738
4752
|
if "." in include_file
|
|
4739
4753
|
else eval_var(include_file)
|
|
4740
4754
|
)
|
|
4741
|
-
included_pipe = parse_pipe(include_file)
|
|
4755
|
+
included_pipe = parse_pipe(include_file, skip_eval=skip_eval)
|
|
4742
4756
|
pipe_nodes = doc.nodes.copy()
|
|
4743
4757
|
for included_node in included_pipe.nodes.copy():
|
|
4744
4758
|
unrolled_included_node = next(
|
|
@@ -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 {
|
|
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")
|
|
@@ -1655,6 +1655,11 @@ def get_var_names_and_types(t, node_id=None): # noqa: C901
|
|
|
1655
1655
|
raise SQLTemplateException(e)
|
|
1656
1656
|
|
|
1657
1657
|
|
|
1658
|
+
@lru_cache(maxsize=256)
|
|
1659
|
+
def get_var_names_and_types_cached(t: Template):
|
|
1660
|
+
return get_var_names_and_types(t)
|
|
1661
|
+
|
|
1662
|
+
|
|
1658
1663
|
def wrap_vars(t):
|
|
1659
1664
|
def _n(chunks, v):
|
|
1660
1665
|
for x in chunks:
|
|
@@ -1746,8 +1751,55 @@ def get_template_and_variables(sql: str, name: Optional[str]):
|
|
|
1746
1751
|
raise SQLTemplateException(e)
|
|
1747
1752
|
|
|
1748
1753
|
|
|
1754
|
+
def preprocess_variables(variables: dict, t: Template):
|
|
1755
|
+
"""
|
|
1756
|
+
>>> preprocess_variables({"test": "1,2"}, Template("select * from table where f = {{Array(test, 'String')}}"))
|
|
1757
|
+
{'test': ['1', '2']}
|
|
1758
|
+
>>> preprocess_variables({"test": ['1', '2']}, Template("select * from table where f = {{Array(test, 'String')}}"))
|
|
1759
|
+
{'test': ['1', '2']}
|
|
1760
|
+
>>> preprocess_variables({"test": [1,2]}, Template("select * from table where f = {{Array(test, 'String')}}"))
|
|
1761
|
+
{'test': ['1', '2']}
|
|
1762
|
+
>>> preprocess_variables({"test": '24'}, Template("select * from table where f = {{Int32(test)}}"))
|
|
1763
|
+
{}
|
|
1764
|
+
>>> preprocess_variables({"test": "1,2,3"}, Template("select * from table where f = {{Array(test,'Int32')}}"))
|
|
1765
|
+
{'test': [1, 2, 3]}
|
|
1766
|
+
>>> preprocess_variables({"test": "1,2,msg"}, Template("select * from table where f = {{Array(test,'Int32')}}"))
|
|
1767
|
+
{}
|
|
1768
|
+
"""
|
|
1769
|
+
template_variables = get_var_names_and_types_cached(t)
|
|
1770
|
+
processed_variables = {}
|
|
1771
|
+
for variable, value in variables.items():
|
|
1772
|
+
try:
|
|
1773
|
+
template_vars = [t_var for t_var in template_variables if t_var["name"] == variable] or None
|
|
1774
|
+
if template_vars is None or value is None:
|
|
1775
|
+
continue
|
|
1776
|
+
|
|
1777
|
+
t_var = template_vars[0]
|
|
1778
|
+
var_type = t_var.get("type")
|
|
1779
|
+
if var_type is None:
|
|
1780
|
+
continue
|
|
1781
|
+
|
|
1782
|
+
# For now, we only preprocess Array types
|
|
1783
|
+
match = re.match(r"Array\((\w+)\)", var_type)
|
|
1784
|
+
if match is None:
|
|
1785
|
+
continue
|
|
1786
|
+
|
|
1787
|
+
array_type = match.group(1)
|
|
1788
|
+
array_fn = type_fns.get("Array")
|
|
1789
|
+
parsed_exp = array_fn(value, array_type)
|
|
1790
|
+
processed_variables[variable] = ast.literal_eval(parsed_exp)
|
|
1791
|
+
except Exception:
|
|
1792
|
+
continue
|
|
1793
|
+
|
|
1794
|
+
return processed_variables
|
|
1795
|
+
|
|
1796
|
+
|
|
1749
1797
|
def render_sql_template(
|
|
1750
|
-
sql: str,
|
|
1798
|
+
sql: str,
|
|
1799
|
+
variables: Optional[dict] = None,
|
|
1800
|
+
test_mode: bool = False,
|
|
1801
|
+
name: Optional[str] = None,
|
|
1802
|
+
preprocess_variables_flag: bool = False,
|
|
1751
1803
|
) -> Tuple[str, dict]:
|
|
1752
1804
|
"""
|
|
1753
1805
|
>>> render_sql_template("select * from table where f = {{Float32(foo)}}", { 'foo': -1 })
|
|
@@ -1934,10 +1986,28 @@ def render_sql_template(
|
|
|
1934
1986
|
Traceback (most recent call last):
|
|
1935
1987
|
...
|
|
1936
1988
|
tinybird.sql_template.SQLTemplateException: Template Syntax Error: Error parsing JSON: '' - Expecting value: line 1 column 1 (char 0)
|
|
1989
|
+
>>> 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)
|
|
1990
|
+
("% SELECT test as aa, ['1','2'] as test, '1,2' as a ", {})
|
|
1991
|
+
>>> 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)
|
|
1992
|
+
("% SELECT test as aa, ['1','2'] as test, '1,2' as a ", {})
|
|
1993
|
+
>>> 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)
|
|
1994
|
+
('% SELECT test as aa, [1,2] as test, 3 as a ', {})
|
|
1995
|
+
>>> 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"})
|
|
1996
|
+
("% SELECT test as aa, ['1','2'] as test, '1,,,2' as a ", {})
|
|
1997
|
+
>>> 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"]})
|
|
1998
|
+
("% SELECT test as aa, ['1','2'] as test, '1,2' as a ", {})
|
|
1999
|
+
>>> 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"})
|
|
2000
|
+
Traceback (most recent call last):
|
|
2001
|
+
...
|
|
2002
|
+
ValueError: unsupported operand type(s) for +: 'int' and 'str'
|
|
1937
2003
|
"""
|
|
1938
2004
|
|
|
1939
2005
|
t, template_variables = get_template_and_variables(sql, name)
|
|
1940
2006
|
|
|
2007
|
+
if preprocess_variables_flag and variables is not None:
|
|
2008
|
+
processed_variables = preprocess_variables(variables, t)
|
|
2009
|
+
variables.update(processed_variables)
|
|
2010
|
+
|
|
1941
2011
|
if test_mode:
|
|
1942
2012
|
|
|
1943
2013
|
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.
|
|
@@ -797,9 +797,9 @@ async def fmt(
|
|
|
797
797
|
click.echo(filename)
|
|
798
798
|
extensions = Path(filename).suffixes
|
|
799
799
|
if is_file_a_datasource(filename):
|
|
800
|
-
result = await format_datasource(filename)
|
|
800
|
+
result = await format_datasource(filename, skip_eval=True)
|
|
801
801
|
elif (".pipe" in extensions) or (".incl" in extensions):
|
|
802
|
-
result = await format_pipe(filename, line_length)
|
|
802
|
+
result = await format_pipe(filename, line_length, skip_eval=True)
|
|
803
803
|
else:
|
|
804
804
|
click.echo("Unsupported file type. Supported files types are: .pipe, .incl and .datasource")
|
|
805
805
|
return None
|
|
@@ -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("
|
|
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,
|
|
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(
|
|
410
|
+
await client.connector_delete(connection_id_or_name)
|
|
411
411
|
except DoesNotExistException:
|
|
412
|
-
|
|
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=
|
|
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
|
-
|
|
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.
|
|
3
|
+
Version: 3.8.2.dev2
|
|
4
4
|
Summary: Tinybird Command Line Tool
|
|
5
5
|
Home-page: https://www.tinybird.co/docs/cli/introduction.html
|
|
6
6
|
Author: Tinybird
|
|
@@ -16,9 +16,25 @@ 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
|
|
|
21
|
+
3.8.2.dev2
|
|
22
|
+
************
|
|
23
|
+
|
|
24
|
+
- `Added` New `--policy` option for `create s3_iamrole` command that will generate different hints depending on the case
|
|
25
|
+
|
|
26
|
+
3.8.2.dev1
|
|
27
|
+
************
|
|
28
|
+
|
|
29
|
+
- `Added` Support for connection names when doing `tb connection rm`
|
|
30
|
+
|
|
31
|
+
3.8.1
|
|
32
|
+
************
|
|
33
|
+
|
|
34
|
+
- `Fixed` Avoid system vars evaluation when doing `tb fmt`
|
|
35
|
+
- `Fixed` environment variables substitution for Data Source engine parameters.
|
|
36
|
+
|
|
37
|
+
|
|
22
38
|
3.8.0
|
|
23
39
|
************
|
|
24
40
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/tb_cli_modules/tinyunit/tinyunit.py
RENAMED
|
File without changes
|
{tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/tb_cli_modules/tinyunit/tinyunit_lib.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird/tb_cli_modules/workspace_members.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tinybird-cli-3.8.1.dev1 → tinybird-cli-3.8.2.dev2}/tinybird_cli.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|