tinybird 0.0.1.dev71__py3-none-any.whl → 0.0.1.dev73__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/client.py +7 -5
- tinybird/feedback_manager.py +2 -2
- tinybird/prompts.py +56 -8
- tinybird/tb/__cli__.py +2 -2
- tinybird/tb/cli.py +1 -0
- tinybird/tb/modules/build.py +2 -2
- tinybird/tb/modules/cicd.py +1 -1
- tinybird/tb/modules/cli.py +12 -12
- tinybird/tb/modules/common.py +3 -4
- tinybird/tb/modules/config.py +3 -3
- tinybird/tb/modules/copy.py +1 -1
- tinybird/tb/modules/datafile/build_pipe.py +35 -186
- tinybird/tb/modules/datafile/playground.py +1400 -0
- tinybird/tb/modules/datasource.py +1 -1
- tinybird/tb/modules/deployment.py +10 -1
- tinybird/tb/modules/endpoint.py +10 -6
- tinybird/tb/modules/feedback_manager.py +2 -2
- tinybird/tb/modules/fmt.py +2 -2
- tinybird/tb/modules/local.py +2 -2
- tinybird/tb/modules/local_common.py +3 -3
- tinybird/tb/modules/materialization.py +1 -1
- tinybird/tb/modules/mock.py +1 -1
- tinybird/tb/modules/pipe.py +1 -1
- tinybird/tb/modules/playground.py +135 -0
- tinybird/tb/modules/shell.py +6 -5
- tinybird/tb/modules/token.py +17 -3
- tinybird/tb/modules/watch.py +105 -1
- tinybird/tb/modules/workspace.py +1 -1
- {tinybird-0.0.1.dev71.dist-info → tinybird-0.0.1.dev73.dist-info}/METADATA +1 -1
- {tinybird-0.0.1.dev71.dist-info → tinybird-0.0.1.dev73.dist-info}/RECORD +33 -31
- {tinybird-0.0.1.dev71.dist-info → tinybird-0.0.1.dev73.dist-info}/WHEEL +0 -0
- {tinybird-0.0.1.dev71.dist-info → tinybird-0.0.1.dev73.dist-info}/entry_points.txt +0 -0
- {tinybird-0.0.1.dev71.dist-info → tinybird-0.0.1.dev73.dist-info}/top_level.txt +0 -0
|
@@ -352,7 +352,16 @@ def create_deployment(
|
|
|
352
352
|
click.echo(FeedbackManager.success(message="\n✓ Deployment is valid"))
|
|
353
353
|
else:
|
|
354
354
|
click.echo(FeedbackManager.error(message="\n✗ Deployment is not valid"))
|
|
355
|
-
|
|
355
|
+
deploy_errors = result.get("errors")
|
|
356
|
+
for deploy_error in deploy_errors:
|
|
357
|
+
if deploy_error.get("filename", None):
|
|
358
|
+
click.echo(
|
|
359
|
+
FeedbackManager.error(
|
|
360
|
+
message=f"{deploy_error.get('filename')}\n\n{deploy_error.get('error')}"
|
|
361
|
+
)
|
|
362
|
+
)
|
|
363
|
+
else:
|
|
364
|
+
click.echo(FeedbackManager.error(message=f"{deploy_error.get('error')}"))
|
|
356
365
|
return
|
|
357
366
|
|
|
358
367
|
deploy_result = result.get("result")
|
tinybird/tb/modules/endpoint.py
CHANGED
|
@@ -25,7 +25,7 @@ from tinybird.tb.modules.feedback_manager import FeedbackManager
|
|
|
25
25
|
@cli.group()
|
|
26
26
|
@click.pass_context
|
|
27
27
|
def endpoint(ctx):
|
|
28
|
-
"""Endpoint commands"""
|
|
28
|
+
"""Endpoint commands."""
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
@endpoint.command(name="ls")
|
|
@@ -56,7 +56,7 @@ async def endpoint_ls(ctx: Context, match: str, format_: str):
|
|
|
56
56
|
if pattern and not pattern.search(tk["name"]):
|
|
57
57
|
continue
|
|
58
58
|
token = get_endpoint_token(tokens, tk["name"]) or client.token
|
|
59
|
-
endpoint_url = build_endpoint_url(client, tk["name"], token)
|
|
59
|
+
endpoint_url = build_endpoint_url(client, tk["name"], token, client.staging)
|
|
60
60
|
table_human_readable.append((tk["name"], t["updated_at"][:-7], len(t["nodes"]), endpoint_url))
|
|
61
61
|
table_machine_readable.append(
|
|
62
62
|
{
|
|
@@ -166,7 +166,7 @@ async def endpoint_url(ctx: Context, pipe: str, language: str):
|
|
|
166
166
|
client: TinyB = ctx.ensure_object(dict)["client"]
|
|
167
167
|
tokens = await client.tokens()
|
|
168
168
|
token = get_endpoint_token(tokens, pipe) or client.token
|
|
169
|
-
click.echo(build_endpoint_snippet(client, pipe, token, language))
|
|
169
|
+
click.echo(build_endpoint_snippet(client, pipe, token, language, client.staging))
|
|
170
170
|
if language != "http":
|
|
171
171
|
click.echo(FeedbackManager.success(message="\n✓ Code snippet copied to clipboard!\n"))
|
|
172
172
|
|
|
@@ -269,8 +269,10 @@ async def endpoint_stats(ctx: click.Context, pipes: Tuple[str, ...], format_: st
|
|
|
269
269
|
echo_safe_humanfriendly_tables_format_smart_table(table_human_readable, column_names=columns)
|
|
270
270
|
|
|
271
271
|
|
|
272
|
-
def build_endpoint_snippet(
|
|
273
|
-
|
|
272
|
+
def build_endpoint_snippet(
|
|
273
|
+
tb_client: TinyB, pipe_name: str, token: str, language: str, staging: bool = False
|
|
274
|
+
) -> Optional[str]:
|
|
275
|
+
endpoint_url = build_endpoint_url(tb_client, pipe_name, token, staging)
|
|
274
276
|
if language == "http":
|
|
275
277
|
return endpoint_url
|
|
276
278
|
|
|
@@ -293,13 +295,15 @@ def build_endpoint_snippet(tb_client: TinyB, pipe_name: str, token: str, languag
|
|
|
293
295
|
return snippet
|
|
294
296
|
|
|
295
297
|
|
|
296
|
-
def build_endpoint_url(tb_client: TinyB, pipe_name: str, token: str) -> str:
|
|
298
|
+
def build_endpoint_url(tb_client: TinyB, pipe_name: str, token: str, staging: bool = False) -> str:
|
|
297
299
|
example_params = {
|
|
298
300
|
"format": "json",
|
|
299
301
|
"pipe": pipe_name,
|
|
300
302
|
"q": "",
|
|
301
303
|
"token": token,
|
|
302
304
|
}
|
|
305
|
+
if staging:
|
|
306
|
+
example_params["__tb__deployment"] = "staging"
|
|
303
307
|
response = requests.get(f"{tb_client.host}/examples/query.http?{urlencode(example_params)}")
|
|
304
308
|
return response.text.replace("http://localhost:8001", tb_client.host)
|
|
305
309
|
|
|
@@ -466,7 +466,7 @@ class FeedbackManager:
|
|
|
466
466
|
info_releases_detected = info_message("** Live Release: {current_semver} => New Release: {semver}")
|
|
467
467
|
info_dry_releases_detected = info_message("** [DRY RUN] Live Release: {current_semver} => New Release: {semver}")
|
|
468
468
|
info_pre_prompt_auth_login_user_token = info_message(
|
|
469
|
-
"ℹ️ In order to log into Tinybird, you need your user token. Please, go to {host}/tokens/ and paste your User Token here."
|
|
469
|
+
"ℹ️ In order to log into Tinybird, you need your user token. Please, go to {host}/tokens/ and paste your User Token here." # noqa: RUF001
|
|
470
470
|
)
|
|
471
471
|
prompt_auth_login_user_token = prompt_message("❓ User token:")
|
|
472
472
|
|
|
@@ -617,7 +617,7 @@ Ready? """
|
|
|
617
617
|
)
|
|
618
618
|
warning_datasource_sync = warning_message("** Warning: Do you want to sync the Data Source {datasource}?")
|
|
619
619
|
warning_datasource_sync_bucket = warning_message(
|
|
620
|
-
"** Warning: Do you want to ingest all the selected files (new and previous) from the {datasource} bucket? Be aware that data might be duplicated if
|
|
620
|
+
"** Warning: Do you want to ingest all the selected files (new and previous) from the {datasource} bucket? Be aware that data might be duplicated if you have previously ingested those files.\n"
|
|
621
621
|
)
|
|
622
622
|
warning_users_dont_exist = warning_message(
|
|
623
623
|
"** Warning: The following users do not exist in the workspace {workspace}: {users}"
|
tinybird/tb/modules/fmt.py
CHANGED
|
@@ -38,13 +38,13 @@ async def fmt(
|
|
|
38
38
|
ctx: Context, filenames: List[str], line_length: int, dry_run: bool, yes: bool, diff: bool
|
|
39
39
|
) -> Optional[str]:
|
|
40
40
|
"""
|
|
41
|
-
Formats a .datasource, .pipe or .incl file
|
|
41
|
+
Formats a .datasource, .pipe or .incl file.
|
|
42
42
|
|
|
43
43
|
This command removes comments starting with # from the file, use DESCRIPTION instead.
|
|
44
44
|
|
|
45
45
|
The format command tries to parse the datafile so syntax errors might rise.
|
|
46
46
|
|
|
47
|
-
.incl files must contain a NODE definition
|
|
47
|
+
.incl files must contain a NODE definition.
|
|
48
48
|
"""
|
|
49
49
|
|
|
50
50
|
result = ""
|
tinybird/tb/modules/local.py
CHANGED
|
@@ -109,7 +109,7 @@ def remove_tinybird_local(docker_client):
|
|
|
109
109
|
|
|
110
110
|
@cli.command()
|
|
111
111
|
def upgrade():
|
|
112
|
-
"""Upgrade Tinybird CLI to the latest version"""
|
|
112
|
+
"""Upgrade Tinybird CLI to the latest version."""
|
|
113
113
|
click.echo(FeedbackManager.highlight(message="» Upgrading Tinybird CLI..."))
|
|
114
114
|
process = subprocess.Popen(
|
|
115
115
|
[f"{os.getenv('HOME')}/.local/bin/uv", "tool", "upgrade", "tinybird"],
|
|
@@ -127,7 +127,7 @@ def upgrade():
|
|
|
127
127
|
@cli.group()
|
|
128
128
|
@click.pass_context
|
|
129
129
|
def local(ctx):
|
|
130
|
-
"""
|
|
130
|
+
"""Manage the local Tinybird instance."""
|
|
131
131
|
|
|
132
132
|
|
|
133
133
|
@local.command()
|
|
@@ -11,14 +11,14 @@ from tinybird.tb.modules.exceptions import CLIException
|
|
|
11
11
|
|
|
12
12
|
TB_IMAGE_NAME = "tinybirdco/tinybird-local:beta"
|
|
13
13
|
TB_CONTAINER_NAME = "tinybird-local"
|
|
14
|
-
TB_LOCAL_PORT = int(os.getenv("TB_LOCAL_PORT",
|
|
14
|
+
TB_LOCAL_PORT = int(os.getenv("TB_LOCAL_PORT", 7181))
|
|
15
15
|
TB_LOCAL_HOST = f"http://localhost:{TB_LOCAL_PORT}"
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
async def get_tinybird_local_client(config_obj: Dict[str, Any], build: bool = False,
|
|
18
|
+
async def get_tinybird_local_client(config_obj: Dict[str, Any], build: bool = False, staging: bool = False) -> TinyB:
|
|
19
19
|
"""Get a Tinybird client connected to the local environment."""
|
|
20
20
|
config = await get_tinybird_local_config(config_obj, build=build)
|
|
21
|
-
return config.get_client(host=TB_LOCAL_HOST,
|
|
21
|
+
return config.get_client(host=TB_LOCAL_HOST, staging=staging)
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
async def get_tinybird_local_config(config_obj: Dict[str, Any], build: bool = False) -> CLIConfig:
|
tinybird/tb/modules/mock.py
CHANGED
|
@@ -27,7 +27,7 @@ from tinybird.tb.modules.project import Project
|
|
|
27
27
|
@click.pass_context
|
|
28
28
|
@coro
|
|
29
29
|
async def mock(ctx: click.Context, datasource: str, rows: int, prompt: str) -> None:
|
|
30
|
-
"""Generate sample data for a
|
|
30
|
+
"""Generate sample data for a data source.
|
|
31
31
|
|
|
32
32
|
Args:
|
|
33
33
|
datasource: Path to the datasource file to load sample data into
|
tinybird/tb/modules/pipe.py
CHANGED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import threading
|
|
3
|
+
import time
|
|
4
|
+
from copy import deepcopy
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import List
|
|
7
|
+
from urllib.parse import urlencode
|
|
8
|
+
|
|
9
|
+
import click
|
|
10
|
+
|
|
11
|
+
import tinybird.context as context
|
|
12
|
+
from tinybird.client import TinyB
|
|
13
|
+
from tinybird.tb.modules.cli import cli
|
|
14
|
+
from tinybird.tb.modules.config import CLIConfig
|
|
15
|
+
from tinybird.tb.modules.datafile.exceptions import ParseException
|
|
16
|
+
from tinybird.tb.modules.datafile.parse_datasource import parse_datasource
|
|
17
|
+
from tinybird.tb.modules.datafile.parse_pipe import parse_pipe
|
|
18
|
+
from tinybird.tb.modules.datafile.playground import folder_playground
|
|
19
|
+
from tinybird.tb.modules.feedback_manager import FeedbackManager
|
|
20
|
+
from tinybird.tb.modules.project import Project
|
|
21
|
+
from tinybird.tb.modules.shell import Shell, print_table_formatted
|
|
22
|
+
from tinybird.tb.modules.watch import watch_files
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def is_vendor(f: Path) -> bool:
|
|
26
|
+
return f.parts[0] == "vendor"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def get_vendor_workspace(f: Path) -> str:
|
|
30
|
+
return f.parts[1]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def is_endpoint(f: Path) -> bool:
|
|
34
|
+
return f.suffix == ".pipe" and not is_vendor(f) and f.parts[0] == "endpoints"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def is_pipe(f: Path) -> bool:
|
|
38
|
+
return f.suffix == ".pipe" and not is_vendor(f)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def check_filenames(filenames: List[str]):
|
|
42
|
+
parser_matrix = {".pipe": parse_pipe, ".datasource": parse_datasource}
|
|
43
|
+
incl_suffix = ".incl"
|
|
44
|
+
|
|
45
|
+
for filename in filenames:
|
|
46
|
+
file_suffix = Path(filename).suffix
|
|
47
|
+
if file_suffix == incl_suffix:
|
|
48
|
+
continue
|
|
49
|
+
|
|
50
|
+
parser = parser_matrix.get(file_suffix)
|
|
51
|
+
if not parser:
|
|
52
|
+
raise ParseException(FeedbackManager.error_unsupported_datafile(extension=file_suffix))
|
|
53
|
+
|
|
54
|
+
parser(filename)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@cli.command()
|
|
58
|
+
@click.pass_context
|
|
59
|
+
def playground(
|
|
60
|
+
ctx: click.Context,
|
|
61
|
+
) -> None:
|
|
62
|
+
"""Build the project in Tinybird Local."""
|
|
63
|
+
project: Project = ctx.ensure_object(dict)["project"]
|
|
64
|
+
tb_client: TinyB = ctx.ensure_object(dict)["client"]
|
|
65
|
+
context.disable_template_security_validation.set(True)
|
|
66
|
+
|
|
67
|
+
async def process(filenames: List[str], watch: bool = False):
|
|
68
|
+
datafiles = [f for f in filenames if f.endswith(".datasource") or f.endswith(".pipe")]
|
|
69
|
+
if len(datafiles) > 0:
|
|
70
|
+
check_filenames(filenames=datafiles)
|
|
71
|
+
await folder_playground(
|
|
72
|
+
project, tb_client, filenames=datafiles, is_internal=False, current_ws=None, local_ws=None
|
|
73
|
+
)
|
|
74
|
+
if len(filenames) > 0 and watch:
|
|
75
|
+
filename = filenames[0]
|
|
76
|
+
await build_and_print_resource(tb_client, filename)
|
|
77
|
+
|
|
78
|
+
datafiles = project.get_project_files()
|
|
79
|
+
filenames = datafiles
|
|
80
|
+
|
|
81
|
+
async def build_once(filenames: List[str]):
|
|
82
|
+
ok = False
|
|
83
|
+
try:
|
|
84
|
+
click.echo(FeedbackManager.highlight(message="» Building project...\n"))
|
|
85
|
+
time_start = time.time()
|
|
86
|
+
await process(filenames=filenames, watch=False)
|
|
87
|
+
time_end = time.time()
|
|
88
|
+
elapsed_time = time_end - time_start
|
|
89
|
+
|
|
90
|
+
click.echo(FeedbackManager.success(message=f"\n✓ Build completed in {elapsed_time:.1f}s"))
|
|
91
|
+
ok = True
|
|
92
|
+
except Exception as e:
|
|
93
|
+
error_path = Path(".tb_error.txt")
|
|
94
|
+
if error_path.exists():
|
|
95
|
+
content = error_path.read_text()
|
|
96
|
+
content += f"\n\n{str(e)}"
|
|
97
|
+
error_path.write_text(content)
|
|
98
|
+
else:
|
|
99
|
+
error_path.write_text(str(e))
|
|
100
|
+
click.echo(FeedbackManager.error_exception(error=e))
|
|
101
|
+
ok = False
|
|
102
|
+
return ok
|
|
103
|
+
|
|
104
|
+
build_ok = asyncio.run(build_once(filenames))
|
|
105
|
+
|
|
106
|
+
shell = Shell(project=project, tb_client=tb_client, playground=True)
|
|
107
|
+
click.echo(FeedbackManager.gray(message="\nWatching for changes..."))
|
|
108
|
+
watcher_thread = threading.Thread(
|
|
109
|
+
target=watch_files, args=(filenames, process, shell, project, build_ok), daemon=True
|
|
110
|
+
)
|
|
111
|
+
watcher_thread.start()
|
|
112
|
+
shell.run()
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
async def build_and_print_resource(tb_client: TinyB, filename: str):
|
|
116
|
+
resource_path = Path(filename)
|
|
117
|
+
name = resource_path.stem
|
|
118
|
+
playground_name = name if filename.endswith(".pipe") else None
|
|
119
|
+
config = CLIConfig().get_project_config()
|
|
120
|
+
user_client = deepcopy(tb_client)
|
|
121
|
+
user_client.token = config.get_user_token() or ""
|
|
122
|
+
cli_params = {}
|
|
123
|
+
cli_params["workspace_id"] = config.get("id", None)
|
|
124
|
+
data = await user_client._req(f"/v0/playgrounds?{urlencode(cli_params)}")
|
|
125
|
+
playgrounds = data["playgrounds"]
|
|
126
|
+
playground = next((p for p in playgrounds if p["name"] == (f"{playground_name}" + "__tb__playground")), None)
|
|
127
|
+
if not playground:
|
|
128
|
+
return
|
|
129
|
+
playground_id = playground["id"]
|
|
130
|
+
last_node = playground["nodes"][-1]
|
|
131
|
+
if not last_node:
|
|
132
|
+
return
|
|
133
|
+
node_sql = last_node["sql"]
|
|
134
|
+
res = await tb_client.query(f"{node_sql} FORMAT JSON", playground=playground_id)
|
|
135
|
+
print_table_formatted(res, name)
|
tinybird/tb/modules/shell.py
CHANGED
|
@@ -194,11 +194,12 @@ def _(event):
|
|
|
194
194
|
|
|
195
195
|
|
|
196
196
|
class Shell:
|
|
197
|
-
def __init__(self, project: Project, tb_client: TinyB):
|
|
197
|
+
def __init__(self, project: Project, tb_client: TinyB, playground: bool = False):
|
|
198
198
|
self.history = self.get_history()
|
|
199
199
|
self.project = project
|
|
200
200
|
self.tb_client = tb_client
|
|
201
|
-
self.
|
|
201
|
+
self.env = "cloud" if playground else "build"
|
|
202
|
+
self.prompt_message = "\ntb » "
|
|
202
203
|
self.session: PromptSession = PromptSession(
|
|
203
204
|
completer=DynamicCompleter(project),
|
|
204
205
|
complete_style=CompleteStyle.COLUMN,
|
|
@@ -266,7 +267,7 @@ class Shell:
|
|
|
266
267
|
def handle_mock(self, arg):
|
|
267
268
|
if "mock" in arg.strip().lower():
|
|
268
269
|
arg = arg.replace("mock", "")
|
|
269
|
-
subprocess.run(f"tb --
|
|
270
|
+
subprocess.run(f"tb --{self.env} mock {arg}", shell=True, text=True)
|
|
270
271
|
|
|
271
272
|
def handle_tb(self, argline):
|
|
272
273
|
click.echo("")
|
|
@@ -283,7 +284,7 @@ class Shell:
|
|
|
283
284
|
need_skip = ("mock", "test create", "create")
|
|
284
285
|
if any(arg.startswith(cmd) for cmd in need_skip):
|
|
285
286
|
argline = f"{argline}"
|
|
286
|
-
subprocess.run(f"tb --
|
|
287
|
+
subprocess.run(f"tb --{self.env} {argline}", shell=True, text=True)
|
|
287
288
|
|
|
288
289
|
def default(self, argline):
|
|
289
290
|
click.echo("")
|
|
@@ -298,7 +299,7 @@ class Shell:
|
|
|
298
299
|
need_skip = ("mock", "test create", "create")
|
|
299
300
|
if any(arg.startswith(cmd) for cmd in need_skip):
|
|
300
301
|
argline = f"{argline}"
|
|
301
|
-
subprocess.run(f"tb --
|
|
302
|
+
subprocess.run(f"tb --{self.env} {argline}", shell=True, text=True)
|
|
302
303
|
|
|
303
304
|
def run_sql(self, query, rows_limit=20):
|
|
304
305
|
try:
|
tinybird/tb/modules/token.py
CHANGED
|
@@ -17,7 +17,7 @@ from tinybird.tb.modules.exceptions import CLITokenException
|
|
|
17
17
|
from tinybird.tb.modules.feedback_manager import FeedbackManager
|
|
18
18
|
|
|
19
19
|
|
|
20
|
-
@cli.group(
|
|
20
|
+
@cli.group()
|
|
21
21
|
@click.pass_context
|
|
22
22
|
def token(ctx: Context) -> None:
|
|
23
23
|
"""Token commands."""
|
|
@@ -341,10 +341,24 @@ async def create_static_token(ctx, name: str):
|
|
|
341
341
|
raise CLITokenException("Unknown error")
|
|
342
342
|
|
|
343
343
|
try:
|
|
344
|
-
|
|
344
|
+
token = None
|
|
345
|
+
try:
|
|
346
|
+
click.echo(FeedbackManager.highlight(message=f"\n» Checking if token '{name}' exists..."))
|
|
347
|
+
token = await client.token_get(name)
|
|
348
|
+
except Exception:
|
|
349
|
+
pass
|
|
350
|
+
if token:
|
|
351
|
+
click.echo(FeedbackManager.info(message=f"* Token '{name}' found, updating it..."))
|
|
352
|
+
await client.alter_tokens(name, scoped_parsed)
|
|
353
|
+
else:
|
|
354
|
+
click.echo(FeedbackManager.info(message=f"* Token '{name}' not found, creating it..."))
|
|
355
|
+
await client.create_token(name, scoped_parsed, origin_code=None)
|
|
345
356
|
except AuthNoTokenException:
|
|
346
357
|
raise
|
|
347
358
|
except Exception as e:
|
|
348
359
|
raise CLITokenException(FeedbackManager.error_exception(error=e))
|
|
349
360
|
|
|
350
|
-
|
|
361
|
+
if token:
|
|
362
|
+
click.echo(FeedbackManager.success(message=f"✓ Token '{name}' updated successfully"))
|
|
363
|
+
else:
|
|
364
|
+
click.echo(FeedbackManager.success(message=f"✓ Token '{name}' generated successfully"))
|
tinybird/tb/modules/watch.py
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
|
+
import asyncio
|
|
1
2
|
import os
|
|
2
3
|
import time
|
|
3
4
|
from pathlib import Path
|
|
4
|
-
from typing import Any, Callable, Optional, Union
|
|
5
|
+
from typing import Any, Callable, List, Optional, Union
|
|
5
6
|
|
|
6
7
|
import click
|
|
7
8
|
from watchdog.events import (
|
|
8
9
|
DirDeletedEvent,
|
|
10
|
+
DirMovedEvent,
|
|
9
11
|
FileDeletedEvent,
|
|
12
|
+
FileMovedEvent,
|
|
10
13
|
FileSystemEventHandler,
|
|
11
14
|
)
|
|
12
15
|
from watchdog.observers import Observer
|
|
@@ -136,3 +139,104 @@ def watch_project(
|
|
|
136
139
|
observer.stop()
|
|
137
140
|
|
|
138
141
|
observer.join()
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
class FileChangeHandler(FileSystemEventHandler):
|
|
145
|
+
def __init__(self, filenames: List[str], process: Callable[[List[str]], None], build_ok: bool):
|
|
146
|
+
self.unprocessed_filenames = [os.path.abspath(f) for f in filenames]
|
|
147
|
+
self.process = process
|
|
148
|
+
self.build_ok = build_ok
|
|
149
|
+
|
|
150
|
+
@property
|
|
151
|
+
def filenames(self) -> List[str]:
|
|
152
|
+
return [f for f in self.unprocessed_filenames if os.path.exists(f)]
|
|
153
|
+
|
|
154
|
+
def should_process(self, event: Any) -> Optional[str]:
|
|
155
|
+
if event.is_directory:
|
|
156
|
+
return None
|
|
157
|
+
|
|
158
|
+
def should_process_path(path: str) -> bool:
|
|
159
|
+
if not os.path.exists(path):
|
|
160
|
+
return False
|
|
161
|
+
is_vendor = "vendor/" in path
|
|
162
|
+
if is_vendor:
|
|
163
|
+
return False
|
|
164
|
+
return any(path.endswith(ext) for ext in [".datasource", ".pipe", ".ndjson"])
|
|
165
|
+
|
|
166
|
+
if should_process_path(event.src_path):
|
|
167
|
+
return event.src_path
|
|
168
|
+
|
|
169
|
+
if should_process_path(event.dest_path):
|
|
170
|
+
return event.dest_path
|
|
171
|
+
|
|
172
|
+
return None
|
|
173
|
+
|
|
174
|
+
def on_modified(self, event: Any) -> None:
|
|
175
|
+
if path := self.should_process(event):
|
|
176
|
+
filename = path.split("/")[-1]
|
|
177
|
+
click.echo(FeedbackManager.highlight(message=f"\n\n⟲ Changes detected in {filename}\n"))
|
|
178
|
+
try:
|
|
179
|
+
to_process = [path] if self.build_ok else self.filenames
|
|
180
|
+
self.process(to_process)
|
|
181
|
+
self.build_ok = True
|
|
182
|
+
except Exception as e:
|
|
183
|
+
click.echo(FeedbackManager.error_exception(error=e))
|
|
184
|
+
|
|
185
|
+
def on_moved(self, event: Union[DirMovedEvent, FileMovedEvent]) -> None:
|
|
186
|
+
if path := self.should_process(event):
|
|
187
|
+
is_new_file = False
|
|
188
|
+
if path not in self.unprocessed_filenames:
|
|
189
|
+
is_new_file = True
|
|
190
|
+
self.unprocessed_filenames.append(path)
|
|
191
|
+
|
|
192
|
+
filename = path.split("/")[-1]
|
|
193
|
+
if is_new_file:
|
|
194
|
+
click.echo(FeedbackManager.highlight(message=f"\n\n⟲ New file detected: {filename}\n"))
|
|
195
|
+
else:
|
|
196
|
+
click.echo(FeedbackManager.highlight(message=f"\n\n⟲ Changes detected in {filename}\n"))
|
|
197
|
+
try:
|
|
198
|
+
should_rebuild_all = is_new_file or not self.build_ok
|
|
199
|
+
to_process = self.filenames if should_rebuild_all else [path]
|
|
200
|
+
self.process(to_process)
|
|
201
|
+
self.build_ok = True
|
|
202
|
+
except Exception as e:
|
|
203
|
+
click.echo(FeedbackManager.error_exception(error=e))
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def watch_files(
|
|
207
|
+
filenames: List[str],
|
|
208
|
+
process: Callable,
|
|
209
|
+
shell: Shell,
|
|
210
|
+
project: Project,
|
|
211
|
+
build_ok: bool,
|
|
212
|
+
) -> None:
|
|
213
|
+
# Handle both sync and async process functions
|
|
214
|
+
async def process_wrapper(files: List[str]) -> None:
|
|
215
|
+
click.echo(FeedbackManager.highlight(message="» Rebuilding project..."))
|
|
216
|
+
time_start = time.time()
|
|
217
|
+
if asyncio.iscoroutinefunction(process):
|
|
218
|
+
await process(files, watch=True)
|
|
219
|
+
else:
|
|
220
|
+
process(files, watch=True)
|
|
221
|
+
time_end = time.time()
|
|
222
|
+
elapsed_time = time_end - time_start
|
|
223
|
+
click.echo(
|
|
224
|
+
FeedbackManager.success(message="\n✓ ")
|
|
225
|
+
+ FeedbackManager.gray(message=f"Rebuild completed in {elapsed_time:.1f}s")
|
|
226
|
+
)
|
|
227
|
+
shell.reprint_prompt()
|
|
228
|
+
|
|
229
|
+
event_handler = FileChangeHandler(filenames, lambda f: asyncio.run(process_wrapper(f)), build_ok)
|
|
230
|
+
observer = Observer()
|
|
231
|
+
|
|
232
|
+
observer.schedule(event_handler, path=str(project.path), recursive=True)
|
|
233
|
+
|
|
234
|
+
observer.start()
|
|
235
|
+
|
|
236
|
+
try:
|
|
237
|
+
while True:
|
|
238
|
+
time.sleep(1)
|
|
239
|
+
except KeyboardInterrupt:
|
|
240
|
+
observer.stop()
|
|
241
|
+
|
|
242
|
+
observer.join()
|
tinybird/tb/modules/workspace.py
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
tinybird/__cli__.py,sha256=esPl5QDTzuQgHe5FuxWLm-fURFigGGwjnYLh9GuWUw4,232
|
|
2
|
-
tinybird/client.py,sha256=
|
|
2
|
+
tinybird/client.py,sha256=EX2Z3AxZ9aG3c1qAFe1l48quiu19RfTV7TTuFelxjB4,52258
|
|
3
3
|
tinybird/config.py,sha256=cd_RH7ZjqGjpWwu0efPkhS8VxD9K6Jix4QY2W3w1Pvs,5811
|
|
4
4
|
tinybird/connectors.py,sha256=7Gjms7b5MAaBFGi3xytsJurCylprONpFcYrzp4Fw2Rc,15241
|
|
5
5
|
tinybird/context.py,sha256=VaMhyHruH-uyMypPDfxtuo4scS18b7rxCCdeUVm6ysg,1301
|
|
6
6
|
tinybird/datatypes.py,sha256=XNypumfqNjsvLJ5iNXnbVHRvAJe0aQwI3lS6Cxox-e0,10979
|
|
7
|
-
tinybird/feedback_manager.py,sha256=
|
|
7
|
+
tinybird/feedback_manager.py,sha256=Lsni8G80Qtv_KT_oUFJliQAg3zULFglyhUL27Q6U4cM,68005
|
|
8
8
|
tinybird/git_settings.py,sha256=Sw_8rGmribEFJ4Z_6idrVytxpFYk7ez8ei0qHULzs3E,3934
|
|
9
|
-
tinybird/prompts.py,sha256=
|
|
9
|
+
tinybird/prompts.py,sha256=rDIrkK5S1NJ0bIFjULsCYnGApv9Vc0jmR4tdRuJhM8c,32953
|
|
10
10
|
tinybird/sql.py,sha256=LBi74GxhNAYTb6m2-KNGpAkguSKh7rcvBbERbE7nalA,46195
|
|
11
11
|
tinybird/sql_template.py,sha256=hHz-EbHWlWVMS4e6hTpoieJ1qRAzzZLDq9-fnjT60dw,95732
|
|
12
12
|
tinybird/sql_template_fmt.py,sha256=KUHdj5rYCYm_rKKdXYSJAE9vIyXUQLB0YSZnUXHeBlY,10196
|
|
@@ -15,46 +15,47 @@ tinybird/syncasync.py,sha256=IPnOx6lMbf9SNddN1eBtssg8vCLHMt76SuZ6YNYm-Yk,27761
|
|
|
15
15
|
tinybird/tornado_template.py,sha256=KmW_VD7y-NVqrc8YZKwIaxoJB0XpCcB2izdmxmtmApM,41944
|
|
16
16
|
tinybird/ch_utils/constants.py,sha256=aYvg2C_WxYWsnqPdZB1ZFoIr8ZY-XjUXYyHKE9Ansj0,3890
|
|
17
17
|
tinybird/ch_utils/engine.py,sha256=AUAww-KjGOZg9h0IBlKA3FeacJYB4rOtqcTGJhFM-g8,40392
|
|
18
|
-
tinybird/tb/__cli__.py,sha256=
|
|
19
|
-
tinybird/tb/cli.py,sha256=
|
|
18
|
+
tinybird/tb/__cli__.py,sha256=iBIkon30EHUzmwm7ZXmeuOtNqm--pBPvxefxoQQVbV0,251
|
|
19
|
+
tinybird/tb/cli.py,sha256=KpJ_-V6xVEzcdPRPnHhEdh2EgRPCyhZnfJVqeqMsftI,964
|
|
20
20
|
tinybird/tb/modules/auth.py,sha256=vBA-KsrjAp77kFunGSM-4o7AFdfO7ac4dnrHKrx0alI,9020
|
|
21
|
-
tinybird/tb/modules/build.py,sha256=
|
|
22
|
-
tinybird/tb/modules/cicd.py,sha256=
|
|
23
|
-
tinybird/tb/modules/cli.py,sha256=
|
|
24
|
-
tinybird/tb/modules/common.py,sha256=
|
|
25
|
-
tinybird/tb/modules/config.py,sha256=
|
|
26
|
-
tinybird/tb/modules/copy.py,sha256=
|
|
21
|
+
tinybird/tb/modules/build.py,sha256=mbhb-3Ga5zJx64_QkUzt-TSwKxr0jqcr8X93HZ3Am5k,9693
|
|
22
|
+
tinybird/tb/modules/cicd.py,sha256=Eyut5VsOtXq5hK9g_nGCJ2epfEMDDy2cX3kI0NPJZZA,5355
|
|
23
|
+
tinybird/tb/modules/cli.py,sha256=zCVzffD3YIWB9y06SUf8Ir51R_mejLVTRnwi0SONNxk,16240
|
|
24
|
+
tinybird/tb/modules/common.py,sha256=f5yKjb1dgUzR-SrC4UHnpHXYIu6INcTHwNO96jW8A9w,73201
|
|
25
|
+
tinybird/tb/modules/config.py,sha256=mxUMLWnELkxgpWJAbAS6o0PXsL2fpG0ogdaZgr3xjvU,11038
|
|
26
|
+
tinybird/tb/modules/copy.py,sha256=MAVqKip8_QhOYq99U_XuqSO6hCLJEh5sFtbhcXtI3SI,5802
|
|
27
27
|
tinybird/tb/modules/create.py,sha256=I01JDENOyGKK0Umd2_1Om_nFGP8Uk9vxaOw7PygK02o,12302
|
|
28
|
-
tinybird/tb/modules/datasource.py,sha256=
|
|
29
|
-
tinybird/tb/modules/deployment.py,sha256=
|
|
30
|
-
tinybird/tb/modules/endpoint.py,sha256=
|
|
28
|
+
tinybird/tb/modules/datasource.py,sha256=dNCK9iCR2xPLfwqqwg2ixyE6NuoVEiJU2mBZBmOYrVY,16906
|
|
29
|
+
tinybird/tb/modules/deployment.py,sha256=LDFhUaC6QO7KVxtkaXOccRsksntrxladgGe-jMXlGFg,17407
|
|
30
|
+
tinybird/tb/modules/endpoint.py,sha256=EhVoGAXsFz-83Fiwj1gI-I73iRRvL49d0W81un7hvPE,12080
|
|
31
31
|
tinybird/tb/modules/exceptions.py,sha256=4A2sSjCEqKUMqpP3WI00zouCWW4uLaghXXLZBSw04mY,3363
|
|
32
|
-
tinybird/tb/modules/feedback_manager.py,sha256=
|
|
33
|
-
tinybird/tb/modules/fmt.py,sha256=
|
|
32
|
+
tinybird/tb/modules/feedback_manager.py,sha256=Lv5MBGWNbIFTIy4nNSi0c61bmiQRv5J0nHRiiXUqDuc,68478
|
|
33
|
+
tinybird/tb/modules/fmt.py,sha256=qpf9APqKTKL2uphNgdbj4OMVyLkAxZn6dn4eHF99L5g,3553
|
|
34
34
|
tinybird/tb/modules/job.py,sha256=956Pj8BEEsiD2GZsV9RKKVM3I_CveOLgS82lykO5ukk,2963
|
|
35
35
|
tinybird/tb/modules/llm.py,sha256=AC0VSphTOM2t-v1_3NLvNN_FIbgMo4dTyMqIv5nniPo,835
|
|
36
36
|
tinybird/tb/modules/llm_utils.py,sha256=nS9r4FAElJw8yXtmdYrx-rtI2zXR8qXfi1QqUDCfxvg,3469
|
|
37
|
-
tinybird/tb/modules/local.py,sha256=
|
|
38
|
-
tinybird/tb/modules/local_common.py,sha256=
|
|
37
|
+
tinybird/tb/modules/local.py,sha256=9SZa-59eYROuwFNjJAHD0dRKLK5Reozh9WdchvfSsmQ,5680
|
|
38
|
+
tinybird/tb/modules/local_common.py,sha256=K_RcB9Gay9xLZE_ZZmQs-fuoYHQ0hnZ4E5yTFMpbFoo,2910
|
|
39
39
|
tinybird/tb/modules/login.py,sha256=EGxwVRmMX1Y7ZeCRyA8fqaCWpYYk7NvnZ3x_1g0NlYA,6063
|
|
40
|
-
tinybird/tb/modules/materialization.py,sha256=
|
|
41
|
-
tinybird/tb/modules/mock.py,sha256=
|
|
42
|
-
tinybird/tb/modules/pipe.py,sha256=
|
|
40
|
+
tinybird/tb/modules/materialization.py,sha256=r8Q9HXcYEmfrEzP4WpiasCKDJdSkTPaAKJtZMoJKhi8,5749
|
|
41
|
+
tinybird/tb/modules/mock.py,sha256=ASJTm4p11vkevGXCvU0h57aw7Inx03CTdxd5o2uzd8k,3870
|
|
42
|
+
tinybird/tb/modules/pipe.py,sha256=gcLz0qHgwKDLsWFY3yFLO9a0ETAV1dFbI8YeLHi9460,2429
|
|
43
|
+
tinybird/tb/modules/playground.py,sha256=PKQKROen8sV7MaeMlxi2lIqZ8B14Sz2LgqjVMbBh0pk,4801
|
|
43
44
|
tinybird/tb/modules/project.py,sha256=QdOG65Hcc6r_eQ938CfZeIXyp38RkiTYNPupgqy2gP0,2948
|
|
44
45
|
tinybird/tb/modules/regions.py,sha256=QjsL5H6Kg-qr0aYVLrvb1STeJ5Sx_sjvbOYO0LrEGMk,166
|
|
45
|
-
tinybird/tb/modules/shell.py,sha256=
|
|
46
|
+
tinybird/tb/modules/shell.py,sha256=bhp5pOw4T_4p4XamWEoGJYRgrr5MREzZN8E36aYrb5U,13924
|
|
46
47
|
tinybird/tb/modules/table.py,sha256=4XrtjM-N0zfNtxVkbvLDQQazno1EPXnxTyo7llivfXk,11035
|
|
47
48
|
tinybird/tb/modules/tag.py,sha256=anPmMUBc-TbFovlpFi8GPkKA18y7Y0GczMsMms5TZsU,3502
|
|
48
49
|
tinybird/tb/modules/telemetry.py,sha256=Hh2Io8ZPROSunbOLuMvuIFU4TqwWPmQTqal4WS09K1A,10449
|
|
49
50
|
tinybird/tb/modules/test.py,sha256=4nB11SXv3HC33HaORdg1Erf5GXV2Teyedgs9u750qfM,11543
|
|
50
|
-
tinybird/tb/modules/token.py,sha256=
|
|
51
|
-
tinybird/tb/modules/watch.py,sha256=
|
|
52
|
-
tinybird/tb/modules/workspace.py,sha256=
|
|
51
|
+
tinybird/tb/modules/token.py,sha256=OhqLFpCHVfYeBCxJ0n7n2qoho9E9eGcUfHgL7R1MUVQ,13485
|
|
52
|
+
tinybird/tb/modules/watch.py,sha256=ldB29Y_v1amXcYWhh2hhhV3tekPioXQ7noEhPZhW2Ok,8670
|
|
53
|
+
tinybird/tb/modules/workspace.py,sha256=fF0W6M5_qdwbbXpCUfuhon7CR0NhOOA0XOv5jD2l1JI,6401
|
|
53
54
|
tinybird/tb/modules/workspace_members.py,sha256=Vb5XEaKmkfONyfg2MS5EcpwolMvv7GLwFS5m2EuobT8,8726
|
|
54
55
|
tinybird/tb/modules/datafile/build.py,sha256=seGFSvmgyRrAM1-icsKBkuog3WccfGUYFTPT-xoA5W8,50940
|
|
55
56
|
tinybird/tb/modules/datafile/build_common.py,sha256=rT7VJ5mnQ68R_8US91DAtkusfvjWuG_NObOzNgtN_ko,4562
|
|
56
57
|
tinybird/tb/modules/datafile/build_datasource.py,sha256=VjxaKKLZhPYt3XHOyMmfoqEAWAPI5D78T-8FOaN77MY,17355
|
|
57
|
-
tinybird/tb/modules/datafile/build_pipe.py,sha256
|
|
58
|
+
tinybird/tb/modules/datafile/build_pipe.py,sha256=-22_657DiZodJ7shSu7KvwE_m6bHesNITdUgCMniuBI,22202
|
|
58
59
|
tinybird/tb/modules/datafile/common.py,sha256=1sKkQnyKOaFARQzORncpL6cRVpV9GDOD4oC36daX-Hk,79343
|
|
59
60
|
tinybird/tb/modules/datafile/diff.py,sha256=-0J7PsBO64T7LOZSkZ4ZFHHCPvT7cKItnJkbz2PkndU,6754
|
|
60
61
|
tinybird/tb/modules/datafile/exceptions.py,sha256=8rw2umdZjtby85QbuRKFO5ETz_eRHwUY5l7eHsy1wnI,556
|
|
@@ -65,6 +66,7 @@ tinybird/tb/modules/datafile/format_pipe.py,sha256=58iSTrJ5lg-IsbpX8TQumQTuZ6UIo
|
|
|
65
66
|
tinybird/tb/modules/datafile/parse_datasource.py,sha256=kk35PzesoJOd0LKjYp4kOyCwq4Qo4TiZnoI9qcXjB4k,1519
|
|
66
67
|
tinybird/tb/modules/datafile/parse_pipe.py,sha256=snoy8Ac_Sat7LIXLAKzxjJSl2-TKg9FaZTooxrx6muE,3420
|
|
67
68
|
tinybird/tb/modules/datafile/pipe_checker.py,sha256=LnDLGIHLJ3N7qHb2ptEbPr8CoczNfGwpjOY8EMdxfHQ,24649
|
|
69
|
+
tinybird/tb/modules/datafile/playground.py,sha256=0qNdVEiNRBvJVKL4url_tp6hVtAAdTxYqpLvyJs-loY,56382
|
|
68
70
|
tinybird/tb/modules/datafile/pull.py,sha256=vcjMUbjnZ9XQMGmL33J3ElpbXBTat8Yzp-haeDggZd4,5967
|
|
69
71
|
tinybird/tb/modules/tinyunit/tinyunit.py,sha256=GlDgEXc6TDO3ODxgfATAL2fvbKy-b_CzqoeDrApRm0g,11715
|
|
70
72
|
tinybird/tb/modules/tinyunit/tinyunit_lib.py,sha256=hGh1ZaXC1af7rKnX7222urkj0QJMhMWclqMy59dOqwE,1922
|
|
@@ -74,8 +76,8 @@ tinybird/tb_cli_modules/config.py,sha256=6u6B5QCdiQLbJkCkwtnKGs9H3nP-KXXhC75mF7B
|
|
|
74
76
|
tinybird/tb_cli_modules/exceptions.py,sha256=pmucP4kTF4irIt7dXiG-FcnI-o3mvDusPmch1L8RCWk,3367
|
|
75
77
|
tinybird/tb_cli_modules/regions.py,sha256=QjsL5H6Kg-qr0aYVLrvb1STeJ5Sx_sjvbOYO0LrEGMk,166
|
|
76
78
|
tinybird/tb_cli_modules/telemetry.py,sha256=Hh2Io8ZPROSunbOLuMvuIFU4TqwWPmQTqal4WS09K1A,10449
|
|
77
|
-
tinybird-0.0.1.
|
|
78
|
-
tinybird-0.0.1.
|
|
79
|
-
tinybird-0.0.1.
|
|
80
|
-
tinybird-0.0.1.
|
|
81
|
-
tinybird-0.0.1.
|
|
79
|
+
tinybird-0.0.1.dev73.dist-info/METADATA,sha256=I6j5vki_YHU9gwqs-jelfWHO3t1CcN4h9xF3REtlqH8,2585
|
|
80
|
+
tinybird-0.0.1.dev73.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
|
81
|
+
tinybird-0.0.1.dev73.dist-info/entry_points.txt,sha256=LwdHU6TfKx4Qs7BqqtaczEZbImgU7Abe9Lp920zb_fo,43
|
|
82
|
+
tinybird-0.0.1.dev73.dist-info/top_level.txt,sha256=VqqqEmkAy7UNaD8-V51FCoMMWXjLUlR0IstvK7tJYVY,54
|
|
83
|
+
tinybird-0.0.1.dev73.dist-info/RECORD,,
|