tinybird 0.0.1.dev292__py3-none-any.whl → 0.0.1.dev293__py3-none-any.whl

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.

Potentially problematic release.


This version of tinybird might be problematic. Click here for more details.

@@ -11,7 +11,7 @@ from tinybird.datafile.common import (
11
11
  parse,
12
12
  )
13
13
  from tinybird.datafile.exceptions import IncludeFileNotFoundException, ParseException
14
- from tinybird.sql_template import get_template_and_variables, render_sql_template, secret_template_key
14
+ from tinybird.sql_template import get_template_and_variables, render_sql_template
15
15
  from tinybird.tb.modules.feedback_manager import FeedbackManager
16
16
  from tinybird.tornado_template import UnClosedIfError
17
17
 
@@ -54,7 +54,7 @@ def parse_pipe(
54
54
  if sql.strip()[0] == "%":
55
55
  secrets_list: Optional[List[str]] = None
56
56
  if secrets:
57
- secrets_list = [f"{secret_template_key(secret)}" for secret in secrets.keys()]
57
+ secrets_list = list(secrets.keys())
58
58
  # Setting test_mode=True to ignore errors on required parameters and
59
59
  # secrets_in_test_mode=False to raise errors on missing secrets
60
60
  sql, _, variable_warnings = render_sql_template(
tinybird/sql_template.py CHANGED
@@ -1405,8 +1405,11 @@ def generate(self, **kwargs) -> Tuple[str, TemplateExecutionResults]:
1405
1405
  namespace = {}
1406
1406
  template_execution_results = TemplateExecutionResults()
1407
1407
  for key in kwargs.get("tb_secrets", []):
1408
+ # Avoid double-prefixing if the key already has the tb_secret_ prefix
1408
1409
  if is_secret_template_key(key):
1409
1410
  template_execution_results.add_template_param(key)
1411
+ else:
1412
+ template_execution_results.add_template_param(secret_template_key(key))
1410
1413
 
1411
1414
  if TB_SECRET_IN_TEST_MODE in kwargs:
1412
1415
  template_execution_results[TB_SECRET_IN_TEST_MODE] = None
@@ -1415,15 +1418,20 @@ def generate(self, **kwargs) -> Tuple[str, TemplateExecutionResults]:
1415
1418
  try:
1416
1419
  key = secret_template_key(x)
1417
1420
  if key in template_execution_results.template_params:
1421
+ # secret available: Always use workspace secret regardless of test mode
1418
1422
  template_execution_results.add_ch_param(x)
1419
1423
  return Symbol("{" + sqlescape(x) + ": String}")
1420
1424
  else:
1425
+ # secret not available: Check test mode and defaults
1421
1426
  is_test_mode = TB_SECRET_IN_TEST_MODE in template_execution_results
1422
- if is_test_mode and default is None:
1423
- return Symbol("{" + sqlescape(x) + ": String}")
1424
- elif default is not None:
1427
+ if default is not None:
1428
+ # Use provided default value
1425
1429
  return default
1430
+ elif is_test_mode:
1431
+ # In test mode without default - return placeholder
1432
+ return Symbol("{" + sqlescape(x) + ": String}")
1426
1433
  else:
1434
+ # Not in test mode, no secret, no default - raise error
1427
1435
  raise SQLTemplateException(
1428
1436
  f"Cannot access secret '{x}'. Check the secret exists in the Workspace and the token has the required scope."
1429
1437
  )
tinybird/tb/__cli__.py CHANGED
@@ -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__ = '0.0.1.dev292'
8
- __revision__ = '78dba1d'
7
+ __version__ = '0.0.1.dev293'
8
+ __revision__ = '8c3de05'
@@ -3,7 +3,7 @@ import time
3
3
  from copy import deepcopy
4
4
  from functools import partial
5
5
  from pathlib import Path
6
- from typing import Any, Callable, Dict, List
6
+ from typing import Any, Callable, Dict, List, Optional
7
7
  from urllib.parse import urlencode
8
8
 
9
9
  import click
@@ -19,6 +19,7 @@ from tinybird.tb.modules.config import CLIConfig
19
19
  from tinybird.tb.modules.datafile.playground import folder_playground
20
20
  from tinybird.tb.modules.dev_server import BuildStatus, start_server
21
21
  from tinybird.tb.modules.feedback_manager import FeedbackManager
22
+ from tinybird.tb.modules.local_common import get_local_tokens
22
23
  from tinybird.tb.modules.project import Project
23
24
  from tinybird.tb.modules.shell import Shell, print_table_formatted
24
25
  from tinybird.tb.modules.watch import watch_files, watch_project
@@ -34,6 +35,7 @@ def build(ctx: click.Context, watch: bool) -> None:
34
35
  obj: Dict[str, Any] = ctx.ensure_object(dict)
35
36
  project: Project = ctx.ensure_object(dict)["project"]
36
37
  tb_client: TinyB = ctx.ensure_object(dict)["client"]
38
+ config: Dict[str, Any] = ctx.ensure_object(dict)["config"]
37
39
  if obj["env"] == "cloud":
38
40
  raise click.ClickException(FeedbackManager.error_build_only_supported_in_local())
39
41
 
@@ -46,6 +48,12 @@ def build(ctx: click.Context, watch: bool) -> None:
46
48
  )
47
49
  )
48
50
 
51
+ # First, build vendored workspaces if present
52
+
53
+ build_vendored_workspaces(project=project, tb_client=tb_client, config=config)
54
+ # Ensure SHARED_WITH workspaces exist before building and sharing
55
+ build_shared_with_workspaces(project=project, tb_client=tb_client, config=config)
56
+
49
57
  click.echo(FeedbackManager.highlight_building_project())
50
58
  process(project=project, tb_client=tb_client, watch=False)
51
59
  if watch:
@@ -65,6 +73,7 @@ def dev(ctx: click.Context, data_origin: str, ui: bool) -> None:
65
73
  return dev_cloud(ctx)
66
74
  project: Project = ctx.ensure_object(dict)["project"]
67
75
  tb_client: TinyB = ctx.ensure_object(dict)["client"]
76
+ config: Dict[str, Any] = ctx.ensure_object(dict)["config"]
68
77
  build_status = BuildStatus()
69
78
  if ui:
70
79
  server_thread = threading.Thread(
@@ -74,6 +83,11 @@ def dev(ctx: click.Context, data_origin: str, ui: bool) -> None:
74
83
  # Wait for the server to start
75
84
  time.sleep(0.5)
76
85
 
86
+ # Build vendored workspaces before starting dev build/watch
87
+ build_vendored_workspaces(project=project, tb_client=tb_client, config=config)
88
+ # Ensure SHARED_WITH workspaces exist before dev build/watch
89
+ build_shared_with_workspaces(project=project, tb_client=tb_client, config=config)
90
+
77
91
  click.echo(FeedbackManager.highlight_building_project())
78
92
  process(project=project, tb_client=tb_client, watch=True, build_status=build_status)
79
93
  run_watch(
@@ -127,6 +141,134 @@ def check_filenames(filenames: List[str]):
127
141
  parser(filename)
128
142
 
129
143
 
144
+ def find_workspace_or_create(user_client: TinyB, workspace_name: str) -> Optional[str]:
145
+ # Get a client scoped to the vendored workspace using the user token
146
+ ws_token = None
147
+ org_id = None
148
+ try:
149
+ # Fetch org id and workspaces with tokens
150
+ info = user_client.user_workspaces_with_organization(version="v1")
151
+ org_id = info.get("organization_id")
152
+ workspaces = info.get("workspaces", [])
153
+ found = next((w for w in workspaces if w.get("name") == workspace_name), None)
154
+ if found:
155
+ ws_token = found.get("token")
156
+ # If still not found, try the generic listing
157
+ if not ws_token:
158
+ workspaces_full = user_client.user_workspaces_and_branches(version="v1")
159
+ created_ws = next(
160
+ (w for w in workspaces_full.get("workspaces", []) if w.get("name") == workspace_name), None
161
+ )
162
+ if created_ws:
163
+ ws_token = created_ws.get("token")
164
+ except Exception:
165
+ ws_token = None
166
+
167
+ # If workspace doesn't exist, try to create it and fetch its token
168
+ if not ws_token:
169
+ try:
170
+ user_client.create_workspace(workspace_name, assign_to_organization_id=org_id, version="v1")
171
+ # Fetch token for newly created workspace
172
+ info_after = user_client.user_workspaces_and_branches(version="v1")
173
+ created = next((w for w in info_after.get("workspaces", []) if w.get("name") == workspace_name), None)
174
+ ws_token = created.get("token") if created else None
175
+ except Exception as e:
176
+ click.echo(
177
+ FeedbackManager.warning(
178
+ message=(f"Skipping vendored workspace '{workspace_name}': unable to create or resolve token ({e})")
179
+ )
180
+ )
181
+
182
+ return ws_token
183
+
184
+
185
+ def build_vendored_workspaces(project: Project, tb_client: TinyB, config: Dict[str, Any]) -> None:
186
+ """Build each vendored workspace under project.vendor_path if present.
187
+
188
+ Directory structure expected: vendor/<workspace_name>/<data_project_inside>
189
+ Each top-level directory under vendor is treated as a separate workspace
190
+ whose project files will be built using that workspace's token.
191
+ """
192
+ try:
193
+ vendor_root = Path(project.vendor_path)
194
+
195
+ if not vendor_root.exists() or not vendor_root.is_dir():
196
+ return
197
+
198
+ tokens = get_local_tokens()
199
+ user_token = tokens["user_token"]
200
+ user_client = deepcopy(tb_client)
201
+ user_client.token = user_token
202
+
203
+ # Iterate over vendored workspace folders
204
+ for ws_dir in sorted([p for p in vendor_root.iterdir() if p.is_dir()]):
205
+ workspace_name = ws_dir.name
206
+ ws_token = find_workspace_or_create(user_client, workspace_name)
207
+
208
+ if not ws_token:
209
+ click.echo(
210
+ FeedbackManager.warning(
211
+ message=f"Skipping vendored workspace '{workspace_name}': could not resolve token after creation"
212
+ )
213
+ )
214
+ continue
215
+
216
+ # Build using a client scoped to the vendor workspace token
217
+ vendor_client = deepcopy(tb_client)
218
+ vendor_client.token = ws_token
219
+ vendor_project = Project(folder=str(ws_dir), workspace_name=workspace_name, max_depth=project.max_depth)
220
+
221
+ # Do not exit on error to allow main project to continue
222
+ process(
223
+ project=vendor_project,
224
+ tb_client=vendor_client,
225
+ watch=False,
226
+ silent=False,
227
+ exit_on_error=True,
228
+ load_fixtures=True,
229
+ )
230
+ except Exception as e:
231
+ # Never break the main build due to vendored build errors
232
+ click.echo(FeedbackManager.error_exception(error=e))
233
+
234
+
235
+ def build_shared_with_workspaces(project: Project, tb_client: TinyB, config: Dict[str, Any]) -> None:
236
+ """Scan project for .datasource files and ensure SHARED_WITH workspaces exist."""
237
+
238
+ try:
239
+ # Gather SHARED_WITH workspace names from all .datasource files
240
+ datasource_files = project.get_datasource_files()
241
+ shared_ws_names = set()
242
+
243
+ for filename in datasource_files:
244
+ try:
245
+ doc = parse_datasource(filename).datafile
246
+ for ws_name in doc.shared_with or []:
247
+ shared_ws_names.add(ws_name)
248
+ except Exception:
249
+ # Ignore parse errors here; they'll be handled during the main process()
250
+ continue
251
+
252
+ if not shared_ws_names:
253
+ return
254
+
255
+ # Need a user token to list/create workspaces
256
+ tokens = get_local_tokens()
257
+ user_token = tokens.get("user_token")
258
+ if not user_token:
259
+ click.echo(FeedbackManager.info_skipping_shared_with_entry())
260
+ return
261
+
262
+ user_client = deepcopy(tb_client)
263
+ user_client.token = user_token
264
+
265
+ # Ensure each SHARED_WITH workspace exists
266
+ for ws_name in sorted(shared_ws_names):
267
+ find_workspace_or_create(user_client, ws_name)
268
+ except Exception as e:
269
+ click.echo(FeedbackManager.error_exception(error=e))
270
+
271
+
130
272
  def dev_cloud(
131
273
  ctx: click.Context,
132
274
  ) -> None:
@@ -1209,7 +1209,7 @@ STEP 3: ADD KEY TO SERVICE ACCOUNT
1209
1209
 
1210
1210
  debug_running_file = print_message("** Running {file}", bcolors.CGREY)
1211
1211
 
1212
- highlight_building_project = info_highlight_message("\n» Building project...")
1212
+ highlight_building_project = info_highlight_message("» Building project...")
1213
1213
 
1214
1214
  success = success_message("{message}")
1215
1215
  info = info_message("{message}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: tinybird
3
- Version: 0.0.1.dev292
3
+ Version: 0.0.1.dev293
4
4
  Summary: Tinybird Command Line Tool
5
5
  Home-page: https://www.tinybird.co/docs/forward/commands
6
6
  Author: Tinybird
@@ -6,7 +6,7 @@ tinybird/git_settings.py,sha256=Sw_8rGmribEFJ4Z_6idrVytxpFYk7ez8ei0qHULzs3E,3934
6
6
  tinybird/prompts.py,sha256=HoDv9TxPiP8v2XoGTWYxP133dK9CEbXVv4XE5IT339c,45483
7
7
  tinybird/service_datasources.py,sha256=o6Az3T6OvcChR_7GXRu7sJH173KzGg1Gv-dNd0bI_vY,46085
8
8
  tinybird/sql.py,sha256=7pkwQMUVy0CH5zFgLMZyCx1BeaJSQYC05EmOb9vO3Ls,48694
9
- tinybird/sql_template.py,sha256=kaF5pi-f2JiWSYXEF8JsU1OIxvdu2homHnw4MYjq0n8,101953
9
+ tinybird/sql_template.py,sha256=rNePArEmx5GemxW6GJAiNnln0NRWncGgFe0EbC8G2f4,102463
10
10
  tinybird/sql_template_fmt.py,sha256=KUHdj5rYCYm_rKKdXYSJAE9vIyXUQLB0YSZnUXHeBlY,10196
11
11
  tinybird/sql_toolset.py,sha256=Y2_fQrbk5GSNoRncAmRS_snpV61XQbiOy4uYkeF6pr4,19767
12
12
  tinybird/syncasync.py,sha256=IPnOx6lMbf9SNddN1eBtssg8vCLHMt76SuZ6YNYm-Yk,27761
@@ -17,13 +17,13 @@ tinybird/datafile/common.py,sha256=hr9Qv4Xot21l2dW6xKPa5_JUu8pnGuDNnVpDf8pgthc,1
17
17
  tinybird/datafile/exceptions.py,sha256=8rw2umdZjtby85QbuRKFO5ETz_eRHwUY5l7eHsy1wnI,556
18
18
  tinybird/datafile/parse_connection.py,sha256=tRyn2Rpr1TeWet5BXmMoQgaotbGdYep1qiTak_OqC5E,1825
19
19
  tinybird/datafile/parse_datasource.py,sha256=ssW8QeFSgglVFi3sDZj_HgkJiTJ2069v2JgqnH3CkDE,1825
20
- tinybird/datafile/parse_pipe.py,sha256=xf4m0Tw44QWJzHzAm7Z7FwUoUUtr7noMYjU1NiWnX0k,3880
21
- tinybird/tb/__cli__.py,sha256=-6WKEO5Hmmfh0zrBTi4mPaxkqDYeWDApz2022V9XsK8,247
20
+ tinybird/datafile/parse_pipe.py,sha256=8e9LMecSQVWHC4AXf8cdxoQ1nxUR4fTObYxTctO_EXQ,3816
21
+ tinybird/tb/__cli__.py,sha256=pu9JJjmy3RBdLvb1l9ZlYQN2pYAF28tMrRS8qOplXdI,247
22
22
  tinybird/tb/check_pypi.py,sha256=Gp0HkHHDFMSDL6nxKlOY51z7z1Uv-2LRexNTZSHHGmM,552
23
23
  tinybird/tb/cli.py,sha256=FdDFEIayjmsZEVsVSSvRiVYn_FHOVg_zWQzchnzfWho,1008
24
24
  tinybird/tb/client.py,sha256=IQRaInDjOwr9Fzaz3_xXc3aUGqh94tM2lew7IZbB9eM,53733
25
25
  tinybird/tb/config.py,sha256=mhMTGnMB5KcxGoh3dewIr2Jjsa6pHE183gCPAQWyp6o,3973
26
- tinybird/tb/modules/build.py,sha256=lSCbmrqDxqa8bG009qgp2nNRpQW7JWgSen7UQNCfLVE,7657
26
+ tinybird/tb/modules/build.py,sha256=u31cE8ao_OU32_gat0fDkVEn5dP6tRwsesr5Tnynd3I,13624
27
27
  tinybird/tb/modules/build_common.py,sha256=KGCtyY1JGexXXhcMVFI3e-gRavmRfCoZnfGPSTYHGxk,12962
28
28
  tinybird/tb/modules/cicd.py,sha256=0KLKccha9IP749QvlXBmzdWv1On3mFwMY4DUcJlBxiE,7326
29
29
  tinybird/tb/modules/cli.py,sha256=-hkwg2ZIWPtldkLkgdY7RYWacOJtkMx58eqqYzMoMfw,17793
@@ -39,7 +39,7 @@ tinybird/tb/modules/deprecations.py,sha256=rrszC1f_JJeJ8mUxGoCxckQTJFBCR8wREf4XX
39
39
  tinybird/tb/modules/dev_server.py,sha256=57FCKuWpErwYUYgHspYDkLWEm9F4pbvVOtMrFXX1fVU,10129
40
40
  tinybird/tb/modules/endpoint.py,sha256=ksRj6mfDb9Xv63PhTkV_uKSosgysHElqagg3RTt21Do,11958
41
41
  tinybird/tb/modules/exceptions.py,sha256=KEj3E-zN7Z28igvptlngXIjlHR9vAJk59Vs4zpwm4vA,5469
42
- tinybird/tb/modules/feedback_manager.py,sha256=xkZjwGOV50-oc0y9h58ei4MZ1mwOMOaN_kUcGrtNH48,78132
42
+ tinybird/tb/modules/feedback_manager.py,sha256=duigLI2UIeMzP5IOa7XUty23NRvNssm2ZAKicuun3Pg,78130
43
43
  tinybird/tb/modules/info.py,sha256=F5vY4kHS_kyO2uSBKac92HoOb447oDeRlzpwtAHTuKc,6872
44
44
  tinybird/tb/modules/infra.py,sha256=JE9oLIyF4bi_JBoe-BgZ5HhKp_lQgSihuSV1KIS02Qs,32709
45
45
  tinybird/tb/modules/job.py,sha256=wBsnu8UPTOha2rkLvucgmw4xYv73ubmui3eeSIF68ZM,3107
@@ -121,8 +121,8 @@ tinybird/tb_cli_modules/config.py,sha256=IsgdtFRnUrkY8-Zo32lmk6O7u3bHie1QCxLwgp4
121
121
  tinybird/tb_cli_modules/exceptions.py,sha256=pmucP4kTF4irIt7dXiG-FcnI-o3mvDusPmch1L8RCWk,3367
122
122
  tinybird/tb_cli_modules/regions.py,sha256=QjsL5H6Kg-qr0aYVLrvb1STeJ5Sx_sjvbOYO0LrEGMk,166
123
123
  tinybird/tb_cli_modules/telemetry.py,sha256=Hh2Io8ZPROSunbOLuMvuIFU4TqwWPmQTqal4WS09K1A,10449
124
- tinybird-0.0.1.dev292.dist-info/METADATA,sha256=d2RNeqvNyqjfXrpEIlGB9lV-Kh2KUu3bD8c99JGeGJQ,1845
125
- tinybird-0.0.1.dev292.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
126
- tinybird-0.0.1.dev292.dist-info/entry_points.txt,sha256=LwdHU6TfKx4Qs7BqqtaczEZbImgU7Abe9Lp920zb_fo,43
127
- tinybird-0.0.1.dev292.dist-info/top_level.txt,sha256=VqqqEmkAy7UNaD8-V51FCoMMWXjLUlR0IstvK7tJYVY,54
128
- tinybird-0.0.1.dev292.dist-info/RECORD,,
124
+ tinybird-0.0.1.dev293.dist-info/METADATA,sha256=t4Tsiss9S3-uN8On8FaKHdRoWv1uNYRYxwEAutvIY_A,1845
125
+ tinybird-0.0.1.dev293.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
126
+ tinybird-0.0.1.dev293.dist-info/entry_points.txt,sha256=LwdHU6TfKx4Qs7BqqtaczEZbImgU7Abe9Lp920zb_fo,43
127
+ tinybird-0.0.1.dev293.dist-info/top_level.txt,sha256=VqqqEmkAy7UNaD8-V51FCoMMWXjLUlR0IstvK7tJYVY,54
128
+ tinybird-0.0.1.dev293.dist-info/RECORD,,