tinybird 0.0.1.dev13__tar.gz → 0.0.1.dev15__tar.gz
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-0.0.1.dev13 → tinybird-0.0.1.dev15}/PKG-INFO +1 -1
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/datafile.py +3 -3
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb/cli.py +0 -1
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb/modules/build.py +67 -18
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb/modules/cli.py +8 -29
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb/modules/create.py +5 -9
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb/modules/datafile/build.py +29 -21
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb/modules/datafile/build_pipe.py +4 -0
- tinybird-0.0.1.dev15/tinybird/tb/modules/datafile/common.py +1850 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb/modules/datafile/diff.py +2 -2
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb/modules/datafile/parse_datasource.py +1 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb/modules/datafile/pull.py +17 -21
- tinybird-0.0.1.dev15/tinybird/tb/modules/llm.py +63 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb/modules/local.py +3 -4
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb/modules/login.py +5 -3
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb/modules/mock.py +5 -4
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb/modules/prompts.py +23 -1
- tinybird-0.0.1.dev15/tinybird/tb/modules/test.py +127 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird.egg-info/PKG-INFO +1 -1
- tinybird-0.0.1.dev13/tinybird/tb/modules/datafile/common.py +0 -910
- tinybird-0.0.1.dev13/tinybird/tb/modules/llm.py +0 -73
- tinybird-0.0.1.dev13/tinybird/tb/modules/test.py +0 -107
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/setup.cfg +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/__cli__.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/ch_utils/constants.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/ch_utils/engine.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/check_pypi.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/client.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/config.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/connectors.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/context.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/datatypes.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/feedback_manager.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/git_settings.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/sql.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/sql_template.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/sql_template_fmt.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/sql_toolset.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/syncasync.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb/modules/auth.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb/modules/branch.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb/modules/cicd.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb/modules/common.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb/modules/config.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb/modules/connection.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb/modules/datafile/build_common.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb/modules/datafile/build_datasource.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb/modules/datafile/exceptions.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb/modules/datafile/fixture.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb/modules/datafile/format_common.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb/modules/datafile/format_datasource.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb/modules/datafile/format_pipe.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb/modules/datafile/parse_pipe.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb/modules/datafile/pipe_checker.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb/modules/datasource.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb/modules/exceptions.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb/modules/fmt.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb/modules/job.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb/modules/pipe.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb/modules/regions.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb/modules/table.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb/modules/tag.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb/modules/telemetry.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb/modules/tinyunit/tinyunit.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb/modules/tinyunit/tinyunit_lib.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb/modules/token.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb/modules/workspace.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb/modules/workspace_members.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb_cli.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb_cli_modules/auth.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb_cli_modules/branch.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb_cli_modules/cicd.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb_cli_modules/cli.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb_cli_modules/common.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb_cli_modules/config.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb_cli_modules/connection.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb_cli_modules/datasource.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb_cli_modules/exceptions.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb_cli_modules/fmt.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb_cli_modules/job.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb_cli_modules/pipe.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb_cli_modules/regions.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb_cli_modules/tag.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb_cli_modules/telemetry.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb_cli_modules/test.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb_cli_modules/tinyunit/tinyunit.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb_cli_modules/tinyunit/tinyunit_lib.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb_cli_modules/workspace.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tb_cli_modules/workspace_members.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird/tornado_template.py +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird.egg-info/SOURCES.txt +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird.egg-info/dependency_links.txt +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird.egg-info/entry_points.txt +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird.egg-info/requires.txt +0 -0
- {tinybird-0.0.1.dev13 → tinybird-0.0.1.dev15}/tinybird.egg-info/top_level.txt +0 -0
|
@@ -1051,9 +1051,9 @@ def parse(
|
|
|
1051
1051
|
def _f(sql: str, **kwargs: Any) -> None:
|
|
1052
1052
|
if not parser_state.current_node:
|
|
1053
1053
|
raise ParseException("SQL must be called after a NODE command")
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1054
|
+
_sql = textwrap.dedent(sql).rstrip() if "%" not in sql.strip()[0] else sql.strip()
|
|
1055
|
+
_sql = eval_var(_sql)
|
|
1056
|
+
parser_state.current_node[var_name] = _sql
|
|
1057
1057
|
|
|
1058
1058
|
# HACK this cast is needed because Mypy
|
|
1059
1059
|
return cast(Callable[[str, KwArg(Any)], None], _f)
|
|
@@ -18,7 +18,6 @@ import tinybird.tb.modules.login
|
|
|
18
18
|
import tinybird.tb.modules.mock
|
|
19
19
|
import tinybird.tb.modules.pipe
|
|
20
20
|
import tinybird.tb.modules.tag
|
|
21
|
-
import tinybird.tb.modules.test
|
|
22
21
|
import tinybird.tb.modules.token
|
|
23
22
|
import tinybird.tb.modules.workspace
|
|
24
23
|
import tinybird.tb.modules.workspace_members
|
|
@@ -11,11 +11,14 @@ from typing import Any, Awaitable, Callable, List, Union
|
|
|
11
11
|
|
|
12
12
|
import click
|
|
13
13
|
import humanfriendly
|
|
14
|
+
from click import Context
|
|
14
15
|
from watchdog.events import FileSystemEventHandler
|
|
15
16
|
from watchdog.observers import Observer
|
|
16
17
|
|
|
17
18
|
import tinybird.context as context
|
|
18
|
-
from tinybird.
|
|
19
|
+
from tinybird.syncasync import async_to_sync
|
|
20
|
+
from tinybird.client import TinyB, AuthNoTokenException
|
|
21
|
+
from tinybird.tb.modules.common import CLIException
|
|
19
22
|
from tinybird.config import FeatureFlags
|
|
20
23
|
from tinybird.feedback_manager import FeedbackManager, bcolors
|
|
21
24
|
from tinybird.tb.modules.cli import cli
|
|
@@ -33,10 +36,10 @@ from tinybird.tb.modules.table import format_table
|
|
|
33
36
|
class BuildShell(cmd.Cmd):
|
|
34
37
|
prompt = "\n\001\033[1;32m\002TB > \001\033[0m\002"
|
|
35
38
|
|
|
36
|
-
def __init__(self, folder: str):
|
|
39
|
+
def __init__(self, folder: str, client: TinyB):
|
|
37
40
|
super().__init__()
|
|
38
41
|
self.folder = folder
|
|
39
|
-
|
|
42
|
+
self.client = client
|
|
40
43
|
def do_exit(self, arg):
|
|
41
44
|
sys.exit(0)
|
|
42
45
|
|
|
@@ -55,7 +58,11 @@ class BuildShell(cmd.Cmd):
|
|
|
55
58
|
extra_args = f" --folder {self.folder}" if arg_stripped.startswith("tb mock") else ""
|
|
56
59
|
subprocess.run(arg_stripped + extra_args, shell=True, text=True)
|
|
57
60
|
elif arg_stripped.startswith("with") or arg_stripped.startswith("select"):
|
|
58
|
-
|
|
61
|
+
try:
|
|
62
|
+
run_sql(self.client, argline)
|
|
63
|
+
except Exception as e:
|
|
64
|
+
click.echo(FeedbackManager.error(message=str(e)))
|
|
65
|
+
|
|
59
66
|
elif arg_stripped.startswith("mock "):
|
|
60
67
|
subprocess.run(f"tb {arg_stripped} --folder {self.folder}", shell=True, text=True)
|
|
61
68
|
else:
|
|
@@ -72,7 +79,12 @@ class FileChangeHandler(FileSystemEventHandler):
|
|
|
72
79
|
self.process = process
|
|
73
80
|
|
|
74
81
|
def on_modified(self, event: Any) -> None:
|
|
75
|
-
|
|
82
|
+
is_not_vendor = "vendor/" not in event.src_path
|
|
83
|
+
if (
|
|
84
|
+
is_not_vendor
|
|
85
|
+
and not event.is_directory
|
|
86
|
+
and any(event.src_path.endswith(ext) for ext in [".datasource", ".pipe", ".ndjson"])
|
|
87
|
+
):
|
|
76
88
|
filename = event.src_path.split("/")[-1]
|
|
77
89
|
click.echo(FeedbackManager.highlight(message=f"\n\n⟲ Changes detected in {filename}\n"))
|
|
78
90
|
try:
|
|
@@ -119,6 +131,44 @@ def watch_files(
|
|
|
119
131
|
observer.join()
|
|
120
132
|
|
|
121
133
|
|
|
134
|
+
|
|
135
|
+
def run_sql(client: TinyB, query, rows_limit=20):
|
|
136
|
+
try:
|
|
137
|
+
q = query.strip()
|
|
138
|
+
if q.startswith("insert"):
|
|
139
|
+
click.echo(FeedbackManager.info_append_data())
|
|
140
|
+
raise CLIException(FeedbackManager.error_invalid_query())
|
|
141
|
+
if q.startswith("delete"):
|
|
142
|
+
raise CLIException(FeedbackManager.error_invalid_query())
|
|
143
|
+
|
|
144
|
+
# fuck my life
|
|
145
|
+
def run_query_in_thread():
|
|
146
|
+
loop = asyncio.new_event_loop()
|
|
147
|
+
asyncio.set_event_loop(loop)
|
|
148
|
+
try:
|
|
149
|
+
return loop.run_until_complete(client.query(f"SELECT * FROM ({query}) LIMIT {rows_limit} FORMAT JSON"))
|
|
150
|
+
finally:
|
|
151
|
+
loop.close()
|
|
152
|
+
|
|
153
|
+
# Run the query in a separate thread
|
|
154
|
+
import concurrent.futures
|
|
155
|
+
with concurrent.futures.ThreadPoolExecutor() as executor:
|
|
156
|
+
res = executor.submit(run_query_in_thread).result()
|
|
157
|
+
|
|
158
|
+
except AuthNoTokenException:
|
|
159
|
+
raise
|
|
160
|
+
except Exception as e:
|
|
161
|
+
raise CLIException(FeedbackManager.error_exception(error=str(e)))
|
|
162
|
+
|
|
163
|
+
if isinstance(res, dict) and "error" in res:
|
|
164
|
+
raise CLIException(FeedbackManager.error_exception(error=res["error"]))
|
|
165
|
+
|
|
166
|
+
if isinstance(res, dict) and "data" in res and res["data"]:
|
|
167
|
+
print_table_formatted(res, 'QUERY')
|
|
168
|
+
else:
|
|
169
|
+
click.echo(FeedbackManager.info_no_rows())
|
|
170
|
+
|
|
171
|
+
|
|
122
172
|
@cli.command()
|
|
123
173
|
@click.option(
|
|
124
174
|
"--folder",
|
|
@@ -132,16 +182,12 @@ def watch_files(
|
|
|
132
182
|
is_flag=True,
|
|
133
183
|
help="Watch for changes in the files and re-check them.",
|
|
134
184
|
)
|
|
135
|
-
@click.
|
|
136
|
-
"--skip-datasources",
|
|
137
|
-
is_flag=True,
|
|
138
|
-
help="Skip rebuilding datasources.",
|
|
139
|
-
)
|
|
185
|
+
@click.pass_context
|
|
140
186
|
@coro
|
|
141
187
|
async def build(
|
|
188
|
+
ctx: Context,
|
|
142
189
|
folder: str,
|
|
143
190
|
watch: bool,
|
|
144
|
-
skip_datasources: bool,
|
|
145
191
|
) -> None:
|
|
146
192
|
"""
|
|
147
193
|
Watch for changes in the files and re-check them.
|
|
@@ -171,7 +217,7 @@ async def build(
|
|
|
171
217
|
|
|
172
218
|
parser(filename)
|
|
173
219
|
|
|
174
|
-
async def process(filenames: List[str], watch: bool = False
|
|
220
|
+
async def process(filenames: List[str], watch: bool = False):
|
|
175
221
|
datafiles = [f for f in filenames if f.endswith(".datasource") or f.endswith(".pipe")]
|
|
176
222
|
if len(datafiles) > 0:
|
|
177
223
|
check_filenames(filenames=datafiles)
|
|
@@ -180,7 +226,7 @@ async def build(
|
|
|
180
226
|
filenames=datafiles,
|
|
181
227
|
ignore_sql_errors=ignore_sql_errors,
|
|
182
228
|
is_internal=is_internal,
|
|
183
|
-
|
|
229
|
+
watch=watch,
|
|
184
230
|
)
|
|
185
231
|
|
|
186
232
|
filename = filenames[0]
|
|
@@ -209,7 +255,7 @@ async def build(
|
|
|
209
255
|
try:
|
|
210
256
|
click.echo("⚡ Building project...\n")
|
|
211
257
|
time_start = time.time()
|
|
212
|
-
await process(filenames=filenames, watch=False
|
|
258
|
+
await process(filenames=filenames, watch=False)
|
|
213
259
|
time_end = time.time()
|
|
214
260
|
elapsed_time = time_end - time_start
|
|
215
261
|
for filename in filenames:
|
|
@@ -226,7 +272,7 @@ async def build(
|
|
|
226
272
|
await build_once(filenames)
|
|
227
273
|
|
|
228
274
|
if watch:
|
|
229
|
-
shell = BuildShell(folder=folder)
|
|
275
|
+
shell = BuildShell(folder=folder, client=tb_client)
|
|
230
276
|
click.echo(FeedbackManager.highlight(message="◎ Watching for changes..."))
|
|
231
277
|
watcher_thread = threading.Thread(target=watch_files, args=(filenames, process, shell, folder), daemon=True)
|
|
232
278
|
watcher_thread.start()
|
|
@@ -234,13 +280,16 @@ async def build(
|
|
|
234
280
|
|
|
235
281
|
|
|
236
282
|
async def build_and_print_resource(tb_client: TinyB, filename: str):
|
|
237
|
-
rebuild_colors = [bcolors.FAIL, bcolors.OKBLUE, bcolors.WARNING, bcolors.OKGREEN, bcolors.HEADER]
|
|
238
|
-
rebuild_index = random.randint(0, len(rebuild_colors) - 1)
|
|
239
|
-
rebuild_color = rebuild_colors[rebuild_index % len(rebuild_colors)]
|
|
240
283
|
resource_path = Path(filename)
|
|
241
284
|
name = resource_path.stem
|
|
242
285
|
pipeline = name if filename.endswith(".pipe") else None
|
|
243
286
|
res = await tb_client.query(f"SELECT * FROM {name} FORMAT JSON", pipeline=pipeline)
|
|
287
|
+
print_table_formatted(res, name)
|
|
288
|
+
|
|
289
|
+
def print_table_formatted(res: dict, name: str):
|
|
290
|
+
rebuild_colors = [bcolors.FAIL, bcolors.OKBLUE, bcolors.WARNING, bcolors.OKGREEN, bcolors.HEADER]
|
|
291
|
+
rebuild_index = random.randint(0, len(rebuild_colors) - 1)
|
|
292
|
+
rebuild_color = rebuild_colors[rebuild_index % len(rebuild_colors)]
|
|
244
293
|
data = []
|
|
245
294
|
limit = 5
|
|
246
295
|
for d in res["data"][:5]:
|
|
@@ -10,7 +10,7 @@ import pprint
|
|
|
10
10
|
import re
|
|
11
11
|
import shutil
|
|
12
12
|
import sys
|
|
13
|
-
from os import
|
|
13
|
+
from os import getcwd
|
|
14
14
|
from pathlib import Path
|
|
15
15
|
from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple, Union
|
|
16
16
|
|
|
@@ -26,7 +26,7 @@ from tinybird.client import (
|
|
|
26
26
|
DoesNotExistException,
|
|
27
27
|
TinyB,
|
|
28
28
|
)
|
|
29
|
-
from tinybird.config import
|
|
29
|
+
from tinybird.config import SUPPORTED_CONNECTORS, VERSION, FeatureFlags, get_config
|
|
30
30
|
from tinybird.feedback_manager import FeedbackManager
|
|
31
31
|
from tinybird.tb.modules.common import (
|
|
32
32
|
OLDEST_ROLLBACK,
|
|
@@ -50,6 +50,7 @@ from tinybird.tb.modules.config import CLIConfig
|
|
|
50
50
|
from tinybird.tb.modules.datafile.build import build_graph, folder_push
|
|
51
51
|
from tinybird.tb.modules.datafile.common import (
|
|
52
52
|
Datafile,
|
|
53
|
+
DatafileSyntaxError,
|
|
53
54
|
get_project_filenames,
|
|
54
55
|
get_resource_versions,
|
|
55
56
|
has_internal_datafiles,
|
|
@@ -106,12 +107,6 @@ DEFAULT_PATTERNS: List[Tuple[str, Union[str, Callable[[str], str]]]] = [
|
|
|
106
107
|
@click.option(
|
|
107
108
|
"--with-headers", help="Flag to enable connector to export with headers", is_flag=True, default=False, hidden=True
|
|
108
109
|
)
|
|
109
|
-
@click.option(
|
|
110
|
-
"--version-warning/--no-version-warning",
|
|
111
|
-
envvar="TB_VERSION_WARNING",
|
|
112
|
-
default=True,
|
|
113
|
-
help="Don't print version warning message if there's a new available version. You can use TB_VERSION_WARNING envar",
|
|
114
|
-
)
|
|
115
110
|
@click.option("--show-tokens", is_flag=True, default=False, help="Enable the output of tokens")
|
|
116
111
|
@click.version_option(version=VERSION)
|
|
117
112
|
@click.pass_context
|
|
@@ -135,7 +130,6 @@ async def cli(
|
|
|
135
130
|
sf_storage_integration: str,
|
|
136
131
|
sf_stage,
|
|
137
132
|
with_headers: bool,
|
|
138
|
-
version_warning: bool,
|
|
139
133
|
show_tokens: bool,
|
|
140
134
|
) -> None:
|
|
141
135
|
"""
|
|
@@ -151,17 +145,6 @@ async def cli(
|
|
|
151
145
|
if getenv_bool("TB_DISABLE_SSL_CHECKS", False):
|
|
152
146
|
click.echo(FeedbackManager.warning_disabled_ssl_checks())
|
|
153
147
|
|
|
154
|
-
# ensure that ctx.obj exists and is a dict (in case `cli()` is called)
|
|
155
|
-
# by means other than the `if` block below
|
|
156
|
-
if not environ.get("PYTEST", None) and version_warning and not token:
|
|
157
|
-
from tinybird.check_pypi import CheckPypi
|
|
158
|
-
|
|
159
|
-
latest_version = await CheckPypi().get_latest_version()
|
|
160
|
-
|
|
161
|
-
if "x.y.z" not in CURRENT_VERSION and latest_version != CURRENT_VERSION:
|
|
162
|
-
click.echo(FeedbackManager.warning_update_version(latest_version=latest_version))
|
|
163
|
-
click.echo(FeedbackManager.warning_current_version(current_version=CURRENT_VERSION))
|
|
164
|
-
|
|
165
148
|
if debug:
|
|
166
149
|
logging.basicConfig(level=logging.DEBUG)
|
|
167
150
|
|
|
@@ -361,6 +344,9 @@ def check(filenames: List[str], debug: bool) -> None:
|
|
|
361
344
|
for x in doc.nodes:
|
|
362
345
|
pp.pprint(x)
|
|
363
346
|
|
|
347
|
+
except DatafileSyntaxError as e:
|
|
348
|
+
# TODO(eclbg): add the filename to the error message
|
|
349
|
+
raise CLIException(e)
|
|
364
350
|
except ParseException as e:
|
|
365
351
|
raise CLIException(FeedbackManager.error_exception(error=e))
|
|
366
352
|
|
|
@@ -565,24 +551,17 @@ async def push(
|
|
|
565
551
|
@click.option(
|
|
566
552
|
"--folder", default=None, type=click.Path(exists=True, file_okay=False), help="Folder where files will be placed"
|
|
567
553
|
)
|
|
568
|
-
@click.option(
|
|
569
|
-
"--auto/--no-auto",
|
|
570
|
-
is_flag=True,
|
|
571
|
-
default=True,
|
|
572
|
-
help="Saves datafiles automatically into their default directories (/datasources or /pipes). Default is True",
|
|
573
|
-
)
|
|
574
|
-
@click.option("--match", default=None, help="Retrieve any resourcing matching the pattern. eg --match _test")
|
|
575
554
|
@click.option("-f", "--force", is_flag=True, default=False, help="Override existing files")
|
|
576
555
|
@click.option("--fmt", is_flag=True, default=False, help="Format files before saving")
|
|
577
556
|
@click.pass_context
|
|
578
557
|
@coro
|
|
579
|
-
async def pull(ctx: Context, folder: str,
|
|
558
|
+
async def pull(ctx: Context, folder: str, force: bool, fmt: bool) -> None:
|
|
580
559
|
"""Retrieve latest versions for project files from Tinybird."""
|
|
581
560
|
|
|
582
561
|
client = ctx.ensure_object(dict)["client"]
|
|
583
562
|
folder = folder if folder else getcwd()
|
|
584
563
|
|
|
585
|
-
return await folder_pull(client, folder,
|
|
564
|
+
return await folder_pull(client, folder, force, fmt=fmt)
|
|
586
565
|
|
|
587
566
|
|
|
588
567
|
@cli.command()
|
|
@@ -16,7 +16,6 @@ from tinybird.tb.modules.config import CLIConfig
|
|
|
16
16
|
from tinybird.tb.modules.datafile.fixture import build_fixture_name, persist_fixture
|
|
17
17
|
from tinybird.tb.modules.exceptions import CLIException
|
|
18
18
|
from tinybird.tb.modules.llm import LLM
|
|
19
|
-
from tinybird.tb.modules.local import get_tinybird_local_client
|
|
20
19
|
|
|
21
20
|
|
|
22
21
|
@cli.command()
|
|
@@ -51,7 +50,8 @@ async def create(
|
|
|
51
50
|
"""Initialize a new project."""
|
|
52
51
|
folder = folder or getcwd()
|
|
53
52
|
try:
|
|
54
|
-
|
|
53
|
+
config = CLIConfig.get_project_config()
|
|
54
|
+
tb_client = config.get_client()
|
|
55
55
|
click.echo(FeedbackManager.gray(message="Creating new project structure..."))
|
|
56
56
|
await project_create(tb_client, data, prompt, folder)
|
|
57
57
|
click.echo(FeedbackManager.success(message="✓ Scaffolding completed!\n"))
|
|
@@ -74,13 +74,12 @@ async def create(
|
|
|
74
74
|
datasource_files = [f for f in os.listdir(Path(folder) / "datasources") if f.endswith(".datasource")]
|
|
75
75
|
for datasource_file in datasource_files:
|
|
76
76
|
datasource_path = Path(folder) / "datasources" / datasource_file
|
|
77
|
-
|
|
78
|
-
llm = LLM(key=llm_config["api_key"])
|
|
77
|
+
llm = LLM(client=tb_client)
|
|
79
78
|
datasource_name = datasource_path.stem
|
|
80
79
|
datasource_content = datasource_path.read_text()
|
|
81
80
|
has_json_path = "`json:" in datasource_content
|
|
82
81
|
if has_json_path:
|
|
83
|
-
sql = await llm.generate_sql_sample_data(
|
|
82
|
+
sql = await llm.generate_sql_sample_data(schema=datasource_content, rows=rows)
|
|
84
83
|
result = await tb_client.query(f"{sql} FORMAT JSON")
|
|
85
84
|
data = result.get("data", [])
|
|
86
85
|
fixture_name = build_fixture_name(datasource_path.absolute(), datasource_name, datasource_content)
|
|
@@ -88,8 +87,6 @@ async def create(
|
|
|
88
87
|
click.echo(FeedbackManager.info(message=f"✓ /fixtures/{datasource_name}"))
|
|
89
88
|
|
|
90
89
|
click.echo(FeedbackManager.success(message="✓ Done!\n"))
|
|
91
|
-
|
|
92
|
-
click.echo(FeedbackManager.success(message="✓ Tinybird local is ready!"))
|
|
93
90
|
except Exception as e:
|
|
94
91
|
click.echo(FeedbackManager.error(message=f"Error: {str(e)}"))
|
|
95
92
|
|
|
@@ -130,8 +127,7 @@ TYPE ENDPOINT
|
|
|
130
127
|
)
|
|
131
128
|
elif prompt:
|
|
132
129
|
try:
|
|
133
|
-
|
|
134
|
-
llm = LLM(key=llm_config["api_key"])
|
|
130
|
+
llm = LLM(client=client)
|
|
135
131
|
result = await llm.create_project(prompt)
|
|
136
132
|
for ds in result.datasources:
|
|
137
133
|
content = ds.content.replace("```", "")
|
|
@@ -54,15 +54,12 @@ async def folder_build(
|
|
|
54
54
|
folder: str = ".",
|
|
55
55
|
ignore_sql_errors: bool = False,
|
|
56
56
|
is_internal: bool = False,
|
|
57
|
-
only_pipes: bool = False,
|
|
58
57
|
is_vendor: bool = False,
|
|
59
58
|
current_ws: Optional[Dict[str, Any]] = None,
|
|
60
59
|
local_ws: Optional[Dict[str, Any]] = None,
|
|
61
60
|
workspaces: Optional[List[Dict[str, Any]]] = None,
|
|
61
|
+
watch: bool = False,
|
|
62
62
|
):
|
|
63
|
-
if only_pipes:
|
|
64
|
-
filenames = [f for f in filenames if f.endswith(".pipe")]
|
|
65
|
-
|
|
66
63
|
config = CLIConfig.get_project_config()
|
|
67
64
|
build = True
|
|
68
65
|
dry_run = False
|
|
@@ -118,7 +115,7 @@ async def folder_build(
|
|
|
118
115
|
|
|
119
116
|
vendor_workspaces = []
|
|
120
117
|
|
|
121
|
-
if vendor_path.exists() and not is_vendor:
|
|
118
|
+
if vendor_path.exists() and not is_vendor and not watch:
|
|
122
119
|
user_workspaces = await user_client.user_workspaces()
|
|
123
120
|
for x in vendor_path.iterdir():
|
|
124
121
|
if x.is_dir() and x.name not in existing_workspaces:
|
|
@@ -147,7 +144,7 @@ async def folder_build(
|
|
|
147
144
|
ws_client.token = vendor_ws["token"]
|
|
148
145
|
shared_ws_path = Path(folder) / "vendor" / vendor_ws["name"]
|
|
149
146
|
|
|
150
|
-
if shared_ws_path.exists() and not is_vendor:
|
|
147
|
+
if shared_ws_path.exists() and not is_vendor and not watch:
|
|
151
148
|
await folder_build(
|
|
152
149
|
ws_client, folder=shared_ws_path.as_posix(), is_vendor=True, current_ws=vendor_ws, local_ws=local_ws
|
|
153
150
|
)
|
|
@@ -440,14 +437,16 @@ async def folder_build(
|
|
|
440
437
|
|
|
441
438
|
for group in groups:
|
|
442
439
|
for name in group:
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
440
|
+
is_vendor = resources_to_run_fork_downstream.get(name, {}).get("filename", "").startswith("vendor/")
|
|
441
|
+
if not is_vendor:
|
|
442
|
+
try:
|
|
443
|
+
await tb_client.datasource_delete(name, force=True)
|
|
444
|
+
except Exception:
|
|
445
|
+
pass
|
|
446
|
+
try:
|
|
447
|
+
await tb_client.pipe_delete(name)
|
|
448
|
+
except Exception:
|
|
449
|
+
pass
|
|
451
450
|
|
|
452
451
|
groups.reverse()
|
|
453
452
|
for group in groups:
|
|
@@ -1036,9 +1035,13 @@ async def build_graph(
|
|
|
1036
1035
|
raise click.ClickException(str(e))
|
|
1037
1036
|
|
|
1038
1037
|
for r in res:
|
|
1039
|
-
|
|
1038
|
+
resource_name = r["resource_name"]
|
|
1040
1039
|
warnings = r.get("warnings", [])
|
|
1041
|
-
if
|
|
1040
|
+
if (
|
|
1041
|
+
changed
|
|
1042
|
+
and resource_name in changed
|
|
1043
|
+
and (not changed[resource_name] or changed[resource_name] in ["shared", "remote"])
|
|
1044
|
+
):
|
|
1042
1045
|
continue
|
|
1043
1046
|
|
|
1044
1047
|
if (
|
|
@@ -1046,9 +1049,9 @@ async def build_graph(
|
|
|
1046
1049
|
and r.get("resource", "") == "pipes"
|
|
1047
1050
|
and any(["engine" in x.get("params", {}) for x in r.get("nodes", [])])
|
|
1048
1051
|
):
|
|
1049
|
-
raise click.ClickException(FeedbackManager.error_forkdownstream_pipes_with_engine(pipe=
|
|
1052
|
+
raise click.ClickException(FeedbackManager.error_forkdownstream_pipes_with_engine(pipe=resource_name))
|
|
1050
1053
|
|
|
1051
|
-
to_run[
|
|
1054
|
+
to_run[resource_name] = r
|
|
1052
1055
|
file_deps = r.get("deps", [])
|
|
1053
1056
|
deps += file_deps
|
|
1054
1057
|
# calculate and look for deps
|
|
@@ -1064,7 +1067,7 @@ async def build_graph(
|
|
|
1064
1067
|
to_run[ds_fn] = deepcopy(r)
|
|
1065
1068
|
try:
|
|
1066
1069
|
to_run[ds_fn]["deps"] = list(
|
|
1067
|
-
set(to_run[ds_fn].get("deps", []) + prev.get("deps", []) + [
|
|
1070
|
+
set(to_run[ds_fn].get("deps", []) + prev.get("deps", []) + [resource_name])
|
|
1068
1071
|
)
|
|
1069
1072
|
except ValueError:
|
|
1070
1073
|
pass
|
|
@@ -1085,7 +1088,7 @@ async def build_graph(
|
|
|
1085
1088
|
)
|
|
1086
1089
|
r["shared_with"] = mapped_workspaces
|
|
1087
1090
|
|
|
1088
|
-
dep_map[
|
|
1091
|
+
dep_map[resource_name] = set(dep_list)
|
|
1089
1092
|
return os.path.basename(name), warnings
|
|
1090
1093
|
|
|
1091
1094
|
processed = set()
|
|
@@ -1169,7 +1172,10 @@ async def process_file(
|
|
|
1169
1172
|
workspace_map: Optional[Dict] = None,
|
|
1170
1173
|
workspace_lib_paths: Optional[List[Tuple[str, str]]] = None,
|
|
1171
1174
|
current_ws: Optional[Dict[str, Any]] = None,
|
|
1172
|
-
):
|
|
1175
|
+
) -> List[Dict[str, Any]]:
|
|
1176
|
+
"""Returns a list of resources
|
|
1177
|
+
|
|
1178
|
+
For both datasources and pipes, a list of just one item is returned"""
|
|
1173
1179
|
if workspace_map is None:
|
|
1174
1180
|
workspace_map = {}
|
|
1175
1181
|
|
|
@@ -1349,6 +1355,8 @@ async def process_file(
|
|
|
1349
1355
|
indexes = None
|
|
1350
1356
|
if indexes_list:
|
|
1351
1357
|
indexes = "\n".join([index.to_sql() for index in indexes_list])
|
|
1358
|
+
# Here is where we lose the columns
|
|
1359
|
+
# I don't know why we don't return something more similar to the parsed doc
|
|
1352
1360
|
params = {
|
|
1353
1361
|
"name": append_version_to_name(name, version),
|
|
1354
1362
|
"description": description,
|
|
@@ -264,6 +264,10 @@ async def new_pipe(
|
|
|
264
264
|
except Exception as e:
|
|
265
265
|
raise click.ClickException(FeedbackManager.error_creating_pipe(error=e))
|
|
266
266
|
|
|
267
|
+
if data.get("type") == "endpoint":
|
|
268
|
+
token = tb_client.token
|
|
269
|
+
print(f"""** => Test endpoint with:\n** $ curl {host}/v0/pipes/{p["name"]}.json?token={token}""") # noqa: T201
|
|
270
|
+
|
|
267
271
|
|
|
268
272
|
async def get_token_from_main_branch(branch_tb_client: TinyB) -> Optional[str]:
|
|
269
273
|
token_from_main_branch = None
|