tinybird 0.0.1.dev72__py3-none-any.whl → 0.0.1.dev74__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 +3 -1
- tinybird/prompts.py +54 -6
- tinybird/tb/__cli__.py +2 -2
- tinybird/tb/cli.py +1 -0
- tinybird/tb/modules/build.py +15 -2
- tinybird/tb/modules/cli.py +6 -6
- tinybird/tb/modules/common.py +1 -2
- tinybird/tb/modules/config.py +1 -1
- tinybird/tb/modules/copy.py +1 -1
- tinybird/tb/modules/datafile/build_pipe.py +35 -186
- tinybird/tb/modules/datafile/common.py +17 -0
- tinybird/tb/modules/datafile/playground.py +1400 -0
- tinybird/tb/modules/datasource.py +1 -1
- tinybird/tb/modules/deployment.py +24 -41
- tinybird/tb/modules/endpoint.py +1 -1
- tinybird/tb/modules/fmt.py +2 -2
- tinybird/tb/modules/local.py +2 -2
- 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/project.py +5 -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.dev72.dist-info → tinybird-0.0.1.dev74.dist-info}/METADATA +1 -1
- {tinybird-0.0.1.dev72.dist-info → tinybird-0.0.1.dev74.dist-info}/RECORD +31 -29
- {tinybird-0.0.1.dev72.dist-info → tinybird-0.0.1.dev74.dist-info}/WHEEL +0 -0
- {tinybird-0.0.1.dev72.dist-info → tinybird-0.0.1.dev74.dist-info}/entry_points.txt +0 -0
- {tinybird-0.0.1.dev72.dist-info → tinybird-0.0.1.dev74.dist-info}/top_level.txt +0 -0
|
@@ -339,36 +339,34 @@ def create_deployment(
|
|
|
339
339
|
result = r.json()
|
|
340
340
|
logging.debug(json.dumps(result, indent=2))
|
|
341
341
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
342
|
+
print_changes(result, project)
|
|
343
|
+
|
|
344
|
+
feedback = result.get("deployment", {}).get("feedback", [])
|
|
345
|
+
for f in feedback:
|
|
346
|
+
click.echo(FeedbackManager.warning(message=f"△ {f.get('level')}: {f.get('resource')}: {f.get('message')}"))
|
|
347
|
+
|
|
348
|
+
deploy_errors = result.get("deployment", {}).get("errors")
|
|
349
|
+
for deploy_error in deploy_errors:
|
|
350
|
+
if deploy_error.get("filename", None):
|
|
346
351
|
click.echo(
|
|
347
|
-
FeedbackManager.
|
|
352
|
+
FeedbackManager.error(message=f"{deploy_error.get('filename')}\n\n{deploy_error.get('error')}")
|
|
348
353
|
)
|
|
354
|
+
else:
|
|
355
|
+
click.echo(FeedbackManager.error(message=f"{deploy_error.get('error')}"))
|
|
349
356
|
|
|
350
|
-
|
|
357
|
+
status = result.get("result")
|
|
358
|
+
if check:
|
|
351
359
|
if status == "success":
|
|
352
360
|
click.echo(FeedbackManager.success(message="\n✓ Deployment is valid"))
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
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')}"))
|
|
365
|
-
return
|
|
366
|
-
|
|
367
|
-
deploy_result = result.get("result")
|
|
368
|
-
if deploy_result == "success":
|
|
369
|
-
print_changes(result, project)
|
|
361
|
+
sys.exit(0)
|
|
362
|
+
|
|
363
|
+
click.echo(FeedbackManager.error(message="\n✗ Deployment is not valid"))
|
|
364
|
+
sys.exit(1)
|
|
365
|
+
|
|
366
|
+
status = result.get("result")
|
|
367
|
+
if status == "success":
|
|
370
368
|
deployment = result.get("deployment", {})
|
|
371
|
-
#
|
|
369
|
+
# TODO: This is a hack to show the url in the case of region is public. The URL should be returned by the API
|
|
372
370
|
if client.host == "https://api.europe-west2.gcp.tinybird.co":
|
|
373
371
|
click.echo(
|
|
374
372
|
FeedbackManager.gray(message="Deployment URL: ")
|
|
@@ -381,25 +379,10 @@ def create_deployment(
|
|
|
381
379
|
click.echo(FeedbackManager.info(message="\n✓ Deployment submitted successfully"))
|
|
382
380
|
else:
|
|
383
381
|
click.echo(FeedbackManager.success(message="\n✓ Deployment submitted successfully"))
|
|
384
|
-
|
|
385
|
-
feedback = deployment.get("feedback", [])
|
|
386
|
-
for f in feedback:
|
|
387
|
-
click.echo(
|
|
388
|
-
FeedbackManager.warning(message=f"△ {f.get('level')}: {f.get('resource')}: {f.get('message')}")
|
|
389
|
-
)
|
|
390
|
-
|
|
391
|
-
elif deploy_result == "failed":
|
|
382
|
+
elif status == "failed":
|
|
392
383
|
click.echo(FeedbackManager.error(message="Deployment failed"))
|
|
393
|
-
deploy_errors = result.get("errors")
|
|
394
|
-
for deploy_error in deploy_errors:
|
|
395
|
-
if deploy_error.get("filename", None):
|
|
396
|
-
click.echo(
|
|
397
|
-
FeedbackManager.error(message=f"{deploy_error.get('filename')}\n\n{deploy_error.get('error')}")
|
|
398
|
-
)
|
|
399
|
-
else:
|
|
400
|
-
click.echo(FeedbackManager.error(message=f"{deploy_error.get('error')}"))
|
|
401
384
|
else:
|
|
402
|
-
click.echo(FeedbackManager.error(message=f"Unknown build result {
|
|
385
|
+
click.echo(FeedbackManager.error(message=f"Unknown build result {status}"))
|
|
403
386
|
except Exception as e:
|
|
404
387
|
click.echo(FeedbackManager.error_exception(error=e))
|
|
405
388
|
finally:
|
tinybird/tb/modules/endpoint.py
CHANGED
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()
|
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/project.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import glob
|
|
2
2
|
import os
|
|
3
|
+
import re
|
|
3
4
|
from pathlib import Path
|
|
4
5
|
from typing import Dict, List, Optional
|
|
5
6
|
|
|
@@ -78,3 +79,7 @@ class Project:
|
|
|
78
79
|
if datafile := self.get_datafile(filename):
|
|
79
80
|
datafiles[filename] = datafile
|
|
80
81
|
return datafiles
|
|
82
|
+
|
|
83
|
+
@staticmethod
|
|
84
|
+
def is_endpoint(content: str) -> bool:
|
|
85
|
+
return re.search(r"TYPE endpoint", content, re.IGNORECASE) is not None
|
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
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,47 +15,48 @@ 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=sa2SLi9xyvB-stMjO_bz2sJude-pMt2cBgZYff5bXh8,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=
|
|
21
|
+
tinybird/tb/modules/build.py,sha256=LBLyO5bVzqOegop459-V2QM1s48yMJu2GBSWBSio_u0,10323
|
|
22
22
|
tinybird/tb/modules/cicd.py,sha256=Eyut5VsOtXq5hK9g_nGCJ2epfEMDDy2cX3kI0NPJZZA,5355
|
|
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=
|
|
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=umfWZA95WPRPUsJh8wSBj7Av_PevphWF6skJ1-lvnW8,16556
|
|
30
|
+
tinybird/tb/modules/endpoint.py,sha256=EhVoGAXsFz-83Fiwj1gI-I73iRRvL49d0W81un7hvPE,12080
|
|
31
31
|
tinybird/tb/modules/exceptions.py,sha256=4A2sSjCEqKUMqpP3WI00zouCWW4uLaghXXLZBSw04mY,3363
|
|
32
32
|
tinybird/tb/modules/feedback_manager.py,sha256=Lv5MBGWNbIFTIy4nNSi0c61bmiQRv5J0nHRiiXUqDuc,68478
|
|
33
|
-
tinybird/tb/modules/fmt.py,sha256=
|
|
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=
|
|
37
|
+
tinybird/tb/modules/local.py,sha256=9SZa-59eYROuwFNjJAHD0dRKLK5Reozh9WdchvfSsmQ,5680
|
|
38
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=
|
|
43
|
-
tinybird/tb/modules/
|
|
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
|
|
44
|
+
tinybird/tb/modules/project.py,sha256=YnrfDBenrzXCPO99qOtsTE21SP2vAb3FqBv6NV9Yw2g,3099
|
|
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/common.py,sha256=
|
|
58
|
+
tinybird/tb/modules/datafile/build_pipe.py,sha256=-22_657DiZodJ7shSu7KvwE_m6bHesNITdUgCMniuBI,22202
|
|
59
|
+
tinybird/tb/modules/datafile/common.py,sha256=dEGwdfJf90XQNh2FOLbAy-Y04X3RpfpafT8wzVU13aA,80088
|
|
59
60
|
tinybird/tb/modules/datafile/diff.py,sha256=-0J7PsBO64T7LOZSkZ4ZFHHCPvT7cKItnJkbz2PkndU,6754
|
|
60
61
|
tinybird/tb/modules/datafile/exceptions.py,sha256=8rw2umdZjtby85QbuRKFO5ETz_eRHwUY5l7eHsy1wnI,556
|
|
61
62
|
tinybird/tb/modules/datafile/fixture.py,sha256=si-9LB-LdKQSWDtVW82xDrHtFfko5bgBG1cvjqqrcPU,1064
|
|
@@ -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.dev74.dist-info/METADATA,sha256=1BtfBdQkgTyFzBZ6W4eL38o3T_02rjevCVjj7I6CCVE,2585
|
|
80
|
+
tinybird-0.0.1.dev74.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
|
81
|
+
tinybird-0.0.1.dev74.dist-info/entry_points.txt,sha256=LwdHU6TfKx4Qs7BqqtaczEZbImgU7Abe9Lp920zb_fo,43
|
|
82
|
+
tinybird-0.0.1.dev74.dist-info/top_level.txt,sha256=VqqqEmkAy7UNaD8-V51FCoMMWXjLUlR0IstvK7tJYVY,54
|
|
83
|
+
tinybird-0.0.1.dev74.dist-info/RECORD,,
|
|
File without changes
|