tinybird 4.6.6.dev0__tar.gz → 4.6.7__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.6.dev0 → tinybird-4.6.7}/PKG-INFO +13 -1
  2. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/sql_template.py +9 -12
  3. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/__cli__.py +2 -2
  4. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/build.py +1 -46
  5. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/config.py +0 -3
  6. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/datafile/playground.py +1 -329
  7. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/local_common.py +124 -22
  8. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/login_common.py +12 -3
  9. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/watch.py +1 -100
  10. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb_cli_modules/config.py +0 -6
  11. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird.egg-info/PKG-INFO +13 -1
  12. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/setup.cfg +0 -0
  13. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/__cli__.py +0 -0
  14. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/ch_utils/constants.py +0 -0
  15. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/ch_utils/engine.py +0 -0
  16. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/check_pypi.py +0 -0
  17. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/client.py +0 -0
  18. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/config.py +0 -0
  19. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/context.py +0 -0
  20. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/datafile/common.py +0 -0
  21. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/datafile/exceptions.py +0 -0
  22. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/datafile/parse_connection.py +0 -0
  23. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/datafile/parse_datasource.py +0 -0
  24. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/datafile/parse_pipe.py +0 -0
  25. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/datatypes.py +0 -0
  26. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/feedback_manager.py +0 -0
  27. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/git_settings.py +0 -0
  28. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/iterating/__init__.py +0 -0
  29. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/iterating/data_branch_modes.py +0 -0
  30. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/prompts.py +0 -0
  31. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/service_datasources.py +0 -0
  32. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/sql.py +0 -0
  33. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/sql_template_fmt.py +0 -0
  34. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/sql_toolset.py +0 -0
  35. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/syncasync.py +0 -0
  36. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/check_pypi.py +0 -0
  37. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/cli.py +0 -0
  38. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/client.py +0 -0
  39. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/config.py +0 -0
  40. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/branch.py +0 -0
  41. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/build_common.py +0 -0
  42. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/cicd.py +0 -0
  43. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/cli.py +0 -0
  44. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/common.py +0 -0
  45. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/connection.py +0 -0
  46. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/connection_dynamodb.py +0 -0
  47. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/connection_kafka.py +0 -0
  48. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/connection_s3.py +0 -0
  49. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/copy.py +0 -0
  50. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/create.py +0 -0
  51. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/datafile/build.py +0 -0
  52. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/datafile/build_common.py +0 -0
  53. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/datafile/build_datasource.py +0 -0
  54. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/datafile/build_pipe.py +0 -0
  55. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/datafile/diff.py +0 -0
  56. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/datafile/fixture.py +0 -0
  57. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/datafile/format_common.py +0 -0
  58. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/datafile/format_connection.py +0 -0
  59. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/datafile/format_datasource.py +0 -0
  60. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/datafile/format_pipe.py +0 -0
  61. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/datafile/pipe_checker.py +0 -0
  62. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/datafile/pull.py +0 -0
  63. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/datasource.py +0 -0
  64. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/deployment.py +0 -0
  65. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/deployment_common.py +0 -0
  66. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/deprecations.py +0 -0
  67. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/endpoint.py +0 -0
  68. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/exceptions.py +0 -0
  69. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/feedback_manager.py +0 -0
  70. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/fmt.py +0 -0
  71. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/info.py +0 -0
  72. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/infra.py +0 -0
  73. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/job.py +0 -0
  74. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/job_common.py +0 -0
  75. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/llm.py +0 -0
  76. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/llm_utils.py +0 -0
  77. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/local.py +0 -0
  78. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/local_logs.py +0 -0
  79. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/login.py +0 -0
  80. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/logout.py +0 -0
  81. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/logs.py +0 -0
  82. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/materialization.py +0 -0
  83. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/open.py +0 -0
  84. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/pipe.py +0 -0
  85. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/preview.py +0 -0
  86. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/project.py +0 -0
  87. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/project_commands.py +0 -0
  88. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/py_project.py +0 -0
  89. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/query_output.py +0 -0
  90. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/regions.py +0 -0
  91. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/secret.py +0 -0
  92. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/secret_common.py +0 -0
  93. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/sink.py +0 -0
  94. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/table.py +0 -0
  95. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/telemetry.py +0 -0
  96. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/test.py +0 -0
  97. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/test_common.py +0 -0
  98. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/tinyunit/tinyunit.py +0 -0
  99. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/token.py +0 -0
  100. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/ts_project.py +0 -0
  101. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/workspace.py +0 -0
  102. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb/modules/workspace_members.py +0 -0
  103. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb_cli.py +0 -0
  104. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb_cli_modules/auth.py +0 -0
  105. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb_cli_modules/branch.py +0 -0
  106. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb_cli_modules/cicd.py +0 -0
  107. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb_cli_modules/cli.py +0 -0
  108. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb_cli_modules/common.py +0 -0
  109. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb_cli_modules/connection.py +0 -0
  110. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb_cli_modules/datasource.py +0 -0
  111. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb_cli_modules/exceptions.py +0 -0
  112. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb_cli_modules/fmt.py +0 -0
  113. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb_cli_modules/job.py +0 -0
  114. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb_cli_modules/pipe.py +0 -0
  115. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb_cli_modules/regions.py +0 -0
  116. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb_cli_modules/tag.py +0 -0
  117. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb_cli_modules/telemetry.py +0 -0
  118. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb_cli_modules/test.py +0 -0
  119. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb_cli_modules/tinyunit/tinyunit.py +0 -0
  120. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb_cli_modules/workspace.py +0 -0
  121. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tb_cli_modules/workspace_members.py +0 -0
  122. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird/tornado_template.py +0 -0
  123. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird.egg-info/SOURCES.txt +0 -0
  124. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird.egg-info/dependency_links.txt +0 -0
  125. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird.egg-info/entry_points.txt +0 -0
  126. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/tinybird.egg-info/requires.txt +0 -0
  127. {tinybird-4.6.6.dev0 → tinybird-4.6.7}/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.6.dev0
3
+ Version: 4.6.7
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,18 @@ The Tinybird command-line tool allows you to use all the Tinybird functionality
52
52
  Changelog
53
53
  ----------
54
54
 
55
+ 4.6.7.
56
+ *******
57
+
58
+ - `Fixed` `tb login` now raises an actionable error when the CLI cannot complete the authentication.
59
+
60
+ 4.6.6
61
+ *******
62
+
63
+ - `Fixed` `tb local` now starts the Tinybird Local container using the host's native architecture (``linux/arm64`` on Apple Silicon, ``linux/amd64`` elsewhere) instead of always forcing ``linux/amd64``, so it no longer runs under emulation on arm64 machines.
64
+ - `Added` ``TB_LOCAL_IMAGE_PLATFORM`` environment variable to override the auto-detected image platform (e.g. ``linux/amd64`` to run the amd64 image emulated on an arm64 host, or to run a locally built single-arch image that does not match the host).
65
+ - `Fixed` `tb local start` new-version detection now normalizes Docker Hub image digests and no longer pulls when the registry is unreachable.
66
+
55
67
  4.6.5
56
68
  *******
57
69
 
@@ -1953,11 +1953,12 @@ def get_var_data(content, node_id=None):
1953
1953
  """
1954
1954
 
1955
1955
  def node_to_value(x):
1956
- if type(x) in (ast.Bytes, ast.Str):
1957
- return x.s
1958
- elif type(x) == ast.Num: # noqa: E721
1959
- return x.n
1960
- elif type(x) == ast.NameConstant: # noqa: E721
1956
+ # ast.Constant supersedes the deprecated ast.Bytes/ast.Str/ast.Num/
1957
+ # ast.NameConstant node classes. Those were aliases of ast.Constant
1958
+ # since Python 3.8 (emitting DeprecationWarning on access in 3.12) and
1959
+ # were removed in Python 3.14. The parser only ever produces
1960
+ # ast.Constant nodes, so .value holds str/bytes/int/float/bool/None.
1961
+ if type(x) == ast.Constant: # noqa: E721
1961
1962
  return x.value
1962
1963
  elif type(x) == ast.Name: # noqa: E721
1963
1964
  return x.id
@@ -1971,8 +1972,6 @@ def get_var_data(content, node_id=None):
1971
1972
  if not r:
1972
1973
  r = node_to_value(x.right)
1973
1974
  return r
1974
- elif type(x) == ast.Constant: # noqa: E721
1975
- return x.value
1976
1975
  elif type(x) == ast.UnaryOp and type(x.operand) == ast.Constant: # noqa: E721
1977
1976
  if type(x.op) == ast.USub: # noqa: E721
1978
1977
  return x.operand.value * -1
@@ -1991,11 +1990,9 @@ def get_var_data(content, node_id=None):
1991
1990
  return []
1992
1991
 
1993
1992
  first_elem = x.elts[0]
1994
- if type(first_elem) in (ast.Bytes, ast.Str):
1995
- return [elem.s for elem in x.elts]
1996
- elif type(first_elem) == ast.Num: # noqa: E721
1997
- return [elem.n for elem in x.elts]
1998
- elif type(first_elem) == ast.NameConstant or type(first_elem) == ast.Constant: # noqa: E721
1993
+ # See node_to_value: ast.Constant supersedes the deprecated
1994
+ # ast.Bytes/ast.Str/ast.Num/ast.NameConstant (removed in 3.14).
1995
+ if type(first_elem) == ast.Constant: # noqa: E721
1999
1996
  return [elem.value for elem in x.elts]
2000
1997
  elif type(first_elem) == ast.Name: # noqa: E721
2001
1998
  return [elem.id for elem in x.elts]
@@ -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.6.dev0'
8
- __revision__ = '939e465'
7
+ __version__ = '4.6.7'
8
+ __revision__ = '1177944'
@@ -1,22 +1,15 @@
1
- from copy import deepcopy
2
1
  from functools import partial
3
2
  from pathlib import Path
4
- from typing import Any, Callable, Dict, List, Optional
5
- from urllib.parse import urlencode
3
+ from typing import Any, Callable, Dict, Optional
6
4
 
7
5
  import click
8
6
 
9
- from tinybird.datafile.exceptions import ParseException
10
- from tinybird.datafile.parse_datasource import parse_datasource
11
- from tinybird.datafile.parse_pipe import parse_pipe
12
7
  from tinybird.tb.client import TinyB
13
8
  from tinybird.tb.config import CLOUD_HOSTS
14
9
  from tinybird.tb.modules.build_common import process
15
10
  from tinybird.tb.modules.cli import cli, get_current_git_branch
16
- from tinybird.tb.modules.config import CLIConfig
17
11
  from tinybird.tb.modules.feedback_manager import FeedbackManager
18
12
  from tinybird.tb.modules.project import Project
19
- from tinybird.tb.modules.query_output import print_table_formatted
20
13
  from tinybird.tb.modules.watch import watch_project
21
14
 
22
15
 
@@ -182,41 +175,3 @@ def is_endpoint(f: Path) -> bool:
182
175
 
183
176
  def is_pipe(f: Path) -> bool:
184
177
  return f.suffix == ".pipe" and not is_vendor(f)
185
-
186
-
187
- def check_filenames(filenames: List[str]):
188
- parser_matrix = {".pipe": parse_pipe, ".datasource": parse_datasource}
189
- incl_suffix = ".incl"
190
-
191
- for filename in filenames:
192
- file_suffix = Path(filename).suffix
193
- if file_suffix == incl_suffix:
194
- continue
195
-
196
- parser = parser_matrix.get(file_suffix)
197
- if not parser:
198
- raise ParseException(FeedbackManager.error_unsupported_datafile(extension=file_suffix))
199
-
200
- parser(filename)
201
-
202
-
203
- def build_and_print_resource(config: CLIConfig, tb_client: TinyB, filename: str):
204
- resource_path = Path(filename)
205
- name = resource_path.stem
206
- playground_name = name if filename.endswith(".pipe") else None
207
- user_client = deepcopy(tb_client)
208
- user_client.token = config.get_user_token() or ""
209
- cli_params = {}
210
- cli_params["workspace_id"] = config.get("id", None)
211
- data = user_client._req(f"/v0/playgrounds?{urlencode(cli_params)}")
212
- playgrounds = data["playgrounds"]
213
- playground = next((p for p in playgrounds if p["name"] == (f"{playground_name}" + "__tb__playground")), None)
214
- if not playground:
215
- return
216
- playground_id = playground["id"]
217
- last_node = playground["nodes"][-1]
218
- if not last_node:
219
- return
220
- node_sql = last_node["sql"]
221
- res = tb_client.query(f"{node_sql} FORMAT JSON", playground=playground_id)
222
- print_table_formatted(res, name)
@@ -197,9 +197,6 @@ class CLIConfig:
197
197
  netloc: str = url_info.netloc.decode() if isinstance(url_info.netloc, bytes) else url_info.netloc
198
198
  self["host"] = f"{scheme}://{netloc}"
199
199
 
200
- def set_cwd(self, cwd: str) -> None:
201
- self["cwd"] = cwd
202
-
203
200
  def get_host(self, use_defaults_if_needed: bool = False) -> Optional[str]:
204
201
  result: Optional[str] = self.get("host", None)
205
202
  if result:
@@ -9,7 +9,6 @@ from pathlib import Path
9
9
  from typing import Any, Dict, Iterable, List, Optional, Set, Tuple
10
10
 
11
11
  import click
12
- from toposort import toposort
13
12
 
14
13
  from tinybird.datafile.common import (
15
14
  DEFAULT_CRON_PERIOD,
@@ -27,7 +26,7 @@ from tinybird.datafile.common import (
27
26
  get_project_filenames,
28
27
  pp,
29
28
  )
30
- from tinybird.datafile.exceptions import AlreadyExistsException, IncludeFileNotFoundException
29
+ from tinybird.datafile.exceptions import IncludeFileNotFoundException
31
30
  from tinybird.datafile.parse_datasource import parse_datasource
32
31
  from tinybird.datafile.parse_pipe import parse_pipe
33
32
  from tinybird.sql import parse_table_structure, schema_to_sql_columns
@@ -35,339 +34,12 @@ from tinybird.sql_template import get_used_tables_in_template, render_sql_templa
35
34
  from tinybird.tb.client import TinyB
36
35
  from tinybird.tb.modules.common import get_ca_pem_content
37
36
  from tinybird.tb.modules.config import CLIConfig
38
- from tinybird.tb.modules.datafile.build_datasource import is_datasource
39
37
  from tinybird.tb.modules.datafile.build_pipe import (
40
38
  get_target_materialized_data_source_name,
41
- is_endpoint,
42
39
  is_endpoint_with_no_dependencies,
43
- is_materialized,
44
40
  new_pipe,
45
41
  )
46
42
  from tinybird.tb.modules.feedback_manager import FeedbackManager, get_cli_name
47
- from tinybird.tb.modules.project import Project
48
-
49
-
50
- def folder_playground(
51
- project: Project,
52
- config: CLIConfig,
53
- tb_client: TinyB,
54
- filenames: Optional[List[str]] = None,
55
- is_internal: bool = False,
56
- current_ws: Optional[Dict[str, Any]] = None,
57
- local_ws: Optional[Dict[str, Any]] = None,
58
- ):
59
- build = True
60
- dry_run = False
61
- force = True
62
- only_changes = True
63
- debug = False
64
- run_tests = False
65
- verbose = False
66
- raise_on_exists = False
67
- fork_downstream = True
68
- fork = False
69
- release_created = False
70
- folder = str(project.path)
71
- datasources: List[Dict[str, Any]] = tb_client.datasources()
72
- pipes: List[Dict[str, Any]] = tb_client.pipes(dependencies=True)
73
- build = True
74
- dry_run = False
75
- force = True
76
- only_changes = True
77
- debug = False
78
- check = True
79
- populate = False
80
- populate_subset = None
81
- populate_condition = None
82
- tests_to_run = 0
83
- override_datasource = False
84
- skip_confirmation = True
85
- wait = False
86
- unlink_on_populate_error = False
87
- only_response_times = False
88
- run_tests = False
89
- verbose = False
90
- as_standard = False
91
- raise_on_exists = False
92
- fork_downstream = True
93
- fork = False
94
- release_created = False
95
- tests_relative_change = 0.01
96
- tests_sample_by_params = 0
97
- tests_filter_by = None
98
- tests_failfast = False
99
- tests_ignore_order = False
100
- tests_validate_processed_bytes = False
101
- tests_check_requests_from_branch = False
102
- user_token = None
103
- ignore_sql_errors = False
104
- is_vendor = False
105
- current_ws = current_ws or local_ws
106
-
107
- existing_resources: List[str] = [x["name"] for x in datasources] + [x["name"] for x in pipes]
108
- remote_resource_names = [get_remote_resource_name_without_version(x) for x in existing_resources]
109
-
110
- if not filenames:
111
- filenames = get_project_filenames(folder)
112
-
113
- # build graph to get new versions for all the files involved in the query
114
- # dependencies need to be processed always to get the versions
115
- dependencies_graph = build_graph(
116
- filenames,
117
- tb_client,
118
- dir_path=folder,
119
- process_dependencies=True,
120
- skip_connectors=True,
121
- vendor_paths=[],
122
- current_ws=current_ws,
123
- only_changes=only_changes,
124
- fork_downstream=fork_downstream,
125
- is_internal=is_internal,
126
- build=build,
127
- )
128
-
129
- if debug:
130
- pp.pprint(dependencies_graph.to_run)
131
-
132
- def should_push_file(
133
- name: str,
134
- remote_resource_names: List[str],
135
- force: bool,
136
- run_tests: bool,
137
- ) -> bool:
138
- """
139
- Function to know if we need to run a file or not
140
- """
141
- if name not in remote_resource_names:
142
- return True
143
- # When we need to try to push a file when it doesn't exist and the version is different that the existing one
144
- resource_full_name = name
145
- if resource_full_name not in existing_resources:
146
- return True
147
- return force or run_tests
148
-
149
- def push(
150
- name: str,
151
- to_run: Dict[str, Dict[str, Any]],
152
- dry_run: bool,
153
- fork_downstream: Optional[bool] = False,
154
- fork: Optional[bool] = False,
155
- ):
156
- if name in to_run:
157
- resource = to_run[name]["resource"]
158
- if resource == "datasources":
159
- return
160
- if not dry_run:
161
- if should_push_file(name, remote_resource_names, force, run_tests):
162
- click.echo(FeedbackManager.info_processing_new_resource(name=name, version=""))
163
- try:
164
- exec_file(
165
- to_run[name],
166
- config,
167
- tb_client,
168
- force,
169
- check,
170
- debug and verbose,
171
- populate,
172
- populate_subset,
173
- populate_condition,
174
- unlink_on_populate_error,
175
- wait,
176
- user_token,
177
- override_datasource,
178
- ignore_sql_errors,
179
- skip_confirmation,
180
- only_response_times,
181
- run_tests,
182
- as_standard,
183
- tests_to_run,
184
- tests_relative_change,
185
- tests_sample_by_params,
186
- tests_filter_by,
187
- tests_failfast,
188
- tests_ignore_order,
189
- tests_validate_processed_bytes,
190
- tests_check_requests_from_branch,
191
- current_ws,
192
- local_ws,
193
- fork_downstream,
194
- fork,
195
- build,
196
- is_vendor,
197
- )
198
- if not run_tests:
199
- click.echo(
200
- FeedbackManager.success_create(
201
- name=(
202
- name
203
- if to_run[name]["version"] is None
204
- else f"{name}__v{to_run[name]['version']}"
205
- )
206
- )
207
- )
208
- except Exception as e:
209
- filename = to_run[name]["filename"]
210
- exception = FeedbackManager.error_push_file_exception(
211
- filename=filename,
212
- error=e,
213
- )
214
- raise click.ClickException(exception)
215
- elif raise_on_exists:
216
- raise AlreadyExistsException(
217
- FeedbackManager.warning_name_already_exists(
218
- name=name if to_run[name]["version"] is None else f"{name}__v{to_run[name]['version']}"
219
- )
220
- )
221
- elif name_matches_existing_resource(resource, name, tb_client):
222
- if resource == "pipes":
223
- click.echo(FeedbackManager.error_pipe_cannot_be_pushed(name=name))
224
- else:
225
- click.echo(FeedbackManager.error_datasource_cannot_be_pushed(name=name))
226
- else:
227
- click.echo(
228
- FeedbackManager.warning_name_already_exists(
229
- name=(name if to_run[name]["version"] is None else f"{name}__v{to_run[name]['version']}")
230
- )
231
- )
232
- if dry_run:
233
- if should_push_file(name, remote_resource_names, force, run_tests):
234
- extension = "pipe" if resource == "pipes" else "datasource"
235
- click.echo(FeedbackManager.info_building_resource(name=f"{name}.{extension}", version=""))
236
- elif name_matches_existing_resource(resource, name, tb_client):
237
- if resource == "pipes":
238
- click.echo(FeedbackManager.warning_pipe_cannot_be_pushed(name=name))
239
- else:
240
- click.echo(FeedbackManager.warning_datasource_cannot_be_pushed(name=name))
241
- else:
242
- click.echo(FeedbackManager.warning_dry_name_already_exists(name=name))
243
-
244
- def push_files(
245
- dependency_graph: GraphDependencies,
246
- dry_run: bool = False,
247
- ):
248
- endpoints_dep_map = dict()
249
- processed = set()
250
-
251
- resources_to_run = dependency_graph.to_run
252
-
253
- # This will generate the graph from right to left and will fill the gaps of the dependencies
254
- # If we have a graph like this:
255
- # A -> B -> C
256
- # If we only modify A, the normal dependencies graph will only contain a node like _{A => B}
257
- # But we need a graph that contains A, B and C and the dependencies between them to deploy them in the right order
258
- dependencies_graph_fork_downstream, resources_to_run_fork_downstream = generate_forkdownstream_graph(
259
- dependency_graph.all_dep_map,
260
- dependency_graph.all_resources,
261
- resources_to_run,
262
- list(dependency_graph.dep_map.keys()),
263
- )
264
-
265
- # First, we will deploy the datasources that need to be deployed.
266
- # We need to deploy the datasources from left to right as some datasources might have MV that depend on the column types of previous datasources. Ex: `test_change_column_type_landing_datasource` test
267
- groups = [group for group in toposort(dependencies_graph_fork_downstream)]
268
-
269
- groups.reverse()
270
- for group in groups:
271
- for name in group:
272
- if name in processed or not is_datasource(resources_to_run_fork_downstream[name]):
273
- continue
274
-
275
- # If we are trying to modify a Kafka or CDK datasource, we need to inform the user that the resource needs to be post-released
276
- kafka_connection_name = (
277
- resources_to_run_fork_downstream[name].get("params", {}).get("kafka_connection_name")
278
- )
279
- service = resources_to_run_fork_downstream[name].get("params", {}).get("import_service")
280
- if release_created and (kafka_connection_name or service):
281
- connector = "Kafka" if kafka_connection_name else service
282
- error_msg = FeedbackManager.error_connector_require_post_release(connector=connector)
283
- raise click.ClickException(error_msg)
284
-
285
- push(
286
- name,
287
- resources_to_run_fork_downstream,
288
- dry_run,
289
- fork_downstream,
290
- fork,
291
- )
292
- processed.add(name)
293
-
294
- # Now, we will create a map of all the endpoints and there dependencies
295
- # We are using the forkdownstream graph to get the dependencies of the endpoints as the normal dependencies graph only contains the resources that are going to be deployed
296
- # But does not include the missing gaps
297
- # If we have ENDPOINT_A ----> MV_PIPE_B -----> DATASOURCE_B ------> ENDPOINT_C
298
- # Where endpoint A is being used in the MV_PIPE_B, if we only modify the endpoint A
299
- # The dependencies graph will only contain the endpoint A and the MV_PIPE_B, but not the DATASOURCE_B and the ENDPOINT_C
300
- groups = [group for group in toposort(dependencies_graph_fork_downstream)]
301
- for group in groups:
302
- for name in group:
303
- if name in processed or not is_endpoint(resources_to_run_fork_downstream[name]):
304
- continue
305
-
306
- endpoints_dep_map[name] = dependencies_graph_fork_downstream[name]
307
-
308
- # Now that we have the dependencies of the endpoints, we need to check that the resources has not been deployed yet and only care about the endpoints that depend on endpoints
309
- groups = [group for group in toposort(endpoints_dep_map)]
310
-
311
- # As we have used the forkdownstream graph to get the dependencies of the endpoints, we have all the dependencies of the endpoints
312
- # But we need to deploy the endpoints and the dependencies of the endpoints from left to right
313
- # So we need to reverse the groups
314
- groups.reverse()
315
- for group in groups:
316
- for name in group:
317
- if name in processed or not is_endpoint(resources_to_run_fork_downstream[name]):
318
- continue
319
-
320
- push(
321
- name,
322
- resources_to_run_fork_downstream,
323
- dry_run,
324
- fork_downstream,
325
- fork,
326
- )
327
- processed.add(name)
328
-
329
- # Now we should have the endpoints and datasources deployed, we can deploy the rest of the pipes (copy & sinks)
330
- # We need to rely on the forkdownstream graph as it contains all the modified pipes as well as the dependencies of the pipes
331
- # In this case, we don't need to generate a new graph as we did for the endpoints as the pipes are not going to be used as dependencies and the datasources are already deployed
332
- groups = [group for group in toposort(dependencies_graph_fork_downstream)]
333
- for group in groups:
334
- for name in group:
335
- if name in processed or is_materialized(resources_to_run_fork_downstream.get(name)):
336
- continue
337
-
338
- push(
339
- name,
340
- resources_to_run_fork_downstream,
341
- dry_run,
342
- fork_downstream,
343
- fork,
344
- )
345
- processed.add(name)
346
-
347
- # Finally, we need to deploy the materialized views from right to left.
348
- # We need to rely on the forkdownstream graph as it contains all the modified materialized views as well as the dependencies of the materialized views
349
- # In this case, we don't need to generate a new graph as we did for the endpoints as the pipes are not going to be used as dependencies and the datasources are already deployed
350
- groups = [group for group in toposort(dependencies_graph_fork_downstream)]
351
- for group in groups:
352
- for name in group:
353
- if name in processed or not is_materialized(resources_to_run_fork_downstream.get(name)):
354
- continue
355
-
356
- push(
357
- name,
358
- resources_to_run_fork_downstream,
359
- dry_run,
360
- fork_downstream,
361
- fork,
362
- )
363
- processed.add(name)
364
-
365
- push_files(dependencies_graph, dry_run)
366
-
367
- if not dry_run and not run_tests and verbose:
368
- click.echo(FeedbackManager.info_not_pushing_fixtures())
369
-
370
- return dependencies_graph.to_run
371
43
 
372
44
 
373
45
  def name_matches_existing_resource(resource: str, name: str, tb_client: TinyB):