tinybird 0.0.1.dev41__py3-none-any.whl → 0.0.1.dev43__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 +1 -1
- tinybird/connectors.py +3 -3
- tinybird/feedback_manager.py +1 -1
- tinybird/prompts.py +11 -1
- tinybird/sql.py +1 -1
- tinybird/sql_template_fmt.py +1 -1
- tinybird/tb/__cli__.py +2 -2
- tinybird/tb/modules/build.py +34 -24
- tinybird/tb/modules/build_client.py +1 -1
- tinybird/tb/modules/cicd.py +2 -2
- tinybird/tb/modules/cli.py +6 -4
- tinybird/tb/modules/copy.py +95 -4
- tinybird/tb/modules/datafile/build.py +52 -26
- tinybird/tb/modules/deployment.py +86 -61
- tinybird/tb/modules/endpoint.py +1 -1
- tinybird/tb/modules/llm_utils.py +2 -2
- tinybird/tb/modules/pipe.py +0 -90
- tinybird/tb/modules/project.py +25 -1
- tinybird/tb/modules/shell.py +4 -4
- tinybird/tb/modules/watch.py +54 -5
- tinybird/tb_cli_modules/common.py +1 -1
- tinybird/tornado_template.py +2 -2
- {tinybird-0.0.1.dev41.dist-info → tinybird-0.0.1.dev43.dist-info}/METADATA +1 -1
- {tinybird-0.0.1.dev41.dist-info → tinybird-0.0.1.dev43.dist-info}/RECORD +27 -27
- {tinybird-0.0.1.dev41.dist-info → tinybird-0.0.1.dev43.dist-info}/WHEEL +0 -0
- {tinybird-0.0.1.dev41.dist-info → tinybird-0.0.1.dev43.dist-info}/entry_points.txt +0 -0
- {tinybird-0.0.1.dev41.dist-info → tinybird-0.0.1.dev43.dist-info}/top_level.txt +0 -0
tinybird/client.py
CHANGED
|
@@ -72,7 +72,7 @@ def parse_error_response(response: Response) -> str:
|
|
|
72
72
|
if content.get("error", None):
|
|
73
73
|
error = content["error"]
|
|
74
74
|
if content.get("errors", None):
|
|
75
|
-
error += f
|
|
75
|
+
error += f" -> errors: {content.get('errors')}"
|
|
76
76
|
else:
|
|
77
77
|
error = json.dumps(response, indent=4)
|
|
78
78
|
return error
|
tinybird/connectors.py
CHANGED
|
@@ -246,7 +246,7 @@ class BigQuery(Connector):
|
|
|
246
246
|
uri='{self.gcs.gs_url()}{destination}*.csv',
|
|
247
247
|
format='CSV',
|
|
248
248
|
overwrite=true,
|
|
249
|
-
header={
|
|
249
|
+
header={"true" if with_headers else "false"},
|
|
250
250
|
field_delimiter=',') AS
|
|
251
251
|
{sql}
|
|
252
252
|
"""
|
|
@@ -319,7 +319,7 @@ class Snowflake(Connector):
|
|
|
319
319
|
|
|
320
320
|
def create_stage(self):
|
|
321
321
|
sql = f"""
|
|
322
|
-
create stage "{self.options[
|
|
322
|
+
create stage "{self.options["schema"]}".{self.stage()}
|
|
323
323
|
url='{self.gcs.gcs_url()}'
|
|
324
324
|
storage_integration = {self.storage_integration()};
|
|
325
325
|
"""
|
|
@@ -337,7 +337,7 @@ class Snowflake(Connector):
|
|
|
337
337
|
from ({sql})
|
|
338
338
|
overwrite = true
|
|
339
339
|
file_format = (TYPE=CSV COMPRESSION=NONE ESCAPE_UNENCLOSED_FIELD=NONE FIELD_DELIMITER='|' FIELD_OPTIONALLY_ENCLOSED_BY='"' null_if=())
|
|
340
|
-
header = {"true" if with_headers else "false"
|
|
340
|
+
header = {"true" if with_headers else "false"}
|
|
341
341
|
max_file_size = 2500000000;
|
|
342
342
|
"""
|
|
343
343
|
self.execute(sql)
|
tinybird/feedback_manager.py
CHANGED
|
@@ -951,7 +951,7 @@ Ready? """
|
|
|
951
951
|
)
|
|
952
952
|
success_datasource_alter = success_message("** The Data Source has been correctly updated.")
|
|
953
953
|
success_datasource_kafka_connected = success_message(
|
|
954
|
-
"** Data Source '{id}' created\n
|
|
954
|
+
"** Data Source '{id}' created\n** Kafka streaming connection configured successfully!"
|
|
955
955
|
)
|
|
956
956
|
success_datasource_shared = success_message(
|
|
957
957
|
"** The Data Source {datasource} has been correctly shared with {workspace}"
|
tinybird/prompts.py
CHANGED
|
@@ -371,10 +371,20 @@ You are a Tinybird expert. You will be given a pipe containing different nodes w
|
|
|
371
371
|
- The test command must be a valid Tinybird command that can be run in the terminal.
|
|
372
372
|
- The test command can have as many parameters as are needed to test the pipe.
|
|
373
373
|
- The parameter within Tinybird templating syntax looks like this one {{String(my_param_name, default_value)}}.
|
|
374
|
-
- If there are no parameters, you can omit parameters and generate a single test
|
|
374
|
+
- If there are no parameters, you can omit parameters and generate a single test.
|
|
375
375
|
- The format of the parameters is the following: ?param1=value1¶m2=value2¶m3=value3
|
|
376
|
+
- If some parameters are provided by the user and you need to use them, preserve in the same format as they were provided, like case sensitive.
|
|
376
377
|
</instructions>
|
|
377
378
|
|
|
379
|
+
This is an example of a test with parameters:
|
|
380
|
+
<example>
|
|
381
|
+
<test>
|
|
382
|
+
<name>kpis_date_range</name>
|
|
383
|
+
<description>Test specific date range with daily granularity</description>
|
|
384
|
+
<parameters>?date_from=2024-01-01&date_to=2024-01-10</parameters>
|
|
385
|
+
</test>
|
|
386
|
+
</example>
|
|
387
|
+
|
|
378
388
|
Follow the instructions and generate the following response with no additional text:
|
|
379
389
|
|
|
380
390
|
<response>
|
tinybird/sql.py
CHANGED
|
@@ -241,7 +241,7 @@ def format_parse_error(
|
|
|
241
241
|
message += f" found at position {adjusted_position - len(keyword)}"
|
|
242
242
|
else:
|
|
243
243
|
message += (
|
|
244
|
-
f" found {repr(table_structure[i]) if len(table_structure)>i else 'EOF'} at position {adjusted_position}"
|
|
244
|
+
f" found {repr(table_structure[i]) if len(table_structure) > i else 'EOF'} at position {adjusted_position}"
|
|
245
245
|
)
|
|
246
246
|
return message
|
|
247
247
|
|
tinybird/sql_template_fmt.py
CHANGED
|
@@ -100,7 +100,7 @@ def _format_jinja_node(self, node: Node, max_length: int) -> bool:
|
|
|
100
100
|
parts = tag.code.split("\n")
|
|
101
101
|
prefix = INDENT * (node.depth[0] + node.depth[1])
|
|
102
102
|
if len(parts) > 1:
|
|
103
|
-
tag.code = "\n".join([f
|
|
103
|
+
tag.code = "\n".join([f"{prefix if i != 0 else ''}{part}" for i, part in enumerate(parts)])
|
|
104
104
|
|
|
105
105
|
node.value = str(tag)
|
|
106
106
|
|
tinybird/tb/__cli__.py
CHANGED
|
@@ -4,5 +4,5 @@ __description__ = 'Tinybird Command Line Tool'
|
|
|
4
4
|
__url__ = 'https://www.tinybird.co/docs/cli/introduction.html'
|
|
5
5
|
__author__ = 'Tinybird'
|
|
6
6
|
__author_email__ = 'support@tinybird.co'
|
|
7
|
-
__version__ = '0.0.1.
|
|
8
|
-
__revision__ = '
|
|
7
|
+
__version__ = '0.0.1.dev43'
|
|
8
|
+
__revision__ = 'ecb1311'
|
tinybird/tb/modules/build.py
CHANGED
|
@@ -31,18 +31,16 @@ def build(ctx: click.Context, watch: bool) -> None:
|
|
|
31
31
|
project: Project = ctx.ensure_object(dict)["project"]
|
|
32
32
|
tb_client = asyncio.run(get_tinybird_local_client(str(project.path)))
|
|
33
33
|
click.echo(FeedbackManager.highlight(message="\n» Building project..."))
|
|
34
|
-
|
|
35
34
|
time_start = time.time()
|
|
36
35
|
|
|
37
|
-
def process(file_changed: Optional[str] = None) -> None:
|
|
36
|
+
def process(file_changed: Optional[str] = None, diff: Optional[str] = None) -> None:
|
|
38
37
|
if file_changed and file_changed.endswith(".ndjson"):
|
|
39
|
-
rebuild_fixture(project, file_changed)
|
|
38
|
+
rebuild_fixture(project, tb_client, file_changed)
|
|
40
39
|
else:
|
|
41
40
|
build_project(project, tb_client)
|
|
42
|
-
new_tb_client = asyncio.run(get_tinybird_local_client(str(project.path)))
|
|
43
41
|
try:
|
|
44
42
|
if file_changed:
|
|
45
|
-
build_and_print_resource(
|
|
43
|
+
build_and_print_resource(tb_client, file_changed, diff)
|
|
46
44
|
except Exception:
|
|
47
45
|
pass
|
|
48
46
|
|
|
@@ -52,7 +50,7 @@ def build(ctx: click.Context, watch: bool) -> None:
|
|
|
52
50
|
click.echo(FeedbackManager.success(message=f"\n✓ Build completed in {elapsed_time:.1f}s"))
|
|
53
51
|
|
|
54
52
|
if watch:
|
|
55
|
-
shell = Shell(project=project)
|
|
53
|
+
shell = Shell(project=project, tb_client=tb_client)
|
|
56
54
|
click.echo(FeedbackManager.gray(message="\nWatching for changes..."))
|
|
57
55
|
watcher_thread = threading.Thread(
|
|
58
56
|
target=watch_project,
|
|
@@ -80,6 +78,9 @@ def build_project(project: Project, tb_client: TinyB) -> None:
|
|
|
80
78
|
project_path = project.path
|
|
81
79
|
project_files = project.get_project_files()
|
|
82
80
|
|
|
81
|
+
if not project_files:
|
|
82
|
+
return
|
|
83
|
+
|
|
83
84
|
for file_path in project_files:
|
|
84
85
|
relative_path = str(Path(file_path).relative_to(project_path))
|
|
85
86
|
fd = open(file_path, "rb")
|
|
@@ -88,7 +89,7 @@ def build_project(project: Project, tb_client: TinyB) -> None:
|
|
|
88
89
|
files.append((MULTIPART_BOUNDARY_DATA_PROJECT, (relative_path, fd.read().decode("utf-8"), content_type)))
|
|
89
90
|
HEADERS = {"Authorization": f"Bearer {TINYBIRD_API_KEY}"}
|
|
90
91
|
|
|
91
|
-
r = requests.post(TINYBIRD_API_URL, files=files, headers=HEADERS
|
|
92
|
+
r = requests.post(TINYBIRD_API_URL, files=files, headers=HEADERS)
|
|
92
93
|
try:
|
|
93
94
|
result = r.json()
|
|
94
95
|
except Exception as e:
|
|
@@ -103,19 +104,21 @@ def build_project(project: Project, tb_client: TinyB) -> None:
|
|
|
103
104
|
datasources = result.get("datasources", [])
|
|
104
105
|
pipes = result.get("pipes", [])
|
|
105
106
|
for ds in datasources:
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
107
|
+
ds_path_str: Optional[str] = next(
|
|
108
|
+
(p for p in project_files if p.endswith(ds.get("name") + ".datasource")), None
|
|
109
|
+
)
|
|
110
|
+
if ds_path_str:
|
|
111
|
+
ds_path = Path(ds_path_str)
|
|
112
|
+
ds_path_str = ds_path_str.replace(f"{project.folder}/", "")
|
|
113
|
+
click.echo(FeedbackManager.info(message=f"✓ {ds_path_str} created"))
|
|
110
114
|
for pipe in pipes:
|
|
111
115
|
pipe_name = pipe.get("name")
|
|
112
|
-
|
|
113
|
-
if
|
|
114
|
-
|
|
115
|
-
click.echo(FeedbackManager.info(message=f"✓ {
|
|
116
|
+
pipe_path_str: Optional[str] = next((p for p in project_files if p.endswith(pipe_name + ".pipe")), None)
|
|
117
|
+
if pipe_path_str:
|
|
118
|
+
pipe_path_str = pipe_path_str.replace(f"{project.folder}/", "")
|
|
119
|
+
click.echo(FeedbackManager.info(message=f"✓ {pipe_path_str} created"))
|
|
116
120
|
|
|
117
121
|
try:
|
|
118
|
-
new_tb_client = asyncio.run(get_tinybird_local_client(str(project.path)))
|
|
119
122
|
for filename in project_files:
|
|
120
123
|
if filename.endswith(".datasource"):
|
|
121
124
|
ds_path = Path(filename)
|
|
@@ -128,7 +131,7 @@ def build_project(project: Project, tb_client: TinyB) -> None:
|
|
|
128
131
|
fixture_path = fixture_folder / f"{ds_name}.ndjson"
|
|
129
132
|
|
|
130
133
|
if fixture_path.exists():
|
|
131
|
-
append_fixture(
|
|
134
|
+
append_fixture(tb_client, ds_name, str(fixture_path))
|
|
132
135
|
except Exception:
|
|
133
136
|
pass
|
|
134
137
|
|
|
@@ -166,9 +169,8 @@ def append_fixture(
|
|
|
166
169
|
)
|
|
167
170
|
|
|
168
171
|
|
|
169
|
-
def rebuild_fixture(project: Project, fixture: str) -> None:
|
|
172
|
+
def rebuild_fixture(project: Project, tb_client: TinyB, fixture: str) -> None:
|
|
170
173
|
try:
|
|
171
|
-
tb_client = asyncio.run(get_tinybird_local_client(str(project.path)))
|
|
172
174
|
fixture_path = Path(fixture)
|
|
173
175
|
datasources_path = Path(project.folder) / "datasources"
|
|
174
176
|
ds_name = fixture_path.stem
|
|
@@ -188,9 +190,17 @@ def rebuild_fixture(project: Project, fixture: str) -> None:
|
|
|
188
190
|
click.echo(FeedbackManager.error_exception(error=e))
|
|
189
191
|
|
|
190
192
|
|
|
191
|
-
def build_and_print_resource(tb_client: TinyB, filename: str):
|
|
193
|
+
def build_and_print_resource(tb_client: TinyB, filename: str, diff: Optional[str] = None):
|
|
194
|
+
table_name = diff
|
|
192
195
|
resource_path = Path(filename)
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
196
|
+
resource_name = resource_path.stem
|
|
197
|
+
|
|
198
|
+
pipeline = resource_name if filename.endswith(".pipe") else None
|
|
199
|
+
|
|
200
|
+
if not table_name:
|
|
201
|
+
table_name = resource_name
|
|
202
|
+
|
|
203
|
+
sql = f"SELECT * FROM {table_name} FORMAT JSON"
|
|
204
|
+
|
|
205
|
+
res = asyncio.run(tb_client.query(sql, pipeline=pipeline))
|
|
206
|
+
print_table_formatted(res, table_name)
|
|
@@ -166,7 +166,7 @@ def build_client(
|
|
|
166
166
|
build_ok = asyncio.run(build_once(filenames))
|
|
167
167
|
|
|
168
168
|
if watch:
|
|
169
|
-
shell = Shell(project=project,
|
|
169
|
+
shell = Shell(project=project, tb_client=tb_client)
|
|
170
170
|
click.echo(FeedbackManager.gray(message="\nWatching for changes..."))
|
|
171
171
|
watcher_thread = threading.Thread(
|
|
172
172
|
target=watch_files, args=(filenames, process, shell, project, build_ok), daemon=True
|
tinybird/tb/modules/cicd.py
CHANGED
|
@@ -39,7 +39,7 @@ jobs:
|
|
|
39
39
|
working-directory: '{{ data_project_dir }}'
|
|
40
40
|
services:
|
|
41
41
|
tinybird:
|
|
42
|
-
image: tinybirdco/tinybird-local:
|
|
42
|
+
image: tinybirdco/tinybird-local:beta
|
|
43
43
|
ports:
|
|
44
44
|
- 80:80
|
|
45
45
|
steps:
|
|
@@ -83,7 +83,7 @@ tinybird_ci_workflow:
|
|
|
83
83
|
- tb build
|
|
84
84
|
- tb test run
|
|
85
85
|
services:
|
|
86
|
-
- name: tinybirdco/tinybird-local:
|
|
86
|
+
- name: tinybirdco/tinybird-local:beta
|
|
87
87
|
alias: tinybird-local
|
|
88
88
|
"""
|
|
89
89
|
|
tinybird/tb/modules/cli.py
CHANGED
|
@@ -66,15 +66,17 @@ VERSION = f"{__cli__.__version__} (rev {__cli__.__revision__})"
|
|
|
66
66
|
@click.option("--host", help="Use custom host, defaults to TB_HOST envvar, then to https://api.tinybird.co")
|
|
67
67
|
@click.option("--show-tokens", is_flag=True, default=False, help="Enable the output of tokens")
|
|
68
68
|
@click.option("--prod/--local", is_flag=True, default=False, help="Run against production or local")
|
|
69
|
-
@click.option("--folder", type=str,
|
|
69
|
+
@click.option("--folder", type=str, help="Folder where files will be placed")
|
|
70
70
|
@click.version_option(version=VERSION)
|
|
71
71
|
@click.pass_context
|
|
72
72
|
@coro
|
|
73
|
-
async def cli(
|
|
73
|
+
async def cli(
|
|
74
|
+
ctx: Context, debug: bool, token: str, host: str, show_tokens: bool, prod: bool, folder: Optional[str]
|
|
75
|
+
) -> None:
|
|
74
76
|
"""
|
|
75
77
|
Use `OBFUSCATE_REGEX_PATTERN` and `OBFUSCATE_PATTERN_SEPARATOR` environment variables to define a regex pattern and a separator (in case of a single string with multiple regex) to obfuscate secrets in the CLI output.
|
|
76
78
|
"""
|
|
77
|
-
project = Project(folder=folder)
|
|
79
|
+
project = Project(folder=folder or os.getcwd())
|
|
78
80
|
# We need to unpatch for our tests not to break
|
|
79
81
|
if show_tokens or not prod or ctx.invoked_subcommand == "build":
|
|
80
82
|
__unpatch_click_output()
|
|
@@ -87,7 +89,7 @@ async def cli(ctx: Context, debug: bool, token: str, host: str, show_tokens: boo
|
|
|
87
89
|
if debug:
|
|
88
90
|
logging.basicConfig(level=logging.DEBUG)
|
|
89
91
|
|
|
90
|
-
config_temp = CLIConfig.get_project_config(project.path)
|
|
92
|
+
config_temp = CLIConfig.get_project_config(str(project.path))
|
|
91
93
|
if token:
|
|
92
94
|
config_temp.set_token(token)
|
|
93
95
|
if host:
|
tinybird/tb/modules/copy.py
CHANGED
|
@@ -5,13 +5,14 @@
|
|
|
5
5
|
|
|
6
6
|
import json
|
|
7
7
|
import re
|
|
8
|
+
from typing import Optional, Tuple
|
|
8
9
|
|
|
9
10
|
import click
|
|
10
11
|
from click import Context
|
|
11
12
|
|
|
12
|
-
from tinybird.client import TinyB
|
|
13
|
+
from tinybird.client import AuthNoTokenException, TinyB
|
|
13
14
|
from tinybird.tb.modules.cli import cli
|
|
14
|
-
from tinybird.tb.modules.common import coro, echo_safe_humanfriendly_tables_format_smart_table
|
|
15
|
+
from tinybird.tb.modules.common import coro, echo_safe_humanfriendly_tables_format_smart_table, wait_job
|
|
15
16
|
from tinybird.tb.modules.datafile.common import get_name_version
|
|
16
17
|
from tinybird.tb.modules.exceptions import CLIPipeException
|
|
17
18
|
from tinybird.tb.modules.feedback_manager import FeedbackManager
|
|
@@ -38,10 +39,10 @@ async def copy_ls(ctx: Context, match: str, format_: str):
|
|
|
38
39
|
"""List copy pipes"""
|
|
39
40
|
|
|
40
41
|
client: TinyB = ctx.ensure_object(dict)["client"]
|
|
41
|
-
pipes = await client.pipes(dependencies=False, node_attrs="name", attrs="name,updated_at")
|
|
42
|
+
pipes = await client.pipes(dependencies=False, node_attrs="name", attrs="name,updated_at,type")
|
|
42
43
|
copies = [p for p in pipes if p.get("type") == "copy"]
|
|
43
44
|
copies = sorted(copies, key=lambda p: p["updated_at"])
|
|
44
|
-
columns = ["name", "updated at", "nodes"
|
|
45
|
+
columns = ["name", "updated at", "nodes"]
|
|
45
46
|
table_human_readable = []
|
|
46
47
|
table_machine_readable = []
|
|
47
48
|
pattern = re.compile(match) if match else None
|
|
@@ -66,3 +67,93 @@ async def copy_ls(ctx: Context, match: str, format_: str):
|
|
|
66
67
|
click.echo(json.dumps({"pipes": table_machine_readable}, indent=2))
|
|
67
68
|
else:
|
|
68
69
|
raise CLIPipeException(FeedbackManager.error_pipe_ls_type())
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@copy.command(name="run", short_help="Run an on-demand copy job")
|
|
73
|
+
@click.argument("pipe_name_or_id")
|
|
74
|
+
@click.option("--wait", is_flag=True, default=False, help="Wait for the copy job to finish")
|
|
75
|
+
@click.option(
|
|
76
|
+
"--mode", type=click.Choice(["append", "replace"], case_sensitive=True), default=None, help="Copy strategy"
|
|
77
|
+
)
|
|
78
|
+
@click.option("--yes", is_flag=True, default=False, help="Do not ask for confirmation")
|
|
79
|
+
@click.option(
|
|
80
|
+
"--param",
|
|
81
|
+
nargs=1,
|
|
82
|
+
type=str,
|
|
83
|
+
multiple=True,
|
|
84
|
+
default=None,
|
|
85
|
+
help="Key and value of the params you want the Copy pipe to be called with. For example: tb pipe copy run <my_copy_pipe> --param foo=bar",
|
|
86
|
+
)
|
|
87
|
+
@click.pass_context
|
|
88
|
+
@coro
|
|
89
|
+
async def copy_run(
|
|
90
|
+
ctx: click.Context, pipe_name_or_id: str, wait: bool, mode: str, yes: bool, param: Optional[Tuple[str]]
|
|
91
|
+
):
|
|
92
|
+
"""Run an on-demand copy job"""
|
|
93
|
+
|
|
94
|
+
params = dict(key_value.split("=") for key_value in param) if param else {}
|
|
95
|
+
|
|
96
|
+
if yes or click.confirm(FeedbackManager.warning_confirm_copy_pipe(pipe=pipe_name_or_id)):
|
|
97
|
+
click.echo(FeedbackManager.info_copy_job_running(pipe=pipe_name_or_id))
|
|
98
|
+
client: TinyB = ctx.ensure_object(dict)["client"]
|
|
99
|
+
|
|
100
|
+
try:
|
|
101
|
+
response = await client.pipe_run_copy(pipe_name_or_id, params, mode)
|
|
102
|
+
|
|
103
|
+
job_id = response["job"]["job_id"]
|
|
104
|
+
job_url = response["job"]["job_url"]
|
|
105
|
+
target_datasource_id = response["tags"]["copy_target_datasource"]
|
|
106
|
+
target_datasource = await client.get_datasource(target_datasource_id)
|
|
107
|
+
target_datasource_name = target_datasource["name"]
|
|
108
|
+
click.echo(
|
|
109
|
+
FeedbackManager.success_copy_job_created(target_datasource=target_datasource_name, job_url=job_url)
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
if wait:
|
|
113
|
+
await wait_job(client, job_id, job_url, "** Copying data")
|
|
114
|
+
click.echo(FeedbackManager.success_data_copied_to_ds(target_datasource=target_datasource_name))
|
|
115
|
+
|
|
116
|
+
except AuthNoTokenException:
|
|
117
|
+
raise
|
|
118
|
+
except Exception as e:
|
|
119
|
+
raise CLIPipeException(FeedbackManager.error_creating_copy_job(error=e))
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
@copy.command(name="resume", short_help="Resume a paused copy pipe")
|
|
123
|
+
@click.argument("pipe_name_or_id")
|
|
124
|
+
@click.pass_context
|
|
125
|
+
@coro
|
|
126
|
+
async def copy_resume(ctx: click.Context, pipe_name_or_id: str):
|
|
127
|
+
"""Resume a paused copy pipe"""
|
|
128
|
+
|
|
129
|
+
click.echo(FeedbackManager.info_copy_pipe_resuming(pipe=pipe_name_or_id))
|
|
130
|
+
client: TinyB = ctx.ensure_object(dict)["client"]
|
|
131
|
+
|
|
132
|
+
try:
|
|
133
|
+
await client.pipe_resume_copy(pipe_name_or_id)
|
|
134
|
+
click.echo(FeedbackManager.success_copy_pipe_resumed(pipe=pipe_name_or_id))
|
|
135
|
+
|
|
136
|
+
except AuthNoTokenException:
|
|
137
|
+
raise
|
|
138
|
+
except Exception as e:
|
|
139
|
+
raise CLIPipeException(FeedbackManager.error_resuming_copy_pipe(error=e))
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
@copy.command(name="pause", short_help="Pause a running copy pipe")
|
|
143
|
+
@click.argument("pipe_name_or_id")
|
|
144
|
+
@click.pass_context
|
|
145
|
+
@coro
|
|
146
|
+
async def copy_pause(ctx: click.Context, pipe_name_or_id: str):
|
|
147
|
+
"""Pause a running copy pipe"""
|
|
148
|
+
|
|
149
|
+
click.echo(FeedbackManager.info_copy_pipe_pausing(pipe=pipe_name_or_id))
|
|
150
|
+
client: TinyB = ctx.ensure_object(dict)["client"]
|
|
151
|
+
|
|
152
|
+
try:
|
|
153
|
+
await client.pipe_pause_copy(pipe_name_or_id)
|
|
154
|
+
click.echo(FeedbackManager.success_copy_pipe_paused(pipe=pipe_name_or_id))
|
|
155
|
+
|
|
156
|
+
except AuthNoTokenException:
|
|
157
|
+
raise
|
|
158
|
+
except Exception as e:
|
|
159
|
+
raise CLIPipeException(FeedbackManager.error_pausing_copy_pipe(error=e))
|
|
@@ -707,6 +707,7 @@ async def process(
|
|
|
707
707
|
):
|
|
708
708
|
name, kind = filename.rsplit(".", 1)
|
|
709
709
|
warnings = []
|
|
710
|
+
embedded_datasources = {} if embedded_datasources is None else embedded_datasources
|
|
710
711
|
|
|
711
712
|
try:
|
|
712
713
|
res = await process_file(
|
|
@@ -806,31 +807,45 @@ async def get_processed(
|
|
|
806
807
|
to_run: Optional[Dict[str, Any]] = None,
|
|
807
808
|
vendor_paths: Optional[List[Tuple[str, str]]] = None,
|
|
808
809
|
processed: Optional[Set[str]] = None,
|
|
809
|
-
tb_client: TinyB = None,
|
|
810
|
+
tb_client: Optional[TinyB] = None,
|
|
810
811
|
skip_connectors: bool = False,
|
|
811
812
|
current_ws: Optional[Dict[str, Any]] = None,
|
|
812
813
|
fork_downstream: Optional[bool] = False,
|
|
813
814
|
is_internal: Optional[bool] = False,
|
|
814
815
|
dir_path: Optional[str] = None,
|
|
815
|
-
embedded_datasources: Optional[Dict[str, Any]] = None,
|
|
816
|
+
embedded_datasources: Optional[Dict[str, Dict[str, Any]]] = None,
|
|
816
817
|
):
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
if dep_map is None
|
|
820
|
-
|
|
821
|
-
if
|
|
822
|
-
|
|
823
|
-
if processed is None:
|
|
824
|
-
processed = set()
|
|
818
|
+
# Initialize with proper type annotations
|
|
819
|
+
deps_list: List[str] = [] if deps is None else deps
|
|
820
|
+
dep_map_dict: Dict[str, Any] = {} if dep_map is None else dep_map
|
|
821
|
+
to_run_dict: Dict[str, Any] = {} if to_run is None else to_run
|
|
822
|
+
processed_set: Set[str] = set() if processed is None else processed
|
|
823
|
+
embedded_ds: Dict[str, Dict[str, Any]] = {} if embedded_datasources is None else embedded_datasources
|
|
825
824
|
|
|
826
825
|
for filename in filenames:
|
|
827
826
|
# just process changed filenames (tb deploy and --only-changes)
|
|
828
|
-
if changed:
|
|
827
|
+
if changed is not None:
|
|
829
828
|
resource = Path(filename).resolve().stem
|
|
830
829
|
if resource in changed and (not changed[resource] or changed[resource] in ["shared", "remote"]):
|
|
831
830
|
continue
|
|
832
831
|
if os.path.isdir(filename):
|
|
833
|
-
await get_processed(
|
|
832
|
+
await get_processed(
|
|
833
|
+
filenames=get_project_filenames(filename),
|
|
834
|
+
changed=changed,
|
|
835
|
+
verbose=verbose,
|
|
836
|
+
deps=deps_list,
|
|
837
|
+
dep_map=dep_map_dict,
|
|
838
|
+
to_run=to_run_dict,
|
|
839
|
+
vendor_paths=vendor_paths,
|
|
840
|
+
processed=processed_set,
|
|
841
|
+
tb_client=tb_client,
|
|
842
|
+
skip_connectors=skip_connectors,
|
|
843
|
+
current_ws=current_ws,
|
|
844
|
+
fork_downstream=fork_downstream,
|
|
845
|
+
is_internal=is_internal,
|
|
846
|
+
dir_path=dir_path,
|
|
847
|
+
embedded_datasources=embedded_ds,
|
|
848
|
+
)
|
|
834
849
|
else:
|
|
835
850
|
if verbose:
|
|
836
851
|
click.echo(FeedbackManager.info_processing_file(filename=filename))
|
|
@@ -838,12 +853,15 @@ async def get_processed(
|
|
|
838
853
|
if ".incl" in filename:
|
|
839
854
|
click.echo(FeedbackManager.warning_skipping_include_file(file=filename))
|
|
840
855
|
|
|
856
|
+
if tb_client is None:
|
|
857
|
+
raise ValueError("tb_client cannot be None")
|
|
858
|
+
|
|
841
859
|
name, warnings = await process(
|
|
842
860
|
filename=filename,
|
|
843
861
|
tb_client=tb_client,
|
|
844
|
-
deps=
|
|
845
|
-
dep_map=
|
|
846
|
-
to_run=
|
|
862
|
+
deps=deps_list,
|
|
863
|
+
dep_map=dep_map_dict,
|
|
864
|
+
to_run=to_run_dict,
|
|
847
865
|
vendor_paths=vendor_paths,
|
|
848
866
|
skip_connectors=skip_connectors,
|
|
849
867
|
current_ws=current_ws,
|
|
@@ -852,9 +870,9 @@ async def get_processed(
|
|
|
852
870
|
is_internal=is_internal,
|
|
853
871
|
dir_path=dir_path,
|
|
854
872
|
verbose=verbose,
|
|
855
|
-
embedded_datasources=
|
|
873
|
+
embedded_datasources=embedded_ds,
|
|
856
874
|
)
|
|
857
|
-
|
|
875
|
+
processed_set.add(name)
|
|
858
876
|
|
|
859
877
|
if verbose:
|
|
860
878
|
if len(warnings) == 1:
|
|
@@ -890,7 +908,7 @@ async def build_graph(
|
|
|
890
908
|
to_run: Dict[str, Any] = {}
|
|
891
909
|
deps: List[str] = []
|
|
892
910
|
dep_map: Dict[str, Any] = {}
|
|
893
|
-
embedded_datasources = {}
|
|
911
|
+
embedded_datasources: Dict[str, Dict[str, Any]] = {}
|
|
894
912
|
|
|
895
913
|
# These dictionaries are used to store all the resources and there dependencies for the whole project
|
|
896
914
|
# This is used for the downstream dependency graph
|
|
@@ -919,17 +937,18 @@ async def build_graph(
|
|
|
919
937
|
all_dep_map = all_dependencies_graph.dep_map
|
|
920
938
|
all_resources = all_dependencies_graph.to_run
|
|
921
939
|
|
|
922
|
-
processed = set()
|
|
940
|
+
processed: Set[str] = set()
|
|
923
941
|
|
|
924
942
|
await get_processed(
|
|
925
943
|
filenames=filenames,
|
|
926
|
-
tb_client=tb_client,
|
|
927
944
|
changed=changed,
|
|
945
|
+
verbose=verbose,
|
|
928
946
|
deps=deps,
|
|
929
947
|
dep_map=dep_map,
|
|
930
948
|
to_run=to_run,
|
|
931
949
|
vendor_paths=vendor_paths,
|
|
932
950
|
processed=processed,
|
|
951
|
+
tb_client=tb_client,
|
|
933
952
|
skip_connectors=skip_connectors,
|
|
934
953
|
current_ws=current_ws,
|
|
935
954
|
fork_downstream=fork_downstream,
|
|
@@ -1198,18 +1217,22 @@ async def process_file(
|
|
|
1198
1217
|
raise Exception(f"Invalid import schedule: '{cron}'. Valid values are: {valid_values}")
|
|
1199
1218
|
|
|
1200
1219
|
if cron == ON_DEMAND_CRON:
|
|
1220
|
+
if import_params is None:
|
|
1221
|
+
import_params = {}
|
|
1201
1222
|
import_params["import_schedule"] = ON_DEMAND_CRON_EXPECTED_BY_THE_API
|
|
1223
|
+
|
|
1202
1224
|
if cron == AUTO_CRON:
|
|
1203
1225
|
period: int = DEFAULT_CRON_PERIOD
|
|
1204
1226
|
|
|
1205
|
-
if current_ws:
|
|
1227
|
+
if current_ws is not None:
|
|
1206
1228
|
workspaces = (await tb_client.user_workspaces()).get("workspaces", [])
|
|
1207
1229
|
workspace_rate_limits: Dict[str, Dict[str, int]] = next(
|
|
1208
1230
|
(w.get("rate_limits", {}) for w in workspaces if w["id"] == current_ws["id"]), {}
|
|
1209
1231
|
)
|
|
1210
|
-
|
|
1211
|
-
"
|
|
1212
|
-
|
|
1232
|
+
if workspace_rate_limits:
|
|
1233
|
+
rate_limit_config = workspace_rate_limits.get("api_datasources_create_append_replace", {})
|
|
1234
|
+
if rate_limit_config:
|
|
1235
|
+
period = rate_limit_config.get("period", DEFAULT_CRON_PERIOD)
|
|
1213
1236
|
|
|
1214
1237
|
def seconds_to_cron_expression(seconds: int) -> str:
|
|
1215
1238
|
minutes = seconds // 60
|
|
@@ -1223,10 +1246,13 @@ async def process_file(
|
|
|
1223
1246
|
return f"*/{minutes} * * * *"
|
|
1224
1247
|
return f"*/{seconds} * * * *"
|
|
1225
1248
|
|
|
1249
|
+
if import_params is None:
|
|
1250
|
+
import_params = {}
|
|
1226
1251
|
import_params["import_schedule"] = seconds_to_cron_expression(period)
|
|
1227
1252
|
|
|
1228
|
-
|
|
1229
|
-
|
|
1253
|
+
# Include all import_ parameters in the datasource params
|
|
1254
|
+
if import_params is not None:
|
|
1255
|
+
params.update(import_params)
|
|
1230
1256
|
|
|
1231
1257
|
# Substitute the import parameters with the ones used by the
|
|
1232
1258
|
# import API:
|
|
@@ -124,10 +124,95 @@ def deployment_group() -> None:
|
|
|
124
124
|
help="Auto-promote the deployment. Only works if --wait is enabled. Disabled by default.",
|
|
125
125
|
)
|
|
126
126
|
@click.pass_context
|
|
127
|
-
def
|
|
127
|
+
def deployment_create(ctx: click.Context, wait: bool, auto: bool) -> None:
|
|
128
128
|
"""
|
|
129
129
|
Validate and deploy the project server side.
|
|
130
130
|
"""
|
|
131
|
+
create_deployment(ctx, wait, auto)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
@deployment_group.command(name="ls")
|
|
135
|
+
@click.pass_context
|
|
136
|
+
def deployment_ls(ctx: click.Context) -> None:
|
|
137
|
+
"""
|
|
138
|
+
List all the deployments you have in the project.
|
|
139
|
+
"""
|
|
140
|
+
client = ctx.ensure_object(dict)["client"]
|
|
141
|
+
|
|
142
|
+
TINYBIRD_API_KEY = client.token
|
|
143
|
+
HEADERS = {"Authorization": f"Bearer {TINYBIRD_API_KEY}"}
|
|
144
|
+
TINYBIRD_API_URL = f"{client.host}/v1/deployments"
|
|
145
|
+
|
|
146
|
+
r = requests.get(TINYBIRD_API_URL, headers=HEADERS)
|
|
147
|
+
result = r.json()
|
|
148
|
+
logging.debug(json.dumps(result, indent=2))
|
|
149
|
+
|
|
150
|
+
status_map = {"data_ready": "Ready", "failed": "Failed"}
|
|
151
|
+
columns = ["ID", "Status", "Created at", "Live"]
|
|
152
|
+
table = []
|
|
153
|
+
for deployment in result.get("deployments"):
|
|
154
|
+
table.append(
|
|
155
|
+
[
|
|
156
|
+
deployment.get("id"),
|
|
157
|
+
status_map.get(deployment.get("status"), "In progress"),
|
|
158
|
+
datetime.fromisoformat(deployment.get("created_at")).strftime("%Y-%m-%d %H:%M:%S"),
|
|
159
|
+
deployment.get("live"),
|
|
160
|
+
]
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
echo_safe_humanfriendly_tables_format_smart_table(table, column_names=columns)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
@deployment_group.command(name="promote")
|
|
167
|
+
@click.pass_context
|
|
168
|
+
def deployment_promote(ctx: click.Context) -> None:
|
|
169
|
+
"""
|
|
170
|
+
Promote last deploy to ready and remove old one.
|
|
171
|
+
"""
|
|
172
|
+
client = ctx.ensure_object(dict)["client"]
|
|
173
|
+
|
|
174
|
+
TINYBIRD_API_KEY = client.token
|
|
175
|
+
HEADERS = {"Authorization": f"Bearer {TINYBIRD_API_KEY}"}
|
|
176
|
+
|
|
177
|
+
promote_deployment(client.host, HEADERS)
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
@deployment_group.command(name="rollback")
|
|
181
|
+
@click.pass_context
|
|
182
|
+
def deployment_rollback(ctx: click.Context) -> None:
|
|
183
|
+
"""
|
|
184
|
+
Rollback to the previous deployment.
|
|
185
|
+
"""
|
|
186
|
+
client = ctx.ensure_object(dict)["client"]
|
|
187
|
+
|
|
188
|
+
TINYBIRD_API_KEY = client.token
|
|
189
|
+
HEADERS = {"Authorization": f"Bearer {TINYBIRD_API_KEY}"}
|
|
190
|
+
|
|
191
|
+
rollback_deployment(client.host, HEADERS)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
@cli.command(name="deploy")
|
|
195
|
+
@click.option(
|
|
196
|
+
"--wait/--no-wait",
|
|
197
|
+
is_flag=True,
|
|
198
|
+
default=False,
|
|
199
|
+
help="Wait for deploy to finish. Disabled by default.",
|
|
200
|
+
)
|
|
201
|
+
@click.option(
|
|
202
|
+
"--auto/--no-auto",
|
|
203
|
+
is_flag=True,
|
|
204
|
+
default=False,
|
|
205
|
+
help="Auto-promote the deployment. Only works if --wait is enabled. Disabled by default.",
|
|
206
|
+
)
|
|
207
|
+
@click.pass_context
|
|
208
|
+
def deploy(ctx: click.Context, wait: bool, auto: bool) -> None:
|
|
209
|
+
"""
|
|
210
|
+
Deploy the project.
|
|
211
|
+
"""
|
|
212
|
+
create_deployment(ctx, wait, auto)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def create_deployment(ctx: click.Context, wait: bool, auto: bool) -> None:
|
|
131
216
|
# TODO: This code is duplicated in build_server.py
|
|
132
217
|
# Should be refactored to be shared
|
|
133
218
|
MULTIPART_BOUNDARY_DATA_PROJECT = "data_project://"
|
|
@@ -203,63 +288,3 @@ def create(ctx: click.Context, wait: bool, auto: bool) -> None:
|
|
|
203
288
|
|
|
204
289
|
if auto:
|
|
205
290
|
promote_deployment(client.host, HEADERS)
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
@deployment_group.command(name="ls")
|
|
209
|
-
@click.pass_context
|
|
210
|
-
def deployment_ls(ctx: click.Context) -> None:
|
|
211
|
-
"""
|
|
212
|
-
List all the deployments you have in the project.
|
|
213
|
-
"""
|
|
214
|
-
client = ctx.ensure_object(dict)["client"]
|
|
215
|
-
|
|
216
|
-
TINYBIRD_API_KEY = client.token
|
|
217
|
-
HEADERS = {"Authorization": f"Bearer {TINYBIRD_API_KEY}"}
|
|
218
|
-
TINYBIRD_API_URL = f"{client.host}/v1/deployments"
|
|
219
|
-
|
|
220
|
-
r = requests.get(TINYBIRD_API_URL, headers=HEADERS)
|
|
221
|
-
result = r.json()
|
|
222
|
-
logging.debug(json.dumps(result, indent=2))
|
|
223
|
-
|
|
224
|
-
status_map = {"data_ready": "Ready", "failed": "Failed"}
|
|
225
|
-
columns = ["ID", "Status", "Created at", "Live"]
|
|
226
|
-
table = []
|
|
227
|
-
for deployment in result.get("deployments"):
|
|
228
|
-
table.append(
|
|
229
|
-
[
|
|
230
|
-
deployment.get("id"),
|
|
231
|
-
status_map.get(deployment.get("status"), "In progress"),
|
|
232
|
-
datetime.fromisoformat(deployment.get("created_at")).strftime("%Y-%m-%d %H:%M:%S"),
|
|
233
|
-
deployment.get("live"),
|
|
234
|
-
]
|
|
235
|
-
)
|
|
236
|
-
|
|
237
|
-
echo_safe_humanfriendly_tables_format_smart_table(table, column_names=columns)
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
@deployment_group.command(name="promote")
|
|
241
|
-
@click.pass_context
|
|
242
|
-
def deployment_promote(ctx: click.Context) -> None:
|
|
243
|
-
"""
|
|
244
|
-
Promote last deploy to ready and remove old one.
|
|
245
|
-
"""
|
|
246
|
-
client = ctx.ensure_object(dict)["client"]
|
|
247
|
-
|
|
248
|
-
TINYBIRD_API_KEY = client.token
|
|
249
|
-
HEADERS = {"Authorization": f"Bearer {TINYBIRD_API_KEY}"}
|
|
250
|
-
|
|
251
|
-
promote_deployment(client.host, HEADERS)
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
@deployment_group.command(name="rollback")
|
|
255
|
-
@click.pass_context
|
|
256
|
-
def deployment_rollback(ctx: click.Context) -> None:
|
|
257
|
-
"""
|
|
258
|
-
Rollback to the previous deployment.
|
|
259
|
-
"""
|
|
260
|
-
client = ctx.ensure_object(dict)["client"]
|
|
261
|
-
|
|
262
|
-
TINYBIRD_API_KEY = client.token
|
|
263
|
-
HEADERS = {"Authorization": f"Bearer {TINYBIRD_API_KEY}"}
|
|
264
|
-
|
|
265
|
-
rollback_deployment(client.host, HEADERS)
|
tinybird/tb/modules/endpoint.py
CHANGED
|
@@ -161,7 +161,7 @@ async def endpoint_url(ctx: Context, pipe: str):
|
|
|
161
161
|
click.echo(build_endpoint_url(client, pipe, token))
|
|
162
162
|
|
|
163
163
|
|
|
164
|
-
def build_endpoint_url(tb_client: TinyB, pipe_name: str, token: Optional[str]) -> str:
|
|
164
|
+
def build_endpoint_url(tb_client: TinyB, pipe_name: str, token: Optional[str]) -> Optional[str]:
|
|
165
165
|
try:
|
|
166
166
|
token = token or tb_client.token
|
|
167
167
|
example_params = {
|
tinybird/tb/modules/llm_utils.py
CHANGED
|
@@ -36,7 +36,7 @@ def generate(llm_call, task: str, feedback: str = "") -> tuple[str, str]:
|
|
|
36
36
|
thoughts = extract_xml(response, "thoughts")
|
|
37
37
|
result = extract_xml(response, "response")
|
|
38
38
|
|
|
39
|
-
return thoughts, result
|
|
39
|
+
return thoughts, result
|
|
40
40
|
|
|
41
41
|
|
|
42
42
|
def evaluate(llm_call, content: str, task: str) -> tuple[str, str]:
|
|
@@ -96,7 +96,7 @@ Output your evaluation concisely in the following format:
|
|
|
96
96
|
"""
|
|
97
97
|
|
|
98
98
|
generator_prompt = """
|
|
99
|
-
Your goal is to complete the task based on <task> tag. If there are feedback
|
|
99
|
+
Your goal is to complete the task based on <task> tag. If there are feedback
|
|
100
100
|
from your previous generations, you should reflect on them to solve the task.
|
|
101
101
|
All xml tags MUST be closed.
|
|
102
102
|
|
tinybird/tb/modules/pipe.py
CHANGED
|
@@ -326,96 +326,6 @@ async def print_pipe(ctx: Context, pipe: str, query: str, format_: str):
|
|
|
326
326
|
click.echo(res)
|
|
327
327
|
|
|
328
328
|
|
|
329
|
-
@pipe_copy.command(name="run", short_help="Run an on-demand copy job")
|
|
330
|
-
@click.argument("pipe_name_or_id")
|
|
331
|
-
@click.option("--wait", is_flag=True, default=False, help="Wait for the copy job to finish")
|
|
332
|
-
@click.option(
|
|
333
|
-
"--mode", type=click.Choice(["append", "replace"], case_sensitive=True), default=None, help="Copy strategy"
|
|
334
|
-
)
|
|
335
|
-
@click.option("--yes", is_flag=True, default=False, help="Do not ask for confirmation")
|
|
336
|
-
@click.option(
|
|
337
|
-
"--param",
|
|
338
|
-
nargs=1,
|
|
339
|
-
type=str,
|
|
340
|
-
multiple=True,
|
|
341
|
-
default=None,
|
|
342
|
-
help="Key and value of the params you want the Copy pipe to be called with. For example: tb pipe copy run <my_copy_pipe> --param foo=bar",
|
|
343
|
-
)
|
|
344
|
-
@click.pass_context
|
|
345
|
-
@coro
|
|
346
|
-
async def pipe_copy_run(
|
|
347
|
-
ctx: click.Context, pipe_name_or_id: str, wait: bool, mode: str, yes: bool, param: Optional[Tuple[str]]
|
|
348
|
-
):
|
|
349
|
-
"""Run an on-demand copy job"""
|
|
350
|
-
|
|
351
|
-
params = dict(key_value.split("=") for key_value in param) if param else {}
|
|
352
|
-
|
|
353
|
-
if yes or click.confirm(FeedbackManager.warning_confirm_copy_pipe(pipe=pipe_name_or_id)):
|
|
354
|
-
click.echo(FeedbackManager.info_copy_job_running(pipe=pipe_name_or_id))
|
|
355
|
-
client: TinyB = ctx.ensure_object(dict)["client"]
|
|
356
|
-
|
|
357
|
-
try:
|
|
358
|
-
response = await client.pipe_run_copy(pipe_name_or_id, params, mode)
|
|
359
|
-
|
|
360
|
-
job_id = response["job"]["job_id"]
|
|
361
|
-
job_url = response["job"]["job_url"]
|
|
362
|
-
target_datasource_id = response["tags"]["copy_target_datasource"]
|
|
363
|
-
target_datasource = await client.get_datasource(target_datasource_id)
|
|
364
|
-
target_datasource_name = target_datasource["name"]
|
|
365
|
-
click.echo(
|
|
366
|
-
FeedbackManager.success_copy_job_created(target_datasource=target_datasource_name, job_url=job_url)
|
|
367
|
-
)
|
|
368
|
-
|
|
369
|
-
if wait:
|
|
370
|
-
await wait_job(client, job_id, job_url, "** Copying data")
|
|
371
|
-
click.echo(FeedbackManager.success_data_copied_to_ds(target_datasource=target_datasource_name))
|
|
372
|
-
|
|
373
|
-
except AuthNoTokenException:
|
|
374
|
-
raise
|
|
375
|
-
except Exception as e:
|
|
376
|
-
raise CLIPipeException(FeedbackManager.error_creating_copy_job(error=e))
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
@pipe_copy.command(name="resume", short_help="Resume a paused copy pipe")
|
|
380
|
-
@click.argument("pipe_name_or_id")
|
|
381
|
-
@click.pass_context
|
|
382
|
-
@coro
|
|
383
|
-
async def pipe_copy_resume(ctx: click.Context, pipe_name_or_id: str):
|
|
384
|
-
"""Resume a paused copy pipe"""
|
|
385
|
-
|
|
386
|
-
click.echo(FeedbackManager.info_copy_pipe_resuming(pipe=pipe_name_or_id))
|
|
387
|
-
client: TinyB = ctx.ensure_object(dict)["client"]
|
|
388
|
-
|
|
389
|
-
try:
|
|
390
|
-
await client.pipe_resume_copy(pipe_name_or_id)
|
|
391
|
-
click.echo(FeedbackManager.success_copy_pipe_resumed(pipe=pipe_name_or_id))
|
|
392
|
-
|
|
393
|
-
except AuthNoTokenException:
|
|
394
|
-
raise
|
|
395
|
-
except Exception as e:
|
|
396
|
-
raise CLIPipeException(FeedbackManager.error_resuming_copy_pipe(error=e))
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
@pipe_copy.command(name="pause", short_help="Pause a running copy pipe")
|
|
400
|
-
@click.argument("pipe_name_or_id")
|
|
401
|
-
@click.pass_context
|
|
402
|
-
@coro
|
|
403
|
-
async def pipe_copy_pause(ctx: click.Context, pipe_name_or_id: str):
|
|
404
|
-
"""Pause a running copy pipe"""
|
|
405
|
-
|
|
406
|
-
click.echo(FeedbackManager.info_copy_pipe_pausing(pipe=pipe_name_or_id))
|
|
407
|
-
client: TinyB = ctx.ensure_object(dict)["client"]
|
|
408
|
-
|
|
409
|
-
try:
|
|
410
|
-
await client.pipe_pause_copy(pipe_name_or_id)
|
|
411
|
-
click.echo(FeedbackManager.success_copy_pipe_paused(pipe=pipe_name_or_id))
|
|
412
|
-
|
|
413
|
-
except AuthNoTokenException:
|
|
414
|
-
raise
|
|
415
|
-
except Exception as e:
|
|
416
|
-
raise CLIPipeException(FeedbackManager.error_pausing_copy_pipe(error=e))
|
|
417
|
-
|
|
418
|
-
|
|
419
329
|
@pipe_sink.command(name="run", short_help="Run an on-demand sink job")
|
|
420
330
|
@click.argument("pipe_name_or_id")
|
|
421
331
|
@click.option("--wait", is_flag=True, default=False, help="Wait for the sink job to finish")
|
tinybird/tb/modules/project.py
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import glob
|
|
2
2
|
import os
|
|
3
3
|
from pathlib import Path
|
|
4
|
-
from typing import List, Optional
|
|
4
|
+
from typing import Dict, List, Optional
|
|
5
5
|
|
|
6
6
|
from tinybird.tb.modules.config import CLIConfig
|
|
7
|
+
from tinybird.tb.modules.datafile.common import Datafile
|
|
8
|
+
from tinybird.tb.modules.datafile.parse_datasource import parse_datasource
|
|
9
|
+
from tinybird.tb.modules.datafile.parse_pipe import parse_pipe
|
|
7
10
|
|
|
8
11
|
|
|
9
12
|
class Project:
|
|
@@ -42,3 +45,24 @@ class Project:
|
|
|
42
45
|
@property
|
|
43
46
|
def pipes(self) -> List[str]:
|
|
44
47
|
return [Path(f).stem for f in glob.glob(f"{self.path}/**/*.pipe", recursive=True)]
|
|
48
|
+
|
|
49
|
+
def get_pipe_datafile(self, filename: str) -> Datafile:
|
|
50
|
+
return parse_pipe(filename)
|
|
51
|
+
|
|
52
|
+
def get_datasource_datafile(self, filename: str) -> Datafile:
|
|
53
|
+
return parse_datasource(filename)
|
|
54
|
+
|
|
55
|
+
def get_datafile(self, filename: str) -> Optional[Datafile]:
|
|
56
|
+
if filename.endswith(".pipe"):
|
|
57
|
+
return self.get_pipe_datafile(filename)
|
|
58
|
+
elif filename.endswith(".datasource"):
|
|
59
|
+
return self.get_datasource_datafile(filename)
|
|
60
|
+
return None
|
|
61
|
+
|
|
62
|
+
def get_project_datafiles(self) -> Dict[str, Datafile]:
|
|
63
|
+
project_filenames = self.get_project_files()
|
|
64
|
+
datafiles: Dict[str, Datafile] = {}
|
|
65
|
+
for filename in project_filenames:
|
|
66
|
+
if datafile := self.get_datafile(filename):
|
|
67
|
+
datafiles[filename] = datafile
|
|
68
|
+
return datafiles
|
tinybird/tb/modules/shell.py
CHANGED
|
@@ -14,9 +14,9 @@ from prompt_toolkit.key_binding import KeyBindings
|
|
|
14
14
|
from prompt_toolkit.shortcuts import CompleteStyle
|
|
15
15
|
from prompt_toolkit.styles import Style
|
|
16
16
|
|
|
17
|
+
from tinybird.client import TinyB
|
|
17
18
|
from tinybird.tb.modules.exceptions import CLIException
|
|
18
19
|
from tinybird.tb.modules.feedback_manager import FeedbackManager, bcolors
|
|
19
|
-
from tinybird.tb.modules.local_common import get_tinybird_local_client
|
|
20
20
|
from tinybird.tb.modules.project import Project
|
|
21
21
|
from tinybird.tb.modules.table import format_table
|
|
22
22
|
|
|
@@ -174,9 +174,10 @@ def _(event):
|
|
|
174
174
|
|
|
175
175
|
|
|
176
176
|
class Shell:
|
|
177
|
-
def __init__(self, project: Project):
|
|
177
|
+
def __init__(self, project: Project, tb_client: TinyB):
|
|
178
178
|
self.history = self.get_history()
|
|
179
179
|
self.project = project
|
|
180
|
+
self.tb_client = tb_client
|
|
180
181
|
self.prompt_message = "\ntb > "
|
|
181
182
|
self.commands = ["create", "mock", "test", "tb", "select"]
|
|
182
183
|
self.session: PromptSession = PromptSession(
|
|
@@ -285,9 +286,8 @@ class Shell:
|
|
|
285
286
|
loop = asyncio.new_event_loop()
|
|
286
287
|
asyncio.set_event_loop(loop)
|
|
287
288
|
try:
|
|
288
|
-
tb_client = asyncio.run(get_tinybird_local_client(str(self.project.path)))
|
|
289
289
|
return loop.run_until_complete(
|
|
290
|
-
tb_client.query(f"SELECT * FROM ({query}) LIMIT {rows_limit} FORMAT JSON")
|
|
290
|
+
self.tb_client.query(f"SELECT * FROM ({query}) LIMIT {rows_limit} FORMAT JSON")
|
|
291
291
|
)
|
|
292
292
|
finally:
|
|
293
293
|
loop.close()
|
tinybird/tb/modules/watch.py
CHANGED
|
@@ -15,6 +15,7 @@ from watchdog.events import (
|
|
|
15
15
|
)
|
|
16
16
|
from watchdog.observers import Observer
|
|
17
17
|
|
|
18
|
+
from tinybird.tb.modules.datafile.common import Datafile, DatafileKind
|
|
18
19
|
from tinybird.tb.modules.feedback_manager import FeedbackManager
|
|
19
20
|
from tinybird.tb.modules.project import Project
|
|
20
21
|
from tinybird.tb.modules.shell import Shell
|
|
@@ -108,7 +109,7 @@ def watch_files(
|
|
|
108
109
|
event_handler = FileChangeHandler(filenames, lambda f: asyncio.run(process_wrapper(f)), build_ok)
|
|
109
110
|
observer = Observer()
|
|
110
111
|
|
|
111
|
-
observer.schedule(event_handler, path=project.path, recursive=True)
|
|
112
|
+
observer.schedule(event_handler, path=str(project.path), recursive=True)
|
|
112
113
|
|
|
113
114
|
observer.start()
|
|
114
115
|
|
|
@@ -122,10 +123,16 @@ def watch_files(
|
|
|
122
123
|
|
|
123
124
|
|
|
124
125
|
class WatchProjectHandler(PatternMatchingEventHandler):
|
|
125
|
-
def __init__(
|
|
126
|
+
def __init__(
|
|
127
|
+
self,
|
|
128
|
+
shell: Shell,
|
|
129
|
+
project: Project,
|
|
130
|
+
process: Callable[[Optional[str], Optional[str]], None],
|
|
131
|
+
):
|
|
126
132
|
self.shell = shell
|
|
127
133
|
self.project = project
|
|
128
134
|
self.process = process
|
|
135
|
+
self.datafiles = project.get_project_datafiles()
|
|
129
136
|
super().__init__(
|
|
130
137
|
patterns=[
|
|
131
138
|
f"{project.path}/**/*.datasource",
|
|
@@ -150,7 +157,7 @@ class WatchProjectHandler(PatternMatchingEventHandler):
|
|
|
150
157
|
def _process(self, path: Optional[str] = None) -> None:
|
|
151
158
|
click.echo(FeedbackManager.highlight(message="» Rebuilding project..."))
|
|
152
159
|
time_start = time.time()
|
|
153
|
-
self.process(path)
|
|
160
|
+
self.process(path, self.diff(path))
|
|
154
161
|
time_end = time.time()
|
|
155
162
|
elapsed_time = time_end - time_start
|
|
156
163
|
click.echo(
|
|
@@ -159,6 +166,48 @@ class WatchProjectHandler(PatternMatchingEventHandler):
|
|
|
159
166
|
)
|
|
160
167
|
self.shell.reprint_prompt()
|
|
161
168
|
|
|
169
|
+
def diff(self, path: Optional[str] = None) -> Optional[str]:
|
|
170
|
+
if not path:
|
|
171
|
+
return None
|
|
172
|
+
|
|
173
|
+
current_datafile = self.datafiles.get(path, None)
|
|
174
|
+
new_datafile = self.project.get_datafile(path)
|
|
175
|
+
table_name = None
|
|
176
|
+
if current_datafile and new_datafile:
|
|
177
|
+
if current_datafile.kind == DatafileKind.datasource:
|
|
178
|
+
table_name = self.datasource_diff(current_datafile, new_datafile)
|
|
179
|
+
elif current_datafile.kind == DatafileKind.pipe:
|
|
180
|
+
table_name = self.pipe_diff(current_datafile, new_datafile)
|
|
181
|
+
|
|
182
|
+
self.refresh_datafiles()
|
|
183
|
+
return table_name
|
|
184
|
+
|
|
185
|
+
def refresh_datafiles(self) -> None:
|
|
186
|
+
self.datafiles = self.project.get_project_datafiles()
|
|
187
|
+
|
|
188
|
+
def datasource_diff(self, current_datafile: Datafile, new_datafile: Datafile) -> Optional[str]:
|
|
189
|
+
current_schema = current_datafile.nodes[0].get("schema")
|
|
190
|
+
new_schema = new_datafile.nodes[0].get("schema")
|
|
191
|
+
if current_schema != new_schema:
|
|
192
|
+
return current_datafile.nodes[0].get("name")
|
|
193
|
+
return None
|
|
194
|
+
|
|
195
|
+
def pipe_diff(self, current_datafile: Datafile, new_datafile: Datafile) -> Optional[str]:
|
|
196
|
+
current_nodes = current_datafile.nodes
|
|
197
|
+
current_sql_dict = {node.get("name"): node.get("sql") for node in current_nodes}
|
|
198
|
+
new_nodes = new_datafile.nodes
|
|
199
|
+
new_sql_dict = {node.get("name"): node.get("sql") for node in new_nodes}
|
|
200
|
+
for node in new_sql_dict.keys():
|
|
201
|
+
if node and node not in current_sql_dict:
|
|
202
|
+
return node
|
|
203
|
+
|
|
204
|
+
for node_name, sql in new_sql_dict.items():
|
|
205
|
+
current_sql = current_sql_dict.get(node_name)
|
|
206
|
+
if current_sql and current_sql != sql:
|
|
207
|
+
return node_name
|
|
208
|
+
|
|
209
|
+
return None
|
|
210
|
+
|
|
162
211
|
def on_modified(self, event: Any) -> None:
|
|
163
212
|
if path := self.should_process(event):
|
|
164
213
|
filename = Path(path).name
|
|
@@ -176,12 +225,12 @@ class WatchProjectHandler(PatternMatchingEventHandler):
|
|
|
176
225
|
|
|
177
226
|
def watch_project(
|
|
178
227
|
shell: Shell,
|
|
179
|
-
process: Callable[[Optional[str]], None],
|
|
228
|
+
process: Callable[[Optional[str], Optional[str]], None],
|
|
180
229
|
project: Project,
|
|
181
230
|
) -> None:
|
|
182
231
|
event_handler = WatchProjectHandler(shell=shell, project=project, process=process)
|
|
183
232
|
observer = Observer()
|
|
184
|
-
observer.schedule(event_handler, path=project.path, recursive=True)
|
|
233
|
+
observer.schedule(event_handler, path=str(project.path), recursive=True)
|
|
185
234
|
observer.start()
|
|
186
235
|
|
|
187
236
|
try:
|
|
@@ -1590,7 +1590,7 @@ async def try_update_config_with_remote(
|
|
|
1590
1590
|
def ask_for_admin_token_interactively(ui_host: str, default_token: Optional[str]) -> str:
|
|
1591
1591
|
return (
|
|
1592
1592
|
click.prompt(
|
|
1593
|
-
f
|
|
1593
|
+
f'\nCopy the "admin your@email" token from {ui_host}/tokens and paste it here {"OR press enter to use the token from .tinyb file" if default_token else ""}',
|
|
1594
1594
|
hide_input=True,
|
|
1595
1595
|
show_default=False,
|
|
1596
1596
|
default=default_token,
|
tinybird/tornado_template.py
CHANGED
|
@@ -370,7 +370,7 @@ class Template:
|
|
|
370
370
|
for chunk in self.file.body.chunks:
|
|
371
371
|
if isinstance(chunk, _ExtendsBlock):
|
|
372
372
|
if not loader:
|
|
373
|
-
raise ParseError("{% extends %} block found, but no
|
|
373
|
+
raise ParseError("{% extends %} block found, but no template loader")
|
|
374
374
|
template = loader.load(chunk.name, self.name)
|
|
375
375
|
ancestors.extend(template._get_ancestors(loader))
|
|
376
376
|
return ancestors
|
|
@@ -633,7 +633,7 @@ class _Expression(_Node):
|
|
|
633
633
|
|
|
634
634
|
def generate(self, writer):
|
|
635
635
|
writer.write_line("_tt_tmp = %s" % self.expression, self.line)
|
|
636
|
-
writer.write_line("if isinstance(_tt_tmp, _tt_string_types):
|
|
636
|
+
writer.write_line("if isinstance(_tt_tmp, _tt_string_types): _tt_tmp = _tt_utf8(_tt_tmp)", self.line)
|
|
637
637
|
writer.write_line("else: _tt_tmp = _tt_utf8(str(_tt_tmp))", self.line)
|
|
638
638
|
if not self.raw and writer.current_template.autoescape is not None:
|
|
639
639
|
# In python3 functions like xhtml_escape return unicode,
|
|
@@ -1,58 +1,58 @@
|
|
|
1
1
|
tinybird/__cli__.py,sha256=esPl5QDTzuQgHe5FuxWLm-fURFigGGwjnYLh9GuWUw4,232
|
|
2
|
-
tinybird/client.py,sha256=
|
|
2
|
+
tinybird/client.py,sha256=p3tpPC0QbObihnwGdmw1bRjKmQS2kOebJ9Me3ioM-Ww,51517
|
|
3
3
|
tinybird/config.py,sha256=ENRNyEMXHj_P882o31iFz0hTveziLabVRrxiWE5RRBE,6233
|
|
4
|
-
tinybird/connectors.py,sha256=
|
|
4
|
+
tinybird/connectors.py,sha256=7Gjms7b5MAaBFGi3xytsJurCylprONpFcYrzp4Fw2Rc,15241
|
|
5
5
|
tinybird/context.py,sha256=A3GBApac9xO6hrAMJ1s9dMrI_ou9aKF84CdEjtPddMk,1417
|
|
6
6
|
tinybird/datatypes.py,sha256=XNypumfqNjsvLJ5iNXnbVHRvAJe0aQwI3lS6Cxox-e0,10979
|
|
7
|
-
tinybird/feedback_manager.py,sha256=
|
|
7
|
+
tinybird/feedback_manager.py,sha256=g1r9NcFfKXdk_13soaiTZLvdoUGleVfawl6Yfj3zmRw,67823
|
|
8
8
|
tinybird/git_settings.py,sha256=Sw_8rGmribEFJ4Z_6idrVytxpFYk7ez8ei0qHULzs3E,3934
|
|
9
|
-
tinybird/prompts.py,sha256=
|
|
10
|
-
tinybird/sql.py,sha256=
|
|
9
|
+
tinybird/prompts.py,sha256=GEf-14SZZIAYcNUIbwnmmCrhylrRy6EuDyGo8-_Z6jo,25672
|
|
10
|
+
tinybird/sql.py,sha256=igHaRIeEREN5XYowwpIGYG8gDB5kn5p2cDNL1t8uc40,46168
|
|
11
11
|
tinybird/sql_template.py,sha256=GmMLAI10MTqjQo9qztuQHLRWs67teozsWDxUBdvkAn4,93668
|
|
12
|
-
tinybird/sql_template_fmt.py,sha256=
|
|
12
|
+
tinybird/sql_template_fmt.py,sha256=KUHdj5rYCYm_rKKdXYSJAE9vIyXUQLB0YSZnUXHeBlY,10196
|
|
13
13
|
tinybird/sql_toolset.py,sha256=NEUj8Ro5x9XlfVLlGr6nWt9o7OLWVxlqs6TIpgumUNs,14678
|
|
14
14
|
tinybird/syncasync.py,sha256=IPnOx6lMbf9SNddN1eBtssg8vCLHMt76SuZ6YNYm-Yk,27761
|
|
15
|
-
tinybird/tornado_template.py,sha256=
|
|
15
|
+
tinybird/tornado_template.py,sha256=FL85SMPq2dH4JqKovmSbaolGdEzwOO91NqOzqXo2Qr0,41863
|
|
16
16
|
tinybird/ch_utils/constants.py,sha256=aYvg2C_WxYWsnqPdZB1ZFoIr8ZY-XjUXYyHKE9Ansj0,3890
|
|
17
17
|
tinybird/ch_utils/engine.py,sha256=OXkBhlzGjZotjD0vaT-rFIbSGV4tpiHxE8qO_ip0SyQ,40454
|
|
18
|
-
tinybird/tb/__cli__.py,sha256=
|
|
18
|
+
tinybird/tb/__cli__.py,sha256=ezvYUt2sERQfnRCQPlsLhgBYbA8MgSBu2-Y-q-WNVug,251
|
|
19
19
|
tinybird/tb/cli.py,sha256=_kYDnDS3a45MMKJFZnYZx1gLuVqs4N_Rt8GO4sueAeg,957
|
|
20
20
|
tinybird/tb/modules/auth.py,sha256=EzRWFmwRkXNhUmRaruEVFLdkbUg8xMSix0cAWl5D4Jg,9029
|
|
21
|
-
tinybird/tb/modules/build.py,sha256=
|
|
22
|
-
tinybird/tb/modules/build_client.py,sha256=
|
|
23
|
-
tinybird/tb/modules/cicd.py,sha256=
|
|
24
|
-
tinybird/tb/modules/cli.py,sha256=
|
|
21
|
+
tinybird/tb/modules/build.py,sha256=mdry4XN8rIOomgOXBJmJwF53y2xzY8sej58PQEdSzz0,7647
|
|
22
|
+
tinybird/tb/modules/build_client.py,sha256=3gKxFFNjgUDDPiBpGeLhmm6RB2TNj6e3Z8yUuEg3DEc,7232
|
|
23
|
+
tinybird/tb/modules/cicd.py,sha256=xxXwy-QekJcG14kkJeGNl7LkHduhZXfvBZE8WrU6-t4,5351
|
|
24
|
+
tinybird/tb/modules/cli.py,sha256=8pyz442S-s_Xdr-jPCfVeBwQ4RJC09B4BDKaVhYfpR4,19442
|
|
25
25
|
tinybird/tb/modules/common.py,sha256=e4U7AT0dUBG6O-7Iq2CVN1UHPd6-ZCFucyW0L5gBi4g,70592
|
|
26
26
|
tinybird/tb/modules/config.py,sha256=mie3oMVTf5YOUFEiLs88P16U4LkJafJjSpjwyAkFHog,10979
|
|
27
|
-
tinybird/tb/modules/copy.py,sha256=
|
|
27
|
+
tinybird/tb/modules/copy.py,sha256=QwEloYVSM4zIQ8iTRyLx355-GvZ-BEJ-kzSX81PcRXw,5927
|
|
28
28
|
tinybird/tb/modules/create.py,sha256=iUYt5XG-GPwE3LnHrWqlOHmke0Bv8VMPxjAffxRFYoQ,11438
|
|
29
29
|
tinybird/tb/modules/datasource.py,sha256=-VG2qKlu0fmkhsIB5bPiTp3XuktB_r-ZkIoohEBEXtI,13713
|
|
30
|
-
tinybird/tb/modules/deployment.py,sha256=
|
|
31
|
-
tinybird/tb/modules/endpoint.py,sha256=
|
|
30
|
+
tinybird/tb/modules/deployment.py,sha256=TaFpS6aowGaZ-XjvMdof2sO7yJS2MCsZRqmB3v3_eSs,10228
|
|
31
|
+
tinybird/tb/modules/endpoint.py,sha256=tR0_NEZd0INJEPO6e4manWc6Qw5FT4IZFW6muxTCnrI,6498
|
|
32
32
|
tinybird/tb/modules/exceptions.py,sha256=4A2sSjCEqKUMqpP3WI00zouCWW4uLaghXXLZBSw04mY,3363
|
|
33
33
|
tinybird/tb/modules/feedback_manager.py,sha256=e8tqehRR0Buhs8O0n8N2Sg2vnnBVb1NLtnZqkPrYD_A,68379
|
|
34
34
|
tinybird/tb/modules/fmt.py,sha256=poh6_cwVGSf-sBu6LKWuO2TANL_J8Sgm25sPpwxa3Aw,3558
|
|
35
35
|
tinybird/tb/modules/job.py,sha256=956Pj8BEEsiD2GZsV9RKKVM3I_CveOLgS82lykO5ukk,2963
|
|
36
36
|
tinybird/tb/modules/llm.py,sha256=AC0VSphTOM2t-v1_3NLvNN_FIbgMo4dTyMqIv5nniPo,835
|
|
37
|
-
tinybird/tb/modules/llm_utils.py,sha256=
|
|
37
|
+
tinybird/tb/modules/llm_utils.py,sha256=nS9r4FAElJw8yXtmdYrx-rtI2zXR8qXfi1QqUDCfxvg,3469
|
|
38
38
|
tinybird/tb/modules/local.py,sha256=x4xuCGVkoa8KLYGZEJnFUP8HUkKX05Frp_djRVjVjTs,5669
|
|
39
39
|
tinybird/tb/modules/local_common.py,sha256=afPW6bSc-YI7Q4ngvBV53sM7QxnPGBMe5N91iETM2yE,2783
|
|
40
40
|
tinybird/tb/modules/login.py,sha256=0cS-f3MsQFHc6xjw8FRWJm4EJBH9C7Ri68EcO_tiwes,6508
|
|
41
41
|
tinybird/tb/modules/mock.py,sha256=XoCaFFroJf2jWVxEztPelwXYNle__KilON1e81Mxkd4,3764
|
|
42
|
-
tinybird/tb/modules/pipe.py,sha256=
|
|
43
|
-
tinybird/tb/modules/project.py,sha256=
|
|
42
|
+
tinybird/tb/modules/pipe.py,sha256=fQAlH5V00rKl9kvBWTjgAC3scvNA00ogANGT5hozxkI,14051
|
|
43
|
+
tinybird/tb/modules/project.py,sha256=9bP6G4LUMkkeC_M47d89fKt2Bg_yEMPsO9u5eQW68uo,2450
|
|
44
44
|
tinybird/tb/modules/regions.py,sha256=QjsL5H6Kg-qr0aYVLrvb1STeJ5Sx_sjvbOYO0LrEGMk,166
|
|
45
|
-
tinybird/tb/modules/shell.py,sha256=
|
|
45
|
+
tinybird/tb/modules/shell.py,sha256=iCX9HsvNhVDecop-s4zDPjXfL9GQ56-QODqwHsN5oT8,12994
|
|
46
46
|
tinybird/tb/modules/table.py,sha256=4XrtjM-N0zfNtxVkbvLDQQazno1EPXnxTyo7llivfXk,11035
|
|
47
47
|
tinybird/tb/modules/tag.py,sha256=anPmMUBc-TbFovlpFi8GPkKA18y7Y0GczMsMms5TZsU,3502
|
|
48
48
|
tinybird/tb/modules/telemetry.py,sha256=iEGnMuCuNhvF6ln__j6X9MSTwL_0Hm-GgFHHHvhfknk,10466
|
|
49
49
|
tinybird/tb/modules/test.py,sha256=zciS9klk2dNBiyncqtXgGTVao1B7p4S9hg_Oat_FJIY,11616
|
|
50
50
|
tinybird/tb/modules/token.py,sha256=sPdJoBE-6dd3Sd6W-prst7VOoJ0NbvP0uTaB6dXHs5s,12711
|
|
51
51
|
tinybird/tb/modules/update.py,sha256=RCOGhxJPhlwCzRzPPYFfbvpJeDKu3yEISWUJinr953M,6821
|
|
52
|
-
tinybird/tb/modules/watch.py,sha256=
|
|
52
|
+
tinybird/tb/modules/watch.py,sha256=Kredt5C7OOiI6YOivuR5QBdiDY4J_xLiwqHOROnfcsU,8591
|
|
53
53
|
tinybird/tb/modules/workspace.py,sha256=M0RtXCaw7RdZ3c_fqtmjbVb7HqlV742Drn4OiyZlp3M,6345
|
|
54
54
|
tinybird/tb/modules/workspace_members.py,sha256=Ai6iCOzXX1zQ8q9iXIFSFHsBJlT-8Q28DaG5Ie-UweY,8726
|
|
55
|
-
tinybird/tb/modules/datafile/build.py,sha256=
|
|
55
|
+
tinybird/tb/modules/datafile/build.py,sha256=bq_As61bOqPF0NzeUq8sx3EnCCte4KDoDQZYW6n6UIw,59060
|
|
56
56
|
tinybird/tb/modules/datafile/build_common.py,sha256=IXl-Z51zUi1dypV7meNenX0iu2UmowNeqgG6WHyMHlk,4562
|
|
57
57
|
tinybird/tb/modules/datafile/build_datasource.py,sha256=4aP8_DYCRGghXntZSeWDNJxjps1QRVa7WHoYCzQwQts,17355
|
|
58
58
|
tinybird/tb/modules/datafile/build_pipe.py,sha256=Jgv3YKIvMfjPiSIdw1k2mpaoDdAWMiMRaSHwRgyI97E,28258
|
|
@@ -70,13 +70,13 @@ tinybird/tb/modules/datafile/pull.py,sha256=vcjMUbjnZ9XQMGmL33J3ElpbXBTat8Yzp-ha
|
|
|
70
70
|
tinybird/tb/modules/tinyunit/tinyunit.py,sha256=3EBqKzNCfyDuZiO4H61ihanFBRLFUGeuXf3nDXnYFcU,11727
|
|
71
71
|
tinybird/tb/modules/tinyunit/tinyunit_lib.py,sha256=hGh1ZaXC1af7rKnX7222urkj0QJMhMWclqMy59dOqwE,1922
|
|
72
72
|
tinybird/tb_cli_modules/cicd.py,sha256=0lMkb6CVOFZl5HOwgY8mK4T4mgI7O8335UngLXtCc-c,13851
|
|
73
|
-
tinybird/tb_cli_modules/common.py,sha256=
|
|
73
|
+
tinybird/tb_cli_modules/common.py,sha256=SnC_PLCJHwaFaNg7LsUtc1s5jgiD_vkglbva0D3w8RA,78833
|
|
74
74
|
tinybird/tb_cli_modules/config.py,sha256=6u6B5QCdiQLbJkCkwtnKGs9H3nP-KXXhC75mF7B-1DQ,11464
|
|
75
75
|
tinybird/tb_cli_modules/exceptions.py,sha256=pmucP4kTF4irIt7dXiG-FcnI-o3mvDusPmch1L8RCWk,3367
|
|
76
76
|
tinybird/tb_cli_modules/regions.py,sha256=QjsL5H6Kg-qr0aYVLrvb1STeJ5Sx_sjvbOYO0LrEGMk,166
|
|
77
77
|
tinybird/tb_cli_modules/telemetry.py,sha256=iEGnMuCuNhvF6ln__j6X9MSTwL_0Hm-GgFHHHvhfknk,10466
|
|
78
|
-
tinybird-0.0.1.
|
|
79
|
-
tinybird-0.0.1.
|
|
80
|
-
tinybird-0.0.1.
|
|
81
|
-
tinybird-0.0.1.
|
|
82
|
-
tinybird-0.0.1.
|
|
78
|
+
tinybird-0.0.1.dev43.dist-info/METADATA,sha256=8vZ7c2enj_Q8TUAt2lEOvC4KgA3VZ3Ony-v89LGotmc,2482
|
|
79
|
+
tinybird-0.0.1.dev43.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
|
|
80
|
+
tinybird-0.0.1.dev43.dist-info/entry_points.txt,sha256=LwdHU6TfKx4Qs7BqqtaczEZbImgU7Abe9Lp920zb_fo,43
|
|
81
|
+
tinybird-0.0.1.dev43.dist-info/top_level.txt,sha256=pgw6AzERHBcW3YTi2PW4arjxLkulk2msOz_SomfOEuc,45
|
|
82
|
+
tinybird-0.0.1.dev43.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|