tinybird 0.0.1.dev160__py3-none-any.whl → 0.0.1.dev162__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.

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.dev160'
8
- __revision__ = '8c1a42a'
7
+ __version__ = '0.0.1.dev162'
8
+ __revision__ = 'ccf549a'
@@ -17,7 +17,6 @@ from tinybird.tb.client import TinyB
17
17
  from tinybird.tb.modules.cli import cli
18
18
  from tinybird.tb.modules.common import push_data, sys_exit
19
19
  from tinybird.tb.modules.config import CLIConfig
20
- from tinybird.tb.modules.datafile.build import folder_build
21
20
  from tinybird.tb.modules.datafile.exceptions import ParseException
22
21
  from tinybird.tb.modules.datafile.fixture import FixtureExtension, get_fixture_dir, persist_fixture
23
22
  from tinybird.tb.modules.datafile.parse_datasource import parse_datasource
@@ -75,7 +74,7 @@ def dev(ctx: click.Context, data_origin: str, ui: bool) -> None:
75
74
  )
76
75
 
77
76
 
78
- def build_project(project: Project, tb_client: TinyB, file_changed: Optional[str] = None, silent: bool = False) -> None:
77
+ def build_project(project: Project, tb_client: TinyB, silent: bool = False) -> Optional[bool]:
79
78
  MULTIPART_BOUNDARY_DATA_PROJECT = "data_project://"
80
79
  DATAFILE_TYPE_TO_CONTENT_TYPE = {
81
80
  ".datasource": "text/plain",
@@ -95,7 +94,7 @@ def build_project(project: Project, tb_client: TinyB, file_changed: Optional[str
95
94
  project_files = project.get_project_files()
96
95
 
97
96
  if not project_files:
98
- return
97
+ return False
99
98
 
100
99
  for file_path in project_files:
101
100
  relative_path = str(Path(file_path).relative_to(project_path))
@@ -118,37 +117,40 @@ def build_project(project: Project, tb_client: TinyB, file_changed: Optional[str
118
117
  build_result = result.get("result")
119
118
  if build_result == "success":
120
119
  build = result.get("build")
121
- datasources = build.get("new_datasource_names", [])
122
- pipes = build.get("new_pipe_names", [])
123
- connections = build.get("new_data_connector_names", [])
124
-
125
- if not file_changed and not silent:
126
- for ds in datasources:
127
- ds_path_str: Optional[str] = next(
128
- (p for p in project_files if p.endswith(ds + ".datasource")), None
129
- )
130
- if ds_path_str:
131
- ds_path = Path(ds_path_str)
132
- ds_path_str = ds_path_str.replace(f"{project.folder}/", "")
133
- click.echo(FeedbackManager.info(message=f"✓ {ds_path_str} created"))
134
- for pipe in pipes:
135
- pipe_name = pipe
136
- pipe_path_str: Optional[str] = next(
137
- (p for p in project_files if p.endswith(pipe_name + ".pipe")), None
138
- )
139
- if pipe_path_str:
140
- pipe_path_str = pipe_path_str.replace(f"{project.folder}/", "")
141
- click.echo(FeedbackManager.info(message=f"✓ {pipe_path_str} created"))
142
-
143
- for connection in connections:
144
- connection_name = connection
145
- connection_path_str: Optional[str] = next(
146
- (p for p in project_files if p.endswith(connection_name + ".connection")), None
147
- )
148
- if connection_path_str:
149
- connection_path_str = connection_path_str.replace(f"{project.folder}/", "")
150
- click.echo(FeedbackManager.info(message=f" {connection_path_str} created"))
151
-
120
+ new_datasources = build.get("new_datasource_names", [])
121
+ new_pipes = build.get("new_pipe_names", [])
122
+ new_connections = build.get("new_data_connector_names", [])
123
+ changed_datasources = build.get("changed_datasource_names", [])
124
+ changed_pipes = build.get("changed_pipe_names", [])
125
+ changed_connections = build.get("changed_data_connector_names", [])
126
+ deleted_datasources = build.get("deleted_datasource_names", [])
127
+ deleted_pipes = build.get("deleted_pipe_names", [])
128
+ deleted_connections = build.get("deleted_data_connector_names", [])
129
+
130
+ no_changes = (
131
+ not new_datasources
132
+ and not changed_datasources
133
+ and not new_pipes
134
+ and not changed_pipes
135
+ and not new_connections
136
+ and not changed_connections
137
+ and not deleted_datasources
138
+ and not deleted_pipes
139
+ and not deleted_connections
140
+ )
141
+ if no_changes:
142
+ return False
143
+ else:
144
+ if not silent:
145
+ echo_changes(project, new_datasources, ".datasource", "created")
146
+ echo_changes(project, changed_datasources, ".datasource", "changed")
147
+ echo_changes(project, deleted_datasources, ".datasource", "deleted")
148
+ echo_changes(project, new_pipes, ".pipe", "created")
149
+ echo_changes(project, changed_pipes, ".pipe", "changed")
150
+ echo_changes(project, deleted_pipes, ".pipe", "deleted")
151
+ echo_changes(project, new_connections, ".connection", "created")
152
+ echo_changes(project, changed_connections, ".connection", "changed")
153
+ echo_changes(project, deleted_connections, ".connection", "deleted")
152
154
  try:
153
155
  for filename in project_files:
154
156
  if filename.endswith(".datasource"):
@@ -200,6 +202,8 @@ def build_project(project: Project, tb_client: TinyB, file_changed: Optional[str
200
202
  if error:
201
203
  raise click.ClickException(error)
202
204
 
205
+ return build_result
206
+
203
207
 
204
208
  def append_fixture(
205
209
  tb_client: TinyB,
@@ -285,6 +289,7 @@ def process(
285
289
  time_start = time.time()
286
290
  build_failed = False
287
291
  build_error: Optional[str] = None
292
+ build_result: Optional[bool] = None
288
293
  if build_status:
289
294
  if build_status.building:
290
295
  return build_status.error
@@ -296,7 +301,7 @@ def process(
296
301
  rebuild_fixture_sql(project, tb_client, file_changed)
297
302
  else:
298
303
  try:
299
- build_project(project, tb_client, file_changed, silent)
304
+ build_result = build_project(project, tb_client, silent)
300
305
  if build_status:
301
306
  build_status.building = False
302
307
  build_status.error = None
@@ -305,10 +310,8 @@ def process(
305
310
  build_error = str(e)
306
311
  build_failed = True
307
312
  try:
308
- if file_changed and not build_failed:
309
- asyncio.run(folder_build(project, tb_client, filenames=[file_changed]))
310
- if not build_status:
311
- show_data(tb_client, file_changed, diff)
313
+ if file_changed and not build_failed and not build_status:
314
+ show_data(tb_client, file_changed, diff)
312
315
  except Exception:
313
316
  pass
314
317
 
@@ -327,7 +330,10 @@ def process(
327
330
  return build_error
328
331
  else:
329
332
  if not silent:
330
- click.echo(FeedbackManager.success(message=f"\n✓ {rebuild_str} completed in {elapsed_time:.1f}s"))
333
+ if build_result == False: # noqa: E712
334
+ click.echo(FeedbackManager.info(message="No changes. Build skipped."))
335
+ else:
336
+ click.echo(FeedbackManager.success(message=f"\n✓ {rebuild_str} completed in {elapsed_time:.1f}s"))
331
337
 
332
338
  return None
333
339
 
@@ -473,3 +479,13 @@ async def build_and_print_resource(config: CLIConfig, tb_client: TinyB, filename
473
479
  node_sql = last_node["sql"]
474
480
  res = await tb_client.query(f"{node_sql} FORMAT JSON", playground=playground_id)
475
481
  print_table_formatted(res, name)
482
+
483
+
484
+ def echo_changes(project: Project, changes: List[str], extension: str, status: str):
485
+ for resource in changes:
486
+ path_str = next(
487
+ (p for p in project.get_project_files() if p.endswith(resource + extension)), resource + extension
488
+ )
489
+ if path_str:
490
+ path_str = path_str.replace(f"{project.folder}/", "")
491
+ click.echo(FeedbackManager.info(message=f"✓ {path_str} {status}"))
@@ -36,31 +36,7 @@ async def get_tinybird_local_config(config_obj: Dict[str, Any], test: bool = Fal
36
36
  """
37
37
  path = config_obj.get("path")
38
38
  config = CLIConfig.get_project_config()
39
-
40
- try:
41
- # ruff: noqa: ASYNC210
42
- tokens = requests.get(f"{TB_LOCAL_ADDRESS}/tokens").json()
43
- except Exception:
44
- try:
45
- # Check if tinybird-local is running with docker, in case it's a config issue
46
- output = subprocess.check_output(["docker", "ps"], text=True) # noqa: ASYNC221
47
- header_row = next((line for line in output.splitlines() if "CONTAINER ID" in line), "")
48
- tb_local_row = next(
49
- (line for line in output.splitlines() if TB_CONTAINER_NAME in line),
50
- f"{TB_CONTAINER_NAME} not found in output",
51
- )
52
- add_telemetry_event(
53
- "docker_debug",
54
- data={
55
- "docker_ps_output": header_row + tb_local_row,
56
- },
57
- )
58
- except Exception:
59
- pass
60
- raise CLILocalException(
61
- FeedbackManager.error(message="Tinybird local is not running. Please run `tb local start` first.")
62
- )
63
-
39
+ tokens = get_local_tokens()
64
40
  user_token = tokens["user_token"]
65
41
  admin_token = tokens["admin_token"]
66
42
  default_token = tokens["workspace_admin_token"]
@@ -118,3 +94,29 @@ def get_build_workspace_name(path: str) -> str:
118
94
  def get_test_workspace_name(path: str) -> str:
119
95
  folder_hash = hashlib.sha256(path.encode()).hexdigest()
120
96
  return f"Tinybird_Local_Test_{folder_hash}"
97
+
98
+
99
+ def get_local_tokens() -> Dict[str, str]:
100
+ try:
101
+ # ruff: noqa: ASYNC210
102
+ return requests.get(f"{TB_LOCAL_ADDRESS}/tokens").json()
103
+ except Exception:
104
+ try:
105
+ # Check if tinybird-local is running with docker, in case it's a config issue
106
+ output = subprocess.check_output(["docker", "ps"], text=True)
107
+ header_row = next((line for line in output.splitlines() if "CONTAINER ID" in line), "")
108
+ tb_local_row = next(
109
+ (line for line in output.splitlines() if TB_CONTAINER_NAME in line),
110
+ f"{TB_CONTAINER_NAME} not found in output",
111
+ )
112
+ add_telemetry_event(
113
+ "docker_debug",
114
+ data={
115
+ "docker_ps_output": header_row + tb_local_row,
116
+ },
117
+ )
118
+ except Exception:
119
+ pass
120
+ raise CLILocalException(
121
+ FeedbackManager.error(message="Tinybird local is not running. Please run `tb local start` first.")
122
+ )
@@ -30,7 +30,7 @@ class WatchProjectHandler(PatternMatchingEventHandler):
30
30
  self.project = project
31
31
  self.process = process
32
32
  self.datafiles = project.get_project_datafiles()
33
- patterns = [f"{project.path}/**/*{ext}" for ext in self.valid_extensions]
33
+ patterns = [f"**/*{ext}" for ext in self.valid_extensions]
34
34
  super().__init__(patterns=patterns)
35
35
 
36
36
  def should_process(self, event: Any) -> Optional[str]:
@@ -6,9 +6,10 @@
6
6
  from typing import Any, Dict, List, Optional
7
7
 
8
8
  import click
9
+ import requests
9
10
  from click import Context
10
11
 
11
- from tinybird.tb.client import TinyB
12
+ from tinybird.tb.client import AuthNoTokenException, TinyB
12
13
  from tinybird.tb.modules.cli import cli
13
14
  from tinybird.tb.modules.common import (
14
15
  _get_workspace_plan_name,
@@ -26,6 +27,10 @@ from tinybird.tb.modules.common import (
26
27
  from tinybird.tb.modules.config import CLIConfig
27
28
  from tinybird.tb.modules.exceptions import CLIWorkspaceException
28
29
  from tinybird.tb.modules.feedback_manager import FeedbackManager
30
+ from tinybird.tb.modules.local_common import (
31
+ TB_LOCAL_ADDRESS,
32
+ get_local_tokens,
33
+ )
29
34
 
30
35
 
31
36
  @cli.group()
@@ -221,3 +226,57 @@ async def delete_workspace(
221
226
  click.echo(FeedbackManager.success_workspace_deleted(workspace_name=workspace_to_delete["name"]))
222
227
  except Exception as e:
223
228
  raise CLIWorkspaceException(FeedbackManager.error_exception(error=str(e)))
229
+
230
+
231
+ @workspace.command(name="clear", short_help="Clear a workspace. Only available against Tinybird Local.")
232
+ @click.option("--yes", is_flag=True, default=False, help="Don't ask for confirmation")
233
+ @click.pass_context
234
+ @coro
235
+ async def workspace_clear(ctx: Context, yes: bool) -> None:
236
+ """Delete a workspace where you are an admin."""
237
+ is_cloud = ctx.ensure_object(dict)["env"] == "cloud"
238
+ if is_cloud:
239
+ raise CLIWorkspaceException(
240
+ FeedbackManager.error(
241
+ message="`tb workspace clear` is not available against Tinybird Cloud. Use `tb --cloud deploy` instead."
242
+ )
243
+ )
244
+ yes = yes or click.confirm(
245
+ FeedbackManager.warning(message="Are you sure you want to clear the workspace? [y/N]:"),
246
+ show_default=False,
247
+ prompt_suffix="",
248
+ )
249
+ if yes:
250
+ await clear_workspace()
251
+
252
+
253
+ async def clear_workspace() -> None:
254
+ config = CLIConfig.get_project_config()
255
+ tokens = get_local_tokens()
256
+
257
+ user_token = tokens["user_token"]
258
+ admin_token = tokens["admin_token"]
259
+ user_client = config.get_client(host=TB_LOCAL_ADDRESS, token=user_token)
260
+ ws_name = config.get("name")
261
+ if not ws_name:
262
+ raise AuthNoTokenException()
263
+
264
+ user_workspaces = requests.get( # noqa: ASYNC210
265
+ f"{TB_LOCAL_ADDRESS}/v1/user/workspaces?with_organization=true&token={admin_token}"
266
+ ).json()
267
+ user_org_id = user_workspaces.get("organization_id", {})
268
+ local_workspaces = user_workspaces.get("workspaces", [])
269
+
270
+ ws = next((ws for ws in local_workspaces if ws["name"] == ws_name), None)
271
+
272
+ if ws:
273
+ requests.delete(f"{TB_LOCAL_ADDRESS}/v1/workspaces/{ws['id']}?token={user_token}&hard_delete_confirmation=yes") # noqa: ASYNC210
274
+ ws = None
275
+
276
+ if not ws:
277
+ await user_client.create_workspace(ws_name, assign_to_organization_id=user_org_id, version="v1")
278
+ user_workspaces = requests.get(f"{TB_LOCAL_ADDRESS}/v1/user/workspaces?token={admin_token}").json() # noqa: ASYNC210
279
+ ws = next((ws for ws in user_workspaces["workspaces"] if ws["name"] == ws_name), None)
280
+
281
+ if ws:
282
+ click.echo(FeedbackManager.success(message=f"\n✓ Workspace '{ws_name}' cleared"))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: tinybird
3
- Version: 0.0.1.dev160
3
+ Version: 0.0.1.dev162
4
4
  Summary: Tinybird Command Line Tool
5
5
  Home-page: https://www.tinybird.co/docs/forward/commands
6
6
  Author: Tinybird
@@ -12,12 +12,12 @@ tinybird/syncasync.py,sha256=IPnOx6lMbf9SNddN1eBtssg8vCLHMt76SuZ6YNYm-Yk,27761
12
12
  tinybird/tornado_template.py,sha256=jjNVDMnkYFWXflmT8KU_Ssbo5vR8KQq3EJMk5vYgXRw,41959
13
13
  tinybird/ch_utils/constants.py,sha256=aYvg2C_WxYWsnqPdZB1ZFoIr8ZY-XjUXYyHKE9Ansj0,3890
14
14
  tinybird/ch_utils/engine.py,sha256=BZuPM7MFS7vaEKK5tOMR2bwSAgJudPrJt27uVEwZmTY,40512
15
- tinybird/tb/__cli__.py,sha256=s6LSSQKjMUCbqgJA61muv1NTc64EeZdFL1buos31pag,247
15
+ tinybird/tb/__cli__.py,sha256=P908zolGwSNV24cl38bwFAC73NFhZWghV4jlWXEyxDc,247
16
16
  tinybird/tb/check_pypi.py,sha256=rW4QmDRbtgKdUUwJCnBkVjmTjZSZGN-XgZhx7vMkC0w,1009
17
17
  tinybird/tb/cli.py,sha256=u3eGOhX0MHkuT6tiwaZ0_3twqLmqKXDAOxF7yV_Nn9Q,1075
18
18
  tinybird/tb/client.py,sha256=CSBl_JRuioPyY0H8Ac96dJ9wQXDXfrvK2lwqlOxKGoY,55715
19
19
  tinybird/tb/config.py,sha256=jT9xndpeCY_g0HdB5qE2EquC0TFRRnkPnQFWZWd04jo,3998
20
- tinybird/tb/modules/build.py,sha256=sj77FyQRAtAQdifY8sqVzPzeIUKsxUDwPsz17NSRsVA,18517
20
+ tinybird/tb/modules/build.py,sha256=zakH5812Lop-XHjGmDRdOPeofPtoeyb_2un_T6e50xw,19177
21
21
  tinybird/tb/modules/cicd.py,sha256=MnShTTJzKBYeajswF2jg7p7ZzupaeCgSriAN05MeEdg,7330
22
22
  tinybird/tb/modules/cli.py,sha256=jJ7vF05vppwqetaHiEyk_x2YFb6S53PS-PmYlNMRcTc,14274
23
23
  tinybird/tb/modules/common.py,sha256=_mNLBzC7zkveYXgJ02aMJ9L3LrxsAELx84GwEYdWNa0,82955
@@ -38,7 +38,7 @@ tinybird/tb/modules/job.py,sha256=n4dSSBgnA8NqD7srGahf2xRj6wxkmX9Vl0J-QJ_a2w0,29
38
38
  tinybird/tb/modules/llm.py,sha256=KfsCYmKeW1VQz0iDZhGKCRkQv_Y3kTHh6JuxvofOguE,1076
39
39
  tinybird/tb/modules/llm_utils.py,sha256=nS9r4FAElJw8yXtmdYrx-rtI2zXR8qXfi1QqUDCfxvg,3469
40
40
  tinybird/tb/modules/local.py,sha256=SUaGWH9TLDFFF9uCw4y7UW4NsKgnXG8uxTcxz1dbkCM,14230
41
- tinybird/tb/modules/local_common.py,sha256=OoBUFrMjXvQ3y8hHscpyVH9wMI7h0m79F7G6EiMSku0,4783
41
+ tinybird/tb/modules/local_common.py,sha256=LutxdEG6AR1k5PrUPsDWGVPrr3mA7AbWKhj4Hlewlic,4837
42
42
  tinybird/tb/modules/login.py,sha256=fmXPSdvJnKPv03chptGuu3_Fm6LhP6kUsUKhrmT8rJc,8269
43
43
  tinybird/tb/modules/logout.py,sha256=ULooy1cDBD02-r7voZmhV7udA0ML5tVuflJyShrh56Y,1022
44
44
  tinybird/tb/modules/materialization.py,sha256=QJX5kCPhhm6IXBO1JsalVfbQdypCe_eOUDZ_WHJZWS8,5478
@@ -53,8 +53,8 @@ tinybird/tb/modules/table.py,sha256=4XrtjM-N0zfNtxVkbvLDQQazno1EPXnxTyo7llivfXk,
53
53
  tinybird/tb/modules/telemetry.py,sha256=X0p5AVkM8BNsK_Rhdcg4p2eIf6OHimHO_VLldBqHQ8o,11386
54
54
  tinybird/tb/modules/test.py,sha256=Yopg89cRwOQpgRzsb9nvu2Z-UR2as2vBjVa5PF3uiK0,13420
55
55
  tinybird/tb/modules/token.py,sha256=2fmKwu10_M0pqs6YmJVeILR9ZQB0ejRAET86agASbKM,13488
56
- tinybird/tb/modules/watch.py,sha256=_92co0BjTikQuy7MbHr4TDu9A75QdHsrAO8v7DlYLg4,8898
57
- tinybird/tb/modules/workspace.py,sha256=h4Re1_2DahZ9C-yF7vgquMrYiG7Gj62jNfbzkEJR2BY,8066
56
+ tinybird/tb/modules/watch.py,sha256=H1FieLTVGRqmZ0hR0vELbQJ9l0CThrFCgGCta-MPuAY,8883
57
+ tinybird/tb/modules/workspace.py,sha256=v9o4ImrEOxLhCYhVGtJ-slek8DGOW0cJGn7F91DMmj4,10390
58
58
  tinybird/tb/modules/workspace_members.py,sha256=RYLpyPM1ECCasHRg3uvpckzXplX0_KgNFsSPZn_i6qk,8744
59
59
  tinybird/tb/modules/datafile/build.py,sha256=d_h3pRFDPFrDKGhpFx2iejY25GuB2k8yfNouj6s8caw,50973
60
60
  tinybird/tb/modules/datafile/build_common.py,sha256=LU24kAQmxDJIyoIapDaYG-SU3P4FrMG9UBf8m9PgVSI,4565
@@ -80,8 +80,8 @@ tinybird/tb_cli_modules/config.py,sha256=IsgdtFRnUrkY8-Zo32lmk6O7u3bHie1QCxLwgp4
80
80
  tinybird/tb_cli_modules/exceptions.py,sha256=pmucP4kTF4irIt7dXiG-FcnI-o3mvDusPmch1L8RCWk,3367
81
81
  tinybird/tb_cli_modules/regions.py,sha256=QjsL5H6Kg-qr0aYVLrvb1STeJ5Sx_sjvbOYO0LrEGMk,166
82
82
  tinybird/tb_cli_modules/telemetry.py,sha256=Hh2Io8ZPROSunbOLuMvuIFU4TqwWPmQTqal4WS09K1A,10449
83
- tinybird-0.0.1.dev160.dist-info/METADATA,sha256=Chpz-yZJB3bxzzpn-NhpeLHWx_lb5E5grl70FLLx618,1607
84
- tinybird-0.0.1.dev160.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
85
- tinybird-0.0.1.dev160.dist-info/entry_points.txt,sha256=LwdHU6TfKx4Qs7BqqtaczEZbImgU7Abe9Lp920zb_fo,43
86
- tinybird-0.0.1.dev160.dist-info/top_level.txt,sha256=VqqqEmkAy7UNaD8-V51FCoMMWXjLUlR0IstvK7tJYVY,54
87
- tinybird-0.0.1.dev160.dist-info/RECORD,,
83
+ tinybird-0.0.1.dev162.dist-info/METADATA,sha256=MDkrGU1_u15k7sp6G7ZeL841SKRhXPq6tGYvPd5CfnY,1607
84
+ tinybird-0.0.1.dev162.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
85
+ tinybird-0.0.1.dev162.dist-info/entry_points.txt,sha256=LwdHU6TfKx4Qs7BqqtaczEZbImgU7Abe9Lp920zb_fo,43
86
+ tinybird-0.0.1.dev162.dist-info/top_level.txt,sha256=VqqqEmkAy7UNaD8-V51FCoMMWXjLUlR0IstvK7tJYVY,54
87
+ tinybird-0.0.1.dev162.dist-info/RECORD,,