tinybird 0.0.1.dev5__py3-none-any.whl → 0.0.1.dev6__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.

Files changed (41) hide show
  1. tinybird/__cli__.py +7 -8
  2. tinybird/tb/cli.py +28 -0
  3. tinybird/{tb_cli_modules → tb/modules}/auth.py +5 -5
  4. tinybird/{tb_cli_modules → tb/modules}/branch.py +6 -5
  5. tinybird/{tb_cli_modules → tb/modules}/build.py +8 -8
  6. tinybird/tb/modules/cicd.py +271 -0
  7. tinybird/{tb_cli_modules → tb/modules}/cli.py +23 -23
  8. tinybird/tb/modules/common.py +2098 -0
  9. tinybird/tb/modules/config.py +352 -0
  10. tinybird/{tb_cli_modules → tb/modules}/connection.py +4 -4
  11. tinybird/{tb_cli_modules → tb/modules}/create.py +11 -7
  12. tinybird/{datafile.py → tb/modules/datafile.py} +6 -7
  13. tinybird/{tb_cli_modules → tb/modules}/datasource.py +7 -6
  14. tinybird/tb/modules/exceptions.py +91 -0
  15. tinybird/{tb_cli_modules → tb/modules}/fmt.py +3 -3
  16. tinybird/{tb_cli_modules → tb/modules}/job.py +3 -3
  17. tinybird/{tb_cli_modules → tb/modules}/llm.py +1 -1
  18. tinybird/{tb_cli_modules → tb/modules}/local.py +9 -5
  19. tinybird/{tb_cli_modules → tb/modules}/mock.py +5 -5
  20. tinybird/{tb_cli_modules → tb/modules}/pipe.py +5 -5
  21. tinybird/tb/modules/regions.py +9 -0
  22. tinybird/{tb_cli_modules → tb/modules}/tag.py +2 -2
  23. tinybird/tb/modules/telemetry.py +310 -0
  24. tinybird/{tb_cli_modules → tb/modules}/test.py +5 -5
  25. tinybird/{tb_cli_modules → tb/modules}/tinyunit/tinyunit.py +1 -1
  26. tinybird/{tb_cli_modules → tb/modules}/token.py +3 -3
  27. tinybird/{tb_cli_modules → tb/modules}/workspace.py +5 -5
  28. tinybird/{tb_cli_modules → tb/modules}/workspace_members.py +4 -4
  29. tinybird/tb_cli_modules/common.py +9 -25
  30. tinybird/tb_cli_modules/config.py +0 -8
  31. {tinybird-0.0.1.dev5.dist-info → tinybird-0.0.1.dev6.dist-info}/METADATA +1 -1
  32. tinybird-0.0.1.dev6.dist-info/RECORD +58 -0
  33. tinybird-0.0.1.dev6.dist-info/entry_points.txt +2 -0
  34. tinybird/tb_cli.py +0 -28
  35. tinybird-0.0.1.dev5.dist-info/RECORD +0 -52
  36. tinybird-0.0.1.dev5.dist-info/entry_points.txt +0 -2
  37. /tinybird/{tb_cli_modules → tb/modules}/prompts.py +0 -0
  38. /tinybird/{tb_cli_modules → tb/modules}/table.py +0 -0
  39. /tinybird/{tb_cli_modules → tb/modules}/tinyunit/tinyunit_lib.py +0 -0
  40. {tinybird-0.0.1.dev5.dist-info → tinybird-0.0.1.dev6.dist-info}/WHEEL +0 -0
  41. {tinybird-0.0.1.dev5.dist-info → tinybird-0.0.1.dev6.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,352 @@
1
+ import json
2
+ import os
3
+ from copy import deepcopy
4
+ from dataclasses import dataclass
5
+ from enum import Enum
6
+ from typing import Any, Dict, List, Optional, Tuple, Union
7
+ from urllib.parse import urlparse
8
+
9
+ from packaging import version
10
+
11
+ import tinybird.client as tbc
12
+ from tinybird.config import CURRENT_VERSION, DEFAULT_API_HOST, DEFAULT_LOCALHOST
13
+
14
+ APP_CONFIG_NAME = "tinybird"
15
+
16
+
17
+ class FeatureFlags:
18
+ @classmethod
19
+ def ignore_sql_errors(cls) -> bool: # Context: #1155
20
+ return "TB_IGNORE_SQL_ERRORS" in os.environ
21
+
22
+ @classmethod
23
+ def is_localhost(cls) -> bool:
24
+ return "SET_LOCALHOST" in os.environ
25
+
26
+ @classmethod
27
+ def ignore_ssl_errors(cls) -> bool:
28
+ return os.environ.get("TB_DISABLE_SSL_CHECKS", "0") == "1"
29
+
30
+ @classmethod
31
+ def send_telemetry(cls) -> bool:
32
+ if os.environ.get("TB_CLI_TELEMETRY_OPTOUT", "0") == "1":
33
+ return False
34
+ if "x.y.z" in CURRENT_VERSION and os.environ.get("TB_CLI_TELEMETRY_SEND_IN_LOCAL", "0") == "0":
35
+ return False
36
+ return True
37
+
38
+
39
+ def compare_versions(a: str, b: str) -> int:
40
+ va = version.parse(a)
41
+ vb = version.parse(b)
42
+ return -1 if va < vb else (1 if va > vb else 0)
43
+
44
+
45
+ class ConfigValueOrigin(Enum):
46
+ # Sources for config values (environment variables, .tinyb file or default value)
47
+
48
+ ENVIRONMENT: str = "env"
49
+ CONFIG: str = "conf"
50
+ DEFAULT: str = "default"
51
+ NONE: str = ""
52
+
53
+
54
+ @dataclass
55
+ class ConfigValue:
56
+ name: str
57
+ value: Any
58
+ origin: ConfigValueOrigin
59
+
60
+ def as_tuple(self) -> Tuple[str, str]:
61
+ return (self.name, self.value)
62
+
63
+
64
+ def write_json_file(data: Dict[str, Any], path: str) -> None:
65
+ with open(path, "w") as file:
66
+ file.write(json.dumps(data, indent=4, sort_keys=True))
67
+
68
+
69
+ class CLIConfig:
70
+ # Mapping between environment variables and config values
71
+ ENV_KEYS: Dict[str, str] = {
72
+ "token": "TB_TOKEN",
73
+ "user_token": "TB_USER_TOKEN",
74
+ "host": "TB_HOST",
75
+ "semver": "TB_SEMVER",
76
+ }
77
+
78
+ DEFAULTS: Dict[str, str] = {"host": DEFAULT_API_HOST if not FeatureFlags.is_localhost() else DEFAULT_LOCALHOST}
79
+
80
+ _global: Optional["CLIConfig"] = None
81
+ _projects: Dict[str, "CLIConfig"] = {}
82
+
83
+ def __init__(self, path: Optional[str], parent: Optional["CLIConfig"] = None) -> None:
84
+ self._path = path
85
+ self._parent = parent
86
+ self._values: Dict[str, ConfigValue] = {}
87
+ self._values["version"] = ConfigValue("version", CURRENT_VERSION, ConfigValueOrigin.DEFAULT)
88
+ self._workspaces: List[Dict[str, Any]] = []
89
+
90
+ if self._path:
91
+ self.override_with_file(self._path)
92
+ self.override_with_environment()
93
+ self.override_with_defaults()
94
+
95
+ def __str__(self) -> str:
96
+ return str(self.to_dict())
97
+
98
+ def to_dict(self) -> Dict[str, Any]:
99
+ """Helper to ease"""
100
+ result: Dict[str, Any] = self._parent.to_dict() if self._parent else {}
101
+ result.update(dict((v.name, deepcopy(v.value)) for v in self._values.values()))
102
+ return result
103
+
104
+ def __getitem__(self, key) -> Any:
105
+ """Gets a config key value in this order:
106
+ - Environment
107
+ - Internal dict (by host)
108
+ - Parent dict (if has a parent)
109
+ - Default values
110
+ """
111
+ if key in self._values:
112
+ return self._values[key].value
113
+ if self._parent:
114
+ return self._parent[key]
115
+ raise KeyError(key)
116
+
117
+ def __setitem__(self, key: str, value: Any) -> None:
118
+ self._values[key] = ConfigValue(key, value, ConfigValueOrigin.CONFIG)
119
+
120
+ def __contains__(self, key: str) -> bool:
121
+ return self.get_value_origin(key) != ConfigValueOrigin.NONE
122
+
123
+ def get(self, key: str, default: Any = None) -> Any:
124
+ try:
125
+ return self[key]
126
+ except KeyError:
127
+ return default
128
+
129
+ def get_value_origin(self, key: str) -> ConfigValueOrigin:
130
+ if key in self._values:
131
+ return self._values[key].origin
132
+ if self._parent:
133
+ return self._parent.get_value_origin(key)
134
+ else:
135
+ return ConfigValueOrigin.NONE
136
+
137
+ def persist_to_file(self, override_with_values: Optional["CLIConfig"] = None) -> None:
138
+ if not self._path:
139
+ raise ValueError("Cannot persist configuration: `path` is None")
140
+
141
+ if override_with_values:
142
+ self.update(override_with_values)
143
+
144
+ os.makedirs(os.path.dirname(self._path), exist_ok=True)
145
+ values = dict(v.as_tuple() for v in self._values.values())
146
+ write_json_file(values, self._path)
147
+
148
+ def override_with_file(self, path: str) -> bool:
149
+ """Loads the contents of the passed file."""
150
+ try:
151
+ with open(path) as file:
152
+ values: Dict[str, Any] = json.loads(file.read())
153
+ for k, v in values.items():
154
+ self[k] = v
155
+ return True
156
+ except IOError:
157
+ return False
158
+
159
+ def override_with_environment(self) -> None:
160
+ """Loads environment variables."""
161
+ for config_key, env_key in CLIConfig.ENV_KEYS.items():
162
+ env_value = os.environ.get(env_key, None)
163
+ if env_value:
164
+ self._values[config_key] = ConfigValue(config_key, env_value, ConfigValueOrigin.ENVIRONMENT)
165
+
166
+ def override_with_defaults(self) -> None:
167
+ """Loads default values."""
168
+ for key, default_value in CLIConfig.DEFAULTS.items():
169
+ if key not in self._values:
170
+ self._values[key] = ConfigValue(key, default_value, ConfigValueOrigin.DEFAULT)
171
+
172
+ def set_token(self, token: Optional[str]) -> None:
173
+ self["token"] = token
174
+
175
+ def get_token(self) -> Optional[str]:
176
+ try:
177
+ return self["token"]
178
+ except KeyError:
179
+ return None
180
+
181
+ def set_semver(self, semver: Optional[str]) -> None:
182
+ self["semver"] = semver
183
+
184
+ def get_semver(self) -> Optional[str]:
185
+ try:
186
+ return self["semver"]
187
+ except KeyError:
188
+ return None
189
+
190
+ def set_token_for_host(self, token: Optional[str], host: Optional[str]) -> None:
191
+ """Sets the token for the specified host.
192
+
193
+ Here we ask for the host explicitly to avoid mistakenly setting the token
194
+ for the wrong host.
195
+ """
196
+ if not token and not host:
197
+ return
198
+
199
+ assert isinstance(host, str)
200
+
201
+ tokens: Dict[str, Optional[str]] = self.get("tokens", {})
202
+ tokens[host] = token
203
+ self["tokens"] = tokens
204
+
205
+ def get_token_for_host(self, host: str) -> Optional[str]:
206
+ try:
207
+ return self["tokens"][host]
208
+ except KeyError:
209
+ return None
210
+
211
+ def set_user_token(self, token: Optional[str]) -> None:
212
+ self["user_token"] = token
213
+
214
+ def get_user_token(self) -> Optional[str]:
215
+ try:
216
+ return self["user_token"]
217
+ except KeyError:
218
+ return None
219
+
220
+ def set_host(self, host: Optional[str]) -> None:
221
+ url_info = urlparse(host)
222
+ scheme: str = url_info.scheme.decode() if isinstance(url_info.scheme, bytes) else url_info.scheme
223
+ netloc: str = url_info.netloc.decode() if isinstance(url_info.netloc, bytes) else url_info.netloc
224
+ self["host"] = f"{scheme}://{netloc}"
225
+
226
+ def get_host(self, use_defaults_if_needed: bool = False) -> Optional[str]:
227
+ result: Optional[str] = self.get("host", None)
228
+ if result:
229
+ return result
230
+ if use_defaults_if_needed:
231
+ return CLIConfig.DEFAULTS["host"]
232
+ return None
233
+
234
+ def get_client(self, token: Optional[str] = None, host: Optional[str] = None) -> tbc.TinyB:
235
+ """Returns a new TinyB client configured with:
236
+
237
+ - token:
238
+ - passed token
239
+ - OR the user token
240
+ - OR the current admin token
241
+
242
+ - host:
243
+ - passed host
244
+ - OR the current host
245
+ """
246
+ host = host or self.get_host()
247
+ assert isinstance(host, str)
248
+ token = token or self.get_token() or self.get_token_for_host(host) or ""
249
+ assert isinstance(token, str)
250
+
251
+ return tbc.TinyB(
252
+ token,
253
+ host,
254
+ version=CURRENT_VERSION,
255
+ disable_ssl_checks=FeatureFlags.ignore_ssl_errors(),
256
+ send_telemetry=FeatureFlags.send_telemetry(),
257
+ )
258
+
259
+ def get_user_client(self, host: Optional[str] = None) -> tbc.TinyB:
260
+ return self.get_client(self.get_user_token(), host)
261
+
262
+ def set_workspace_token(self, workspace_id: str, token: str) -> None:
263
+ pass
264
+
265
+ async def get_workspace_token(
266
+ self, workspace_id: Optional[str] = None, host: Optional[str] = None
267
+ ) -> Optional[str]:
268
+ """Returns the token for the specific workspace on a host.
269
+ - If no workspace passed, it uses the current active workspace.
270
+ - If no host is passed, it uses the current one.
271
+ """
272
+
273
+ # First, try to get any saved token for the host
274
+ if host:
275
+ try:
276
+ return self["tokens"][host]
277
+ except KeyError:
278
+ pass
279
+
280
+ # If we don't have an user_token, can't continue
281
+ if not self.get_user_token():
282
+ return None
283
+
284
+ if not workspace_id:
285
+ workspace_id = self.get("id", None)
286
+ if not workspace_id:
287
+ return None
288
+
289
+ client: tbc.TinyB = self.get_client(token=self.get_user_token(), host=host)
290
+
291
+ info: Dict[str, Any] = await client.user_workspaces_and_branches()
292
+ workspaces: List[Dict[str, Any]] = info["workspaces"]
293
+
294
+ result: Optional[str] = next(
295
+ (w["token"] for w in workspaces if workspace_id in (w.get("id"), w.get("name"))), None
296
+ )
297
+ if host:
298
+ self.set_token_for_host(result, host)
299
+
300
+ return result
301
+
302
+ def spawn(self) -> "CLIConfig":
303
+ return CLIConfig(path=None, parent=self)
304
+
305
+ def update(self, other: Union["CLIConfig", Dict[str, Any]]):
306
+ values = other if isinstance(other, dict) else other._values
307
+ for k, v in values.items():
308
+ self[k] = v
309
+
310
+ @staticmethod
311
+ def get_global_config(_path: Optional[str] = None) -> "CLIConfig":
312
+ """Returns the user-specific config.
313
+
314
+ The data is cached between calls, so feel free to use it freely instead
315
+ of saving a reference.
316
+
317
+ Note: the `_path` argument is mainly intended to help during testing.
318
+ """
319
+ if not CLIConfig._global:
320
+ path: Optional[str] = _path or os.environ.get("XDG_CONFIG_HOME", None)
321
+ if not path:
322
+ path = os.path.join(os.environ.get("HOME", "~"), ".config")
323
+ path = os.path.join(path, APP_CONFIG_NAME, ".tinyb")
324
+ CLIConfig._global = CLIConfig(path, parent=None)
325
+ return CLIConfig._global
326
+
327
+ @staticmethod
328
+ def get_project_config(working_dir: Optional[str] = None) -> "CLIConfig":
329
+ """Returns the project-specific config located at `working_dir` (defaults to `os.getcwd()`)
330
+
331
+ The data is cached between calls, given the same `working_dir`.
332
+ """
333
+ working_dir = working_dir or os.getcwd()
334
+ result = CLIConfig._projects.get(working_dir)
335
+ if not result:
336
+ path: str = os.path.join(working_dir, ".tinyb")
337
+ result = CLIConfig(path, parent=CLIConfig.get_global_config())
338
+ CLIConfig._projects[working_dir] = result
339
+ return result
340
+
341
+ @staticmethod
342
+ def get_llm_config(working_dir: Optional[str] = None) -> Dict[str, Any]:
343
+ return (
344
+ CLIConfig.get_project_config(working_dir)
345
+ .get("llms", {})
346
+ .get("openai", {"model": "gpt-4o-mini", "api_key": None})
347
+ )
348
+
349
+ @staticmethod
350
+ def reset() -> None:
351
+ CLIConfig._global = None
352
+ CLIConfig._projects.clear()
@@ -14,8 +14,8 @@ from click import Context
14
14
 
15
15
  from tinybird.client import DoesNotExistException, TinyB
16
16
  from tinybird.feedback_manager import FeedbackManager
17
- from tinybird.tb_cli_modules.cli import cli
18
- from tinybird.tb_cli_modules.common import (
17
+ from tinybird.tb.modules.cli import cli
18
+ from tinybird.tb.modules.common import (
19
19
  ConnectionReplacements,
20
20
  DataConnectorType,
21
21
  _get_setting_value,
@@ -33,8 +33,8 @@ from tinybird.tb_cli_modules.common import (
33
33
  validate_kafka_secret,
34
34
  validate_string_connector_param,
35
35
  )
36
- from tinybird.tb_cli_modules.exceptions import CLIConnectionException
37
- from tinybird.tb_cli_modules.telemetry import is_ci_environment
36
+ from tinybird.tb.modules.exceptions import CLIConnectionException
37
+ from tinybird.tb.modules.telemetry import is_ci_environment
38
38
 
39
39
  DATA_CONNECTOR_SETTINGS: Dict[DataConnectorType, List[str]] = {
40
40
  DataConnectorType.KAFKA: [
@@ -7,14 +7,15 @@ import click
7
7
  from click import Context
8
8
 
9
9
  from tinybird.client import TinyB
10
- from tinybird.datafile import folder_build
11
10
  from tinybird.feedback_manager import FeedbackManager
12
- from tinybird.tb_cli_modules.cli import cli
13
- from tinybird.tb_cli_modules.common import _generate_datafile, coro, generate_datafile, push_data
14
- from tinybird.tb_cli_modules.config import CLIConfig
15
- from tinybird.tb_cli_modules.exceptions import CLIDatasourceException
16
- from tinybird.tb_cli_modules.llm import LLM
17
- from tinybird.tb_cli_modules.local import (
11
+ from tinybird.tb.modules.cicd import init_cicd
12
+ from tinybird.tb.modules.cli import cli
13
+ from tinybird.tb.modules.common import _generate_datafile, coro, generate_datafile, push_data
14
+ from tinybird.tb.modules.config import CLIConfig
15
+ from tinybird.tb.modules.datafile import folder_build
16
+ from tinybird.tb.modules.exceptions import CLIDatasourceException
17
+ from tinybird.tb.modules.llm import LLM
18
+ from tinybird.tb.modules.local import (
18
19
  get_tinybird_local_client,
19
20
  )
20
21
 
@@ -63,6 +64,9 @@ async def create(
63
64
  datasources,
64
65
  pipes,
65
66
  )
67
+
68
+ await init_cicd(data_project_dir=os.path.relpath(folder))
69
+
66
70
  if data:
67
71
  ds_name = os.path.basename(data.split(".")[0])
68
72
  await append_datasource(ctx, tb_client, ds_name, data, None, None, False, 1)
@@ -58,7 +58,12 @@ from humanfriendly.tables import format_pretty_table
58
58
  from mypy_extensions import KwArg, VarArg
59
59
  from toposort import toposort
60
60
 
61
+ from tinybird.ch_utils.engine import ENABLED_ENGINES
62
+ from tinybird.client import AuthException, AuthNoTokenException, CanNotBeDeletedException, DoesNotExistException, TinyB
61
63
  from tinybird.config import PROJECT_PATHS
64
+ from tinybird.feedback_manager import FeedbackManager
65
+ from tinybird.sql import parse_indexes_structure, parse_table_structure, schema_to_sql_columns
66
+ from tinybird.sql_template import get_template_and_variables, get_used_tables_in_template, render_sql_template
62
67
  from tinybird.sql_template_fmt import DEFAULT_FMT_LINE_LENGTH, format_sql_template
63
68
  from tinybird.syncasync import sync_to_async
64
69
  from tinybird.tb_cli_modules.common import (
@@ -70,13 +75,7 @@ from tinybird.tb_cli_modules.common import (
70
75
  )
71
76
  from tinybird.tb_cli_modules.config import CLIConfig
72
77
  from tinybird.tb_cli_modules.exceptions import CLIGitReleaseException, CLIPipeException
73
-
74
- from .ch_utils.engine import ENABLED_ENGINES
75
- from .client import AuthException, AuthNoTokenException, CanNotBeDeletedException, DoesNotExistException, TinyB
76
- from .feedback_manager import FeedbackManager
77
- from .sql import parse_indexes_structure, parse_table_structure, schema_to_sql_columns
78
- from .sql_template import get_template_and_variables, get_used_tables_in_template, render_sql_template
79
- from .tornado_template import UnClosedIfError
78
+ from tinybird.tornado_template import UnClosedIfError
80
79
 
81
80
  os.environ["GIT_PYTHON_REFRESH"] = "quiet"
82
81
  from git import HEAD, Diff, GitCommandError, InvalidGitRepositoryError, Repo
@@ -15,16 +15,15 @@ from click import Context
15
15
 
16
16
  from tinybird.client import AuthNoTokenException, CanNotBeDeletedException, DoesNotExistException, TinyB
17
17
  from tinybird.config import get_display_host
18
- from tinybird.tb_cli_modules.config import CLIConfig
18
+ from tinybird.tb.modules.config import CLIConfig
19
19
 
20
20
  if TYPE_CHECKING:
21
21
  from tinybird.connectors import Connector
22
22
 
23
- from tinybird.datafile import get_name_version, wait_job
24
23
  from tinybird.feedback_manager import FeedbackManager
25
- from tinybird.tb_cli_modules.branch import warn_if_in_live
26
- from tinybird.tb_cli_modules.cli import cli
27
- from tinybird.tb_cli_modules.common import (
24
+ from tinybird.tb.modules.branch import warn_if_in_live
25
+ from tinybird.tb.modules.cli import cli
26
+ from tinybird.tb.modules.common import (
28
27
  _analyze,
29
28
  _generate_datafile,
30
29
  ask_for_user_token,
@@ -40,8 +39,10 @@ from tinybird.tb_cli_modules.common import (
40
39
  validate_kafka_auto_offset_reset,
41
40
  validate_kafka_group,
42
41
  validate_kafka_topic,
42
+ wait_job,
43
43
  )
44
- from tinybird.tb_cli_modules.exceptions import CLIDatasourceException
44
+ from tinybird.tb.modules.datafile import get_name_version
45
+ from tinybird.tb.modules.exceptions import CLIDatasourceException
45
46
 
46
47
 
47
48
  @cli.group()
@@ -0,0 +1,91 @@
1
+ from typing import Any, Dict, Optional
2
+
3
+ import click
4
+
5
+ from tinybird.tb.modules.telemetry import add_telemetry_event
6
+
7
+
8
+ class CLIException(click.exceptions.ClickException):
9
+ """Default exception for all exceptions raised in the CLI.
10
+
11
+ Allows to specift a custom telemetry event to be sent before
12
+ raising the exception (if not specified, it sends a generic
13
+ `error` beacon with the error message)
14
+ """
15
+
16
+ def __init__(self, message: str, telemetry_event: Optional[str] = None, **kw_telemetry_event_data: Any) -> None:
17
+ telemetry_event = telemetry_event or "error"
18
+ data: Dict[str, Any] = {"error": message}
19
+ data.update(kw_telemetry_event_data)
20
+ add_telemetry_event(telemetry_event, **data)
21
+ super().__init__(message)
22
+
23
+
24
+ class CLIAuthException(CLIException):
25
+ """Exceptions generated by the auth commands"""
26
+
27
+ def __init__(self, message: str, **kw_telemetry_event_data: Any) -> None:
28
+ super().__init__(message, "auth_error", **kw_telemetry_event_data)
29
+
30
+
31
+ class CLIReleaseException(CLIException):
32
+ """Exceptions generated by the release commands"""
33
+
34
+ def __init__(self, message: str, **kw_telemetry_event_data: Any) -> None:
35
+ super().__init__(message, "release_error", **kw_telemetry_event_data)
36
+
37
+
38
+ class CLIBranchException(CLIException):
39
+ """Exceptions generated by the branch commands"""
40
+
41
+ def __init__(self, message: str, **kw_telemetry_event_data: Any) -> None:
42
+ super().__init__(message, "branch_error", **kw_telemetry_event_data)
43
+
44
+
45
+ class CLIGitReleaseException(CLIException):
46
+ """Exceptions generated by the git release related commands"""
47
+
48
+ def __init__(self, message: str, **kw_telemetry_event_data: Any) -> None:
49
+ super().__init__(message, "git_release_error", **kw_telemetry_event_data)
50
+
51
+
52
+ class CLIConnectionException(CLIException):
53
+ """Exceptions generated by the connection commands"""
54
+
55
+ def __init__(self, message: str, **kw_telemetry_event_data: Any) -> None:
56
+ super().__init__(message, "connection_error", **kw_telemetry_event_data)
57
+
58
+
59
+ class CLIDatasourceException(CLIException):
60
+ """Exceptions generated by the datasource commands"""
61
+
62
+ def __init__(self, message: str, **kw_telemetry_event_data: Any) -> None:
63
+ super().__init__(message, "datasource_error", **kw_telemetry_event_data)
64
+
65
+
66
+ class CLIPipeException(CLIException):
67
+ """Exceptions generated by the pipe commands"""
68
+
69
+ def __init__(self, message: str, **kw_telemetry_event_data: Any) -> None:
70
+ super().__init__(message, "pipe_error", **kw_telemetry_event_data)
71
+
72
+
73
+ class CLIWorkspaceMembersException(CLIException):
74
+ """Exceptions generated by the workspace members commands"""
75
+
76
+ def __init__(self, message: str, **kw_telemetry_event_data: Any) -> None:
77
+ super().__init__(message, "workspace_members_error", **kw_telemetry_event_data)
78
+
79
+
80
+ class CLIWorkspaceException(CLIException):
81
+ """Exceptions generated by the workspace commands"""
82
+
83
+ def __init__(self, message: str, **kw_telemetry_event_data: Any) -> None:
84
+ super().__init__(message, "workspace_error", **kw_telemetry_event_data)
85
+
86
+
87
+ class CLITokenException(CLIException):
88
+ """Exceptions generated by the token commands"""
89
+
90
+ def __init__(self, message: str, **kw_telemetry_event_data: Any) -> None:
91
+ super().__init__(message, "token_error", **kw_telemetry_event_data)
@@ -7,10 +7,10 @@ import aiofiles
7
7
  import click
8
8
  from click import Context
9
9
 
10
- from tinybird.datafile import color_diff, format_datasource, format_pipe, is_file_a_datasource, peek
11
10
  from tinybird.feedback_manager import FeedbackManager
12
- from tinybird.tb_cli_modules.cli import cli
13
- from tinybird.tb_cli_modules.common import coro
11
+ from tinybird.tb.modules.cli import cli
12
+ from tinybird.tb.modules.common import coro
13
+ from tinybird.tb.modules.datafile import color_diff, format_datasource, format_pipe, is_file_a_datasource, peek
14
14
 
15
15
 
16
16
  @cli.command()
@@ -8,9 +8,9 @@ from click import Context
8
8
 
9
9
  from tinybird.client import DoesNotExistException, TinyB
10
10
  from tinybird.feedback_manager import FeedbackManager
11
- from tinybird.tb_cli_modules.cli import cli
12
- from tinybird.tb_cli_modules.common import coro, echo_safe_humanfriendly_tables_format_smart_table
13
- from tinybird.tb_cli_modules.exceptions import CLIException
11
+ from tinybird.tb.modules.cli import cli
12
+ from tinybird.tb.modules.common import coro, echo_safe_humanfriendly_tables_format_smart_table
13
+ from tinybird.tb.modules.exceptions import CLIException
14
14
 
15
15
 
16
16
  @cli.group()
@@ -6,7 +6,7 @@ from openai import OpenAI
6
6
  from pydantic import BaseModel
7
7
 
8
8
  from tinybird.client import TinyB
9
- from tinybird.tb_cli_modules.prompts import create_project_prompt, sample_data_sql_prompt
9
+ from tinybird.tb.modules.prompts import create_project_prompt, sample_data_sql_prompt
10
10
 
11
11
 
12
12
  class DataFile(BaseModel):
@@ -6,12 +6,12 @@ import requests
6
6
 
7
7
  import docker
8
8
  from tinybird.feedback_manager import FeedbackManager
9
- from tinybird.tb_cli_modules.cli import cli
10
- from tinybird.tb_cli_modules.common import (
9
+ from tinybird.tb.modules.cli import cli
10
+ from tinybird.tb.modules.common import (
11
11
  coro,
12
12
  )
13
- from tinybird.tb_cli_modules.config import CLIConfig
14
- from tinybird.tb_cli_modules.exceptions import CLIException
13
+ from tinybird.tb.modules.config import CLIConfig
14
+ from tinybird.tb.modules.exceptions import CLIException
15
15
 
16
16
  # TODO: Use the official Tinybird image once it's available 'tinybirdco/tinybird-local:latest'
17
17
  TB_IMAGE_NAME = "registry.gitlab.com/tinybird/analytics/tinybird-local-jammy-3.11:latest"
@@ -114,7 +114,11 @@ def remove_tinybird_local(docker_client):
114
114
  def get_tinybird_local_client():
115
115
  """Get a Tinybird client connected to the local environment."""
116
116
  config = CLIConfig.get_project_config()
117
- tokens = requests.get(f"{TB_LOCAL_HOST}/tokens").json()
117
+ try:
118
+ tokens = requests.get(f"{TB_LOCAL_HOST}/tokens").json()
119
+ except Exception:
120
+ raise CLIException("Tinybird local environment is not running. Please start it with `tb local start`.")
121
+
118
122
  token = tokens["workspace_admin_token"]
119
123
  config.set_token(token)
120
124
  config.set_host(TB_LOCAL_HOST)
@@ -4,11 +4,11 @@ from pathlib import Path
4
4
  import click
5
5
 
6
6
  from tinybird.feedback_manager import FeedbackManager
7
- from tinybird.tb_cli_modules.cli import cli
8
- from tinybird.tb_cli_modules.common import CLIException, coro
9
- from tinybird.tb_cli_modules.config import CLIConfig
10
- from tinybird.tb_cli_modules.llm import LLM
11
- from tinybird.tb_cli_modules.local import get_tinybird_local_client
7
+ from tinybird.tb.modules.cli import cli
8
+ from tinybird.tb.modules.common import CLIException, coro
9
+ from tinybird.tb.modules.config import CLIConfig
10
+ from tinybird.tb.modules.llm import LLM
11
+ from tinybird.tb.modules.local import get_tinybird_local_client
12
12
 
13
13
 
14
14
  @cli.command()
@@ -16,12 +16,12 @@ from click import Context
16
16
  import tinybird.context as context
17
17
  from tinybird.client import AuthNoTokenException, DoesNotExistException, TinyB
18
18
  from tinybird.config import DEFAULT_API_HOST, FeatureFlags
19
- from tinybird.datafile import PipeNodeTypes, PipeTypes, folder_push, get_name_version, process_file, wait_job
20
19
  from tinybird.feedback_manager import FeedbackManager
21
- from tinybird.tb_cli_modules.branch import warn_if_in_live
22
- from tinybird.tb_cli_modules.cli import cli
23
- from tinybird.tb_cli_modules.common import coro, create_tb_client, echo_safe_humanfriendly_tables_format_smart_table
24
- from tinybird.tb_cli_modules.exceptions import CLIPipeException
20
+ from tinybird.tb.modules.branch import warn_if_in_live
21
+ from tinybird.tb.modules.cli import cli
22
+ from tinybird.tb.modules.common import coro, create_tb_client, echo_safe_humanfriendly_tables_format_smart_table
23
+ from tinybird.tb.modules.datafile import PipeNodeTypes, PipeTypes, folder_push, get_name_version, process_file, wait_job
24
+ from tinybird.tb.modules.exceptions import CLIPipeException
25
25
 
26
26
 
27
27
  @cli.group()
@@ -0,0 +1,9 @@
1
+ from typing import Optional, TypedDict
2
+
3
+
4
+ class Region(TypedDict):
5
+ name: str
6
+ provider: str
7
+ api_host: str
8
+ host: str
9
+ default_password: Optional[str]