tinybird 0.0.1.dev159__py3-none-any.whl → 0.0.1.dev161__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.dev159'
8
- __revision__ = '33da9f4'
7
+ __version__ = '0.0.1.dev161'
8
+ __revision__ = '29ac536'
tinybird/tb/cli.py CHANGED
@@ -4,7 +4,6 @@ import sys
4
4
  if sys.platform == "win32":
5
5
  asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
6
6
 
7
- import tinybird.tb.modules.auth
8
7
  import tinybird.tb.modules.build
9
8
  import tinybird.tb.modules.cli
10
9
  import tinybird.tb.modules.common
@@ -15,6 +14,7 @@ import tinybird.tb.modules.datasource
15
14
  import tinybird.tb.modules.deployment
16
15
  import tinybird.tb.modules.deprecations
17
16
  import tinybird.tb.modules.endpoint
17
+ import tinybird.tb.modules.info
18
18
  import tinybird.tb.modules.infra
19
19
  import tinybird.tb.modules.job
20
20
  import tinybird.tb.modules.local
@@ -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}"))
@@ -111,12 +111,13 @@ tinybird_ci_workflow:
111
111
  before_script:
112
112
  - apt update && apt install -y curl
113
113
  - curl https://tinybird.co | sh
114
+ - for i in {1..10}; do curl -s -o /dev/null "http://$TB_LOCAL_HOST" && break; sleep 5; done
114
115
  script:
115
116
  - export PATH="$HOME/.local/bin:$PATH"
116
117
  - cd $CI_PROJECT_DIR/{{ data_project_dir }}
117
118
  - tb build
118
119
  - tb test run
119
- - tb --cloud --host ${{! TINYBIRD_HOST }} --token ${{! TINYBIRD_TOKEN }} deploy --check
120
+ - tb --cloud --host "$TINYBIRD_HOST" --token "$TINYBIRD_TOKEN" deploy --check
120
121
  services:
121
122
  - name: tinybirdco/tinybird-local:latest
122
123
  alias: tinybird-local
@@ -136,10 +137,13 @@ tinybird_cd_workflow:
136
137
  - .gitlab/tinybird/*{% if data_project_dir != '.' %}
137
138
  - {{ data_project_dir }}/*
138
139
  - {{ data_project_dir }}/**/*{% end %}
140
+ before_script:
141
+ - apt update && apt install -y curl
142
+ - curl https://tinybird.co | sh
139
143
  script:
140
144
  - export PATH="$HOME/.local/bin:$PATH"
141
145
  - cd $CI_PROJECT_DIR/{{ data_project_dir }}
142
- - tb --cloud --host ${{! TINYBIRD_HOST }} --token ${{! TINYBIRD_TOKEN }} deploy
146
+ - tb --cloud --host "$TINYBIRD_HOST" --token "$TINYBIRD_TOKEN" deploy
143
147
  """
144
148
 
145
149
 
@@ -317,7 +317,7 @@ def __unpatch_click_output():
317
317
 
318
318
 
319
319
  async def create_ctx_client(ctx: Context, config: Dict[str, Any], cloud: bool, staging: bool):
320
- commands_without_ctx_client = ["auth", "check", "local", "login", "logout", "update", "upgrade", "create"]
320
+ commands_without_ctx_client = ["auth", "check", "local", "login", "logout", "update", "upgrade", "create", "info"]
321
321
  command = ctx.invoked_subcommand
322
322
  if command in commands_without_ctx_client:
323
323
  return None
@@ -115,6 +115,10 @@ async def gather_with_concurrency(n, *tasks):
115
115
  return await asyncio.gather(*(sem_task(task) for task in tasks))
116
116
 
117
117
 
118
+ def format_robust_table(data: Iterable[Any], column_names: List[str]):
119
+ return humanfriendly.tables.format_robust_table(data, column_names=column_names)
120
+
121
+
118
122
  def echo_safe_humanfriendly_tables_format_smart_table(data: Iterable[Any], column_names: List[str]) -> None:
119
123
  """
120
124
  There is a bug in the humanfriendly library: it breaks to render the small table for small terminals
@@ -656,9 +660,7 @@ async def create_workspace_non_interactive(
656
660
  user_client: TinyB = deepcopy(client)
657
661
  user_client.token = user_token
658
662
 
659
- created_workspace = await user_client.create_workspace(
660
- workspace_name, organization_id, version="v1"
661
- )
663
+ created_workspace = await user_client.create_workspace(workspace_name, organization_id, version="v1")
662
664
  if organization_id and organization_name:
663
665
  click.echo(
664
666
  FeedbackManager.success_workspace_created_with_organization(
@@ -4,6 +4,30 @@ from tinybird.tb.modules.cli import cli
4
4
  from tinybird.tb.modules.feedback_manager import FeedbackManager
5
5
 
6
6
 
7
+ @cli.command(
8
+ name="auth",
9
+ context_settings=dict(
10
+ ignore_unknown_options=True,
11
+ ),
12
+ hidden=True,
13
+ )
14
+ @click.argument("args", nargs=-1, type=click.UNPROCESSED)
15
+ def auth(args) -> None:
16
+ """
17
+ `tb auth` is deprecated. Use `tb login` instead.
18
+ """
19
+ is_info_cmd = "info" in args
20
+ message = "This command is deprecated. Use `tb login` instead."
21
+ if is_info_cmd:
22
+ message = "This command is deprecated. Use `tb info` instead."
23
+ else:
24
+ message = "This command is deprecated. Use `tb login` instead."
25
+ click.echo(FeedbackManager.warning(message=message))
26
+ click.echo(
27
+ "You are using Tinybird Forward CLI.\nYou can find more information in the docs at https://www.tinybird.co/docs/forward"
28
+ )
29
+
30
+
7
31
  @cli.command(
8
32
  name="branch",
9
33
  context_settings=dict(
@@ -0,0 +1,112 @@
1
+ import os
2
+ from typing import Any, Dict, Iterable, List, Optional
3
+
4
+ import click
5
+
6
+ from tinybird.tb.client import TinyB
7
+ from tinybird.tb.config import get_display_cloud_host
8
+ from tinybird.tb.modules.cli import CLIConfig, cli
9
+ from tinybird.tb.modules.common import coro, format_robust_table
10
+ from tinybird.tb.modules.feedback_manager import FeedbackManager
11
+ from tinybird.tb.modules.local_common import TB_LOCAL_ADDRESS, get_tinybird_local_config
12
+ from tinybird.tb.modules.project import Project
13
+
14
+
15
+ @cli.command(name="info")
16
+ @click.pass_context
17
+ @coro
18
+ async def info(ctx: click.Context) -> None:
19
+ """Get information about the project that is currently being used"""
20
+ ctx_config = ctx.ensure_object(dict)["config"]
21
+ project: Project = ctx.ensure_object(dict)["project"]
22
+ click.echo(FeedbackManager.highlight(message="» Tinybird Cloud:"))
23
+ await get_cloud_info(ctx_config)
24
+ click.echo(FeedbackManager.highlight(message="\n» Tinybird Local:"))
25
+ await get_local_info(ctx_config)
26
+ click.echo(FeedbackManager.highlight(message="\n» Project:"))
27
+ await get_project_info(project.folder)
28
+
29
+
30
+ async def get_cloud_info(ctx_config: Dict[str, Any]) -> None:
31
+ config = CLIConfig.get_project_config()
32
+
33
+ try:
34
+ client = config.get_client()
35
+ token = config.get_token() or "No workspace token found"
36
+ api_host = config.get("host") or "No API host found"
37
+ ui_host = get_display_cloud_host(api_host)
38
+ user_email = config.get("user_email") or "No user email found"
39
+ user_token = config.get_user_token() or "No user token found"
40
+ await get_env_info(client, ctx_config, user_email, token, user_token, api_host, ui_host)
41
+ except Exception:
42
+ click.echo(
43
+ FeedbackManager.warning(
44
+ message="\n⚠ Could not retrieve Tinybird Cloud info. Please run `tb login` first or check that you are located in the correct directory."
45
+ )
46
+ )
47
+
48
+
49
+ async def get_local_info(config: Dict[str, Any]) -> None:
50
+ try:
51
+ local_config = await get_tinybird_local_config(config, test=False, silent=False)
52
+ local_client = local_config.get_client(host=TB_LOCAL_ADDRESS, staging=False)
53
+ user_email = local_config.get_user_email() or "No user email found"
54
+ token = local_config.get_token() or "No token found"
55
+ user_token = local_config.get_user_token() or "No user token found"
56
+ api_host = TB_LOCAL_ADDRESS
57
+ ui_host = get_display_cloud_host(api_host)
58
+ await get_env_info(local_client, config, user_email, token, user_token, api_host, ui_host)
59
+ except Exception:
60
+ click.echo(
61
+ FeedbackManager.warning(
62
+ message="\n⚠ Could not retrieve Tinybird Local info. Please run `tb local start` first."
63
+ )
64
+ )
65
+ return
66
+
67
+
68
+ async def get_env_info(
69
+ client: TinyB, config: Dict[str, Any], user_email: str, token: str, user_token: str, api_host: str, ui_host: str
70
+ ) -> None:
71
+ user_workspaces = await client.user_workspaces(version="v1")
72
+ current_workspace = await client.workspace_info(version="v1")
73
+
74
+ def _get_current_workspace(user_workspaces: Dict[str, Any], current_workspace_id: str) -> Optional[Dict[str, Any]]:
75
+ def get_workspace_by_name(workspaces: List[Dict[str, Any]], name: str) -> Optional[Dict[str, Any]]:
76
+ return next((ws for ws in workspaces if ws["name"] == name), None)
77
+
78
+ workspaces: Optional[List[Dict[str, Any]]] = user_workspaces.get("workspaces")
79
+ if not workspaces:
80
+ return None
81
+
82
+ current: Optional[Dict[str, Any]] = get_workspace_by_name(workspaces, current_workspace_id)
83
+ return current
84
+
85
+ current_main_workspace = _get_current_workspace(user_workspaces, config.get("name") or current_workspace["name"])
86
+
87
+ assert isinstance(current_main_workspace, dict)
88
+
89
+ columns = ["user", "workspace_name", "workspace_id", "token", "user_token", "api", "ui"]
90
+ if current_main_workspace["name"]:
91
+ ui_host += f"/{current_main_workspace['name']}"
92
+ table = [
93
+ (user_email, current_main_workspace["name"], current_main_workspace["id"], token, user_token, api_host, ui_host)
94
+ ]
95
+
96
+ click.echo(format_robust_table(table, column_names=columns))
97
+
98
+
99
+ async def get_project_info(project_path: Optional[str] = None) -> None:
100
+ config = CLIConfig.get_project_config()
101
+ tinyb_path = config.get_tinyb_file()
102
+ current_path = os.getcwd()
103
+ project_path = current_path
104
+ if tinyb_path:
105
+ tinyb_dir = os.path.dirname(tinyb_path)
106
+ project_path = os.path.join(tinyb_dir, project_path)
107
+ else:
108
+ tinyb_path = "Not found"
109
+
110
+ columns = ["current", ".tinyb", "project"]
111
+ table: Iterable[Any] = [(current_path, tinyb_path, project_path)]
112
+ click.echo(format_robust_table(table, column_names=columns))
@@ -20,14 +20,16 @@ TB_LOCAL_HOST = re.sub(r"^https?://", "", os.getenv("TB_LOCAL_HOST", "localhost"
20
20
  TB_LOCAL_ADDRESS = f"http://{TB_LOCAL_HOST}:{TB_LOCAL_PORT}"
21
21
 
22
22
 
23
- async def get_tinybird_local_client(config_obj: Dict[str, Any], test: bool = False, staging: bool = False) -> TinyB:
23
+ async def get_tinybird_local_client(
24
+ config_obj: Dict[str, Any], test: bool = False, staging: bool = False, silent: bool = False
25
+ ) -> TinyB:
24
26
  """Get a Tinybird client connected to the local environment."""
25
27
 
26
- config = await get_tinybird_local_config(config_obj, test=test)
28
+ config = await get_tinybird_local_config(config_obj, test=test, silent=silent)
27
29
  return config.get_client(host=TB_LOCAL_ADDRESS, staging=staging)
28
30
 
29
31
 
30
- async def get_tinybird_local_config(config_obj: Dict[str, Any], test: bool = False) -> CLIConfig:
32
+ async def get_tinybird_local_config(config_obj: Dict[str, Any], test: bool = False, silent: bool = False) -> CLIConfig:
31
33
  """Craft a client config with a workspace name based on the path of the project files
32
34
 
33
35
  It uses the tokens from tinybird local
@@ -90,9 +92,7 @@ async def get_tinybird_local_config(config_obj: Dict[str, Any], test: bool = Fal
90
92
  ws = None
91
93
 
92
94
  if not ws:
93
- await user_client.create_workspace(
94
- ws_name, assign_to_organization_id=user_org_id, version="v1"
95
- )
95
+ await user_client.create_workspace(ws_name, assign_to_organization_id=user_org_id, version="v1")
96
96
  user_workspaces = requests.get(f"{TB_LOCAL_ADDRESS}/v1/user/workspaces?token={admin_token}").json()
97
97
  ws = next((ws for ws in user_workspaces["workspaces"] if ws["name"] == ws_name), None)
98
98
  if not ws:
@@ -62,7 +62,7 @@ async def mock(ctx: click.Context, datasource: str, rows: int, prompt: str, form
62
62
  datasource_path = Path(datasource_from_glob[0])
63
63
 
64
64
  if not datasource_path.exists():
65
- raise Exception(f"Datasource '{datasource_path.stem}' not fosund")
65
+ raise Exception(f"Datasource '{datasource_path.stem}' not found")
66
66
 
67
67
  datasource_content = datasource_path.read_text()
68
68
  config = CLIConfig.get_project_config()
@@ -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]:
@@ -173,9 +173,7 @@ async def create_workspace(
173
173
  ctx, workspace_name, user_token, fork, organization_id, organization_name
174
174
  )
175
175
  else:
176
- await create_workspace_interactive(
177
- ctx, workspace_name, user_token, fork, organization_id, organization_name
178
- )
176
+ await create_workspace_interactive(ctx, workspace_name, user_token, fork, organization_id, organization_name)
179
177
 
180
178
 
181
179
  @workspace.command(name="delete", short_help="Delete a workspace for your Tinybird user")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: tinybird
3
- Version: 0.0.1.dev159
3
+ Version: 0.0.1.dev161
4
4
  Summary: Tinybird Command Line Tool
5
5
  Home-page: https://www.tinybird.co/docs/forward/commands
6
6
  Author: Tinybird
@@ -12,37 +12,37 @@ 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=GyvIw1NTIol8PlS2Xxdz91AMMQN8fLMXK9gDURTEfj0,247
15
+ tinybird/tb/__cli__.py,sha256=E8aXjTvNw5qa6fTVCqpIk7TJE7KKkGYHZ4NJ1NbwTmw,247
16
16
  tinybird/tb/check_pypi.py,sha256=rW4QmDRbtgKdUUwJCnBkVjmTjZSZGN-XgZhx7vMkC0w,1009
17
- tinybird/tb/cli.py,sha256=uPV6pvi4aYVfaiGs0DQO-eoi1g9dHlrgvhutkXkdJko,1075
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/auth.py,sha256=_OeYnmTH83lnqCgQEdS6K0bx1KBUeRmZk2M7JnRmWpk,9037
21
- tinybird/tb/modules/build.py,sha256=sj77FyQRAtAQdifY8sqVzPzeIUKsxUDwPsz17NSRsVA,18517
22
- tinybird/tb/modules/cicd.py,sha256=Cn1DyE28w2maQgIJF4IjZU7J7ULPvMG11DjN2OH-cqE,7161
23
- tinybird/tb/modules/cli.py,sha256=TCms3gyokv3ES1yD6WuL_mLNEqBcAFQHSvgOnr6RYDY,14266
24
- tinybird/tb/modules/common.py,sha256=piwmeZVs2HZ-r4FAS1IN7UvKnlzxD8heyTU-S8tdIxw,82819
20
+ tinybird/tb/modules/build.py,sha256=zakH5812Lop-XHjGmDRdOPeofPtoeyb_2un_T6e50xw,19177
21
+ tinybird/tb/modules/cicd.py,sha256=MnShTTJzKBYeajswF2jg7p7ZzupaeCgSriAN05MeEdg,7330
22
+ tinybird/tb/modules/cli.py,sha256=jJ7vF05vppwqetaHiEyk_x2YFb6S53PS-PmYlNMRcTc,14274
23
+ tinybird/tb/modules/common.py,sha256=_mNLBzC7zkveYXgJ02aMJ9L3LrxsAELx84GwEYdWNa0,82955
25
24
  tinybird/tb/modules/config.py,sha256=ziqW_t_mRVvWOd85VoB4vKyvgMkEfpXDf9H4v38p2xc,11422
26
25
  tinybird/tb/modules/connection.py,sha256=7oOR7x4PhBcm1ETFFCH2YJ_3oeGXjAbmx1cnZX9_L70,9014
27
26
  tinybird/tb/modules/copy.py,sha256=2Mm4FWKehOG7CoOhiF1m9UZJgJn0W1_cMolqju8ONYg,5805
28
27
  tinybird/tb/modules/create.py,sha256=OHUvuHuvP0iecPPGI4eVOHOgR20qy7a_Sw7sbJKuG8g,17411
29
28
  tinybird/tb/modules/datasource.py,sha256=V314rkpdVxVMjsp5qcSCTqDlmp4Vu--qM07BoWh-aqs,17783
30
29
  tinybird/tb/modules/deployment.py,sha256=a6CZrYqAM-t6WxGKjgg16ZvncpZBta5gBq7YEBPBoQc,25811
31
- tinybird/tb/modules/deprecations.py,sha256=nI_VPCB0Seyhlk2Foomoj3oUnx6GV_8Q5DMYVDi9L-o,3751
30
+ tinybird/tb/modules/deprecations.py,sha256=rrszC1f_JJeJ8mUxGoCxckQTJFBCR8wREf4XXXN-PRc,4507
32
31
  tinybird/tb/modules/dev_server.py,sha256=57FCKuWpErwYUYgHspYDkLWEm9F4pbvVOtMrFXX1fVU,10129
33
32
  tinybird/tb/modules/endpoint.py,sha256=XySDt3pk66vxOZ0egUfz4bY8bEk3BjOXkv-L0OIJ3sc,12083
34
33
  tinybird/tb/modules/exceptions.py,sha256=5jK91w1LPmtqIUfDpHe_Op5OxGz8-p1BPgtLREMIni0,5217
35
34
  tinybird/tb/modules/feedback_manager.py,sha256=c0ZOpG7IHFq3doodezctX64cTcTquIOhO38w9uPuU8Q,76798
35
+ tinybird/tb/modules/info.py,sha256=AThOatkAjN1HbmARIU0MaQ-N4ZmZlZqoAxN18FQRa6M,4797
36
36
  tinybird/tb/modules/infra.py,sha256=fve30Gj3mG9zbquGxS2e4ipcOYOxviWQCpNFfEzJN_Q,33195
37
37
  tinybird/tb/modules/job.py,sha256=n4dSSBgnA8NqD7srGahf2xRj6wxkmX9Vl0J-QJ_a2w0,2966
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=WJsU4UMn5ICVzPsC_rTfxWm1Kl5szfxD1Sqlew1sq0M,4748
41
+ tinybird/tb/modules/local_common.py,sha256=OoBUFrMjXvQ3y8hHscpyVH9wMI7h0m79F7G6EiMSku0,4783
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
45
- tinybird/tb/modules/mock.py,sha256=z_mYNVAu5aeq-5qGB0kSkliOX4j62fgUuLi1-ZcTamA,5304
45
+ tinybird/tb/modules/mock.py,sha256=IyHweMUM6bUH8IhyiX2tTMpdVpTFUeAJ41lZ5P42-HQ,5303
46
46
  tinybird/tb/modules/open.py,sha256=OuctINN77oexpSjth9uoIZPCelKO4Li-yyVxeSnk1io,1371
47
47
  tinybird/tb/modules/pipe.py,sha256=AQKEDagO6e3psPVjJkS_MDbn8aK-apAiLp26k7jgAV0,2432
48
48
  tinybird/tb/modules/project.py,sha256=PwEG8Ob9bW4HZxh1jQq2LyDfIoitqF69Jdu0WsWHMCg,3568
@@ -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=yKCnplv_gEK8VAbC_TyzOX8OKxdBg9x3NsUFLOBrxXA,8088
56
+ tinybird/tb/modules/watch.py,sha256=H1FieLTVGRqmZ0hR0vELbQJ9l0CThrFCgGCta-MPuAY,8883
57
+ tinybird/tb/modules/workspace.py,sha256=h4Re1_2DahZ9C-yF7vgquMrYiG7Gj62jNfbzkEJR2BY,8066
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.dev159.dist-info/METADATA,sha256=Ay8x_tVw1uwVAyEAxHhuVpcxfTwbwNMVO6LlgQ8vxs0,1607
84
- tinybird-0.0.1.dev159.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
85
- tinybird-0.0.1.dev159.dist-info/entry_points.txt,sha256=LwdHU6TfKx4Qs7BqqtaczEZbImgU7Abe9Lp920zb_fo,43
86
- tinybird-0.0.1.dev159.dist-info/top_level.txt,sha256=VqqqEmkAy7UNaD8-V51FCoMMWXjLUlR0IstvK7tJYVY,54
87
- tinybird-0.0.1.dev159.dist-info/RECORD,,
83
+ tinybird-0.0.1.dev161.dist-info/METADATA,sha256=zcXNbkwS4Ut4VaoEViYVtbi9qS0bQ82ra-c_nzjJ2pA,1607
84
+ tinybird-0.0.1.dev161.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
85
+ tinybird-0.0.1.dev161.dist-info/entry_points.txt,sha256=LwdHU6TfKx4Qs7BqqtaczEZbImgU7Abe9Lp920zb_fo,43
86
+ tinybird-0.0.1.dev161.dist-info/top_level.txt,sha256=VqqqEmkAy7UNaD8-V51FCoMMWXjLUlR0IstvK7tJYVY,54
87
+ tinybird-0.0.1.dev161.dist-info/RECORD,,
@@ -1,252 +0,0 @@
1
- # This is a command file for our CLI. Please keep it clean.
2
- #
3
- # - If it makes sense and only when strictly necessary, you can create utility functions in this file.
4
- # - But please, **do not** interleave utility functions and command definitions.
5
-
6
- import os
7
- from typing import Any, Dict, List, Optional
8
-
9
- import click
10
- import humanfriendly.tables
11
-
12
- from tinybird.tb.config import get_display_cloud_host
13
- from tinybird.tb.modules.cli import cli
14
- from tinybird.tb.modules.common import (
15
- configure_connector,
16
- coro,
17
- echo_safe_humanfriendly_tables_format_smart_table,
18
- get_host_from_region,
19
- get_regions,
20
- try_authenticate,
21
- try_update_config_with_remote,
22
- )
23
- from tinybird.tb.modules.config import CLIConfig, ConfigValueOrigin
24
- from tinybird.tb.modules.exceptions import CLIAuthException
25
- from tinybird.tb.modules.feedback_manager import FeedbackManager
26
- from tinybird.tb.modules.regions import Region
27
-
28
-
29
- @cli.group(invoke_without_command=True)
30
- @click.option("--token", help="Use auth token, defaults to TB_TOKEN envvar, then to the .tinyb file")
31
- @click.option(
32
- "--host",
33
- help="Set custom host if it's different than https://api.tinybird.co. Check https://www.tinybird.co/docs/api-reference#regions-and-endpoints for the available list of regions",
34
- )
35
- @click.option(
36
- "--region", envvar="TB_REGION", help="Set region. Run 'tb auth ls' to show available regions. Overrides host."
37
- )
38
- @click.option(
39
- "--connector",
40
- type=click.Choice(["bigquery", "snowflake"], case_sensitive=True),
41
- help="Set credentials for one of the supported connectors",
42
- )
43
- @click.option(
44
- "-i",
45
- "--interactive",
46
- is_flag=True,
47
- default=False,
48
- help="Show available regions and select where to authenticate to",
49
- )
50
- @click.pass_context
51
- @coro
52
- async def auth(ctx: click.Context, token: str, host: str, region: str, connector: str, interactive: bool) -> None:
53
- """Configure auth."""
54
-
55
- config: CLIConfig = CLIConfig.get_project_config()
56
- if token:
57
- config.set_token(token)
58
- if host:
59
- config.set_host(host)
60
-
61
- if connector:
62
- await configure_connector(connector)
63
- return
64
-
65
- # Only run when doing a bare 'tb auth'
66
- if ctx.invoked_subcommand:
67
- return
68
-
69
- assert isinstance(ctx.parent, click.Context)
70
-
71
- env_token = os.environ.get("TB_TOKEN", None)
72
-
73
- # If no token passed, let's clear the current one to
74
- # do a clean auth
75
- if not token and not ctx.parent.params.get("token") and not env_token:
76
- config.set_token(None)
77
- else:
78
- if env_token and not token:
79
- click.echo(FeedbackManager.info_reading_from_env(value="token", envvar="TB_TOKEN"))
80
-
81
- regions: Optional[List[Region]] = None
82
- try_all_regions = True
83
-
84
- if host:
85
- try_all_regions = False
86
-
87
- if region:
88
- regions, host = await get_host_from_region(config, region, config.get_host())
89
- config.set_host(host)
90
- if token:
91
- config.set_token_for_host(token, host)
92
- try_all_regions = False
93
-
94
- if not await try_authenticate(config, regions=regions, interactive=interactive, try_all_regions=try_all_regions):
95
- raise CLIAuthException(FeedbackManager.error_invalid_token_for_host(host=config.get_host()))
96
-
97
- config.persist_to_file()
98
-
99
-
100
- @auth.command(name="login", hidden=True)
101
- @click.option(
102
- "--host",
103
- help="Set custom host if it's different than https://api.tinybird.co. Use `tb auth ls` or check https://docs.tinybird.co/cli.html for the available list of regions.",
104
- )
105
- @click.argument("token", required=False)
106
- @coro
107
- async def auth_login(host: Optional[str], token: Optional[str]) -> None:
108
- """Authenticate with a Tinybird host.
109
-
110
- The authentication mode is token based. After completion, the authentication token
111
- will be stored internally.
112
-
113
- You need your User Token to log into Tinybird. Please, check the docs at
114
- https://www.tinybird.co/docs/concepts/auth-tokens.html for more info.
115
-
116
- Alternatively, tb will use the authentication token found in the environment
117
- variable TB_USER_TOKEN. This method is most suitable for "headless" use of tb
118
- such as in automations or in a CI environment."""
119
- config = CLIConfig.get_project_config()
120
-
121
- if host:
122
- config.set_host(host)
123
-
124
- if not token:
125
- click.echo(FeedbackManager.info_pre_prompt_auth_login_user_token(host=config.get_host()))
126
- token = click.prompt(FeedbackManager.prompt_auth_login_user_token(), type=str, hide_input=True)
127
- if not token:
128
- raise CLIAuthException(FeedbackManager.error_auth_login_token_expected())
129
- config.set_user_token(token)
130
-
131
- auth_info: Dict[str, Any] = await config.get_user_client().check_auth_login()
132
- if not auth_info.get("is_valid", False):
133
- raise CLIAuthException(FeedbackManager.error_auth_login_not_valid(host=config.get_host()))
134
-
135
- if not auth_info.get("is_user", False):
136
- raise CLIAuthException(FeedbackManager.error_auth_login_not_user(host=config.get_host()))
137
-
138
- config.persist_to_file()
139
-
140
- # No more output needed as per "The art of UNIX programming"
141
- # http://www.catb.org/~esr/writings/taoup/html/ch11s09.html
142
- # > be chatty only about things that deviate from what's normally expected.
143
-
144
-
145
- @auth.command(name="logout", hidden=True)
146
- @coro
147
- async def auth_logout() -> None:
148
- """Remove authentication from Tinybird."""
149
- conf = CLIConfig.get_project_config()
150
- conf.set_user_token(None)
151
- conf.persist_to_file()
152
-
153
-
154
- @auth.command(name="info")
155
- @coro
156
- async def auth_info() -> None:
157
- """Get information about the authentication that is currently being used"""
158
-
159
- config = CLIConfig.get_project_config()
160
- _ = await try_update_config_with_remote(config, raise_on_errors=False)
161
-
162
- if "id" in config:
163
- table = []
164
- user_email = config.get("user_email", "No user")
165
-
166
- ORIGINS: Dict[ConfigValueOrigin, str] = {
167
- ConfigValueOrigin.CONFIG: ".tinyb",
168
- ConfigValueOrigin.DEFAULT: ".tinyb",
169
- ConfigValueOrigin.ENVIRONMENT: "env",
170
- }
171
-
172
- token_origin = ORIGINS.get(config.get_value_origin("token"), "")
173
- token = f"{config.get_token()} ({token_origin})"
174
-
175
- host = config.get("host") or ""
176
- ui_host = get_display_cloud_host(host)
177
-
178
- if config.get_user_token():
179
- user_token_origin = ORIGINS.get(config.get_value_origin("user_token"), "")
180
- user_token = f"{config.get_user_token()} ({user_token_origin})"
181
- columns = ["user", "user_token", "token", "host", "ui", "workspace_name", "workspace_id"]
182
- table.append([user_email, user_token, token, host, ui_host, config["name"], config["id"]])
183
- else:
184
- columns = ["user", "token", "host", "ui", "workspace_name", "workspace_id"]
185
- table.append([user_email, token, host, ui_host, config["name"], config["id"]])
186
-
187
- click.echo(humanfriendly.tables.format_robust_table(table, column_names=columns))
188
-
189
-
190
- @auth.command(name="ls")
191
- @coro
192
- async def auth_ls() -> None:
193
- """List available regions to authenticate."""
194
-
195
- click.echo(FeedbackManager.info_available_regions())
196
-
197
- config = CLIConfig.get_project_config()
198
- _ = await try_update_config_with_remote(config, raise_on_errors=False)
199
-
200
- table: List[List[Any]] = []
201
- regions: List[Region] = await get_regions(config)
202
- if regions:
203
-
204
- def is_current(region: Region) -> bool:
205
- return region["host"] == config.get("host") or region["api_host"] == config.get("host")
206
-
207
- for index, region in enumerate(regions):
208
- table.append(
209
- [
210
- index + 1,
211
- region["name"].lower(),
212
- region.get("provider") or "",
213
- region["host"],
214
- region["api_host"],
215
- is_current(region),
216
- ]
217
- )
218
- else:
219
- table.append([1, "default", "", config["host"], True])
220
-
221
- columns: List[str] = ["idx", "region", "provider", "ui", "host", "current"]
222
- echo_safe_humanfriendly_tables_format_smart_table(table, column_names=columns)
223
-
224
-
225
- @auth.command(name="use")
226
- @click.argument("region_name_or_host_or_id")
227
- @coro
228
- async def auth_use(region_name_or_host_or_id: str) -> None:
229
- """Switch to a different region.
230
- You can pass the region name, the region host url, or the region index
231
- after listing available regions with 'tb auth ls'
232
-
233
- \b
234
- Example usage:
235
- \b
236
- $ tb auth use us-east
237
- $ tb auth use 1
238
- $ tb auth use https://ui.us-east.tinybird.co
239
- """
240
-
241
- config = CLIConfig.get_project_config()
242
- _ = await try_update_config_with_remote(config, raise_on_errors=False)
243
-
244
- regions, host = await get_host_from_region(config, region_name_or_host_or_id, config.get_host())
245
- config.set_host(host)
246
-
247
- if not await try_authenticate(config, regions):
248
- msg = FeedbackManager.error_wrong_config_file(config_file=config._path)
249
- raise CLIAuthException(msg)
250
-
251
- config.persist_to_file()
252
- click.echo(FeedbackManager.success_now_using_config(name=config["name"], id=config["id"]))