tinybird 0.0.1.dev17__py3-none-any.whl → 0.0.1.dev19__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 +22 -2
- tinybird/tb/__cli__.py +8 -0
- tinybird/tb/modules/build.py +32 -10
- tinybird/tb/modules/build_shell.py +253 -34
- tinybird/tb/modules/cicd.py +9 -89
- tinybird/tb/modules/cli.py +3 -1
- tinybird/tb/modules/common.py +1 -108
- tinybird/tb/modules/create.py +2 -6
- tinybird/tb/modules/datafile/common.py +224 -247
- tinybird/tb/modules/datafile/parse_datasource.py +8 -0
- tinybird/tb/modules/datafile/parse_pipe.py +10 -1
- tinybird/tb/modules/llm.py +4 -3
- tinybird/tb/modules/local_common.py +1 -1
- tinybird/tb/modules/login.py +17 -10
- tinybird/tb/modules/mock.py +14 -12
- tinybird/tb/modules/test.py +116 -24
- {tinybird-0.0.1.dev17.dist-info → tinybird-0.0.1.dev19.dist-info}/METADATA +2 -1
- {tinybird-0.0.1.dev17.dist-info → tinybird-0.0.1.dev19.dist-info}/RECORD +21 -20
- {tinybird-0.0.1.dev17.dist-info → tinybird-0.0.1.dev19.dist-info}/WHEEL +0 -0
- {tinybird-0.0.1.dev17.dist-info → tinybird-0.0.1.dev19.dist-info}/entry_points.txt +0 -0
- {tinybird-0.0.1.dev17.dist-info → tinybird-0.0.1.dev19.dist-info}/top_level.txt +0 -0
|
@@ -6,6 +6,7 @@ import click
|
|
|
6
6
|
from tinybird.feedback_manager import FeedbackManager
|
|
7
7
|
from tinybird.tb.modules.datafile.common import (
|
|
8
8
|
Datafile,
|
|
9
|
+
DatafileSyntaxError,
|
|
9
10
|
format_filename,
|
|
10
11
|
parse,
|
|
11
12
|
)
|
|
@@ -18,6 +19,7 @@ def parse_datasource(
|
|
|
18
19
|
content: Optional[str] = None,
|
|
19
20
|
skip_eval: bool = False,
|
|
20
21
|
hide_folders: bool = False,
|
|
22
|
+
add_context_to_datafile_syntax_errors: bool = True,
|
|
21
23
|
) -> Datafile:
|
|
22
24
|
basepath = ""
|
|
23
25
|
if not content:
|
|
@@ -30,6 +32,12 @@ def parse_datasource(
|
|
|
30
32
|
filename = format_filename(filename, hide_folders)
|
|
31
33
|
try:
|
|
32
34
|
doc = parse(s, "default", basepath, replace_includes=replace_includes, skip_eval=skip_eval)
|
|
35
|
+
except DatafileSyntaxError as e:
|
|
36
|
+
try:
|
|
37
|
+
if add_context_to_datafile_syntax_errors:
|
|
38
|
+
e.get_context_from_file_contents(s)
|
|
39
|
+
finally:
|
|
40
|
+
raise e
|
|
33
41
|
except ParseException as e:
|
|
34
42
|
raise click.ClickException(
|
|
35
43
|
FeedbackManager.error_parsing_file(filename=filename, lineno=e.lineno, error=e)
|
|
@@ -7,6 +7,7 @@ from tinybird.feedback_manager import FeedbackManager
|
|
|
7
7
|
from tinybird.sql_template import get_template_and_variables, render_sql_template
|
|
8
8
|
from tinybird.tb.modules.datafile.common import (
|
|
9
9
|
Datafile,
|
|
10
|
+
DatafileSyntaxError,
|
|
10
11
|
format_filename,
|
|
11
12
|
parse,
|
|
12
13
|
)
|
|
@@ -20,6 +21,7 @@ def parse_pipe(
|
|
|
20
21
|
content: Optional[str] = None,
|
|
21
22
|
skip_eval: bool = False,
|
|
22
23
|
hide_folders: bool = False,
|
|
24
|
+
add_context_to_datafile_syntax_errors: bool = True,
|
|
23
25
|
) -> Datafile:
|
|
24
26
|
basepath = ""
|
|
25
27
|
if not content:
|
|
@@ -32,7 +34,14 @@ def parse_pipe(
|
|
|
32
34
|
filename = format_filename(filename, hide_folders)
|
|
33
35
|
try:
|
|
34
36
|
sql = ""
|
|
35
|
-
|
|
37
|
+
try:
|
|
38
|
+
doc = parse(s, basepath=basepath, replace_includes=replace_includes, skip_eval=skip_eval)
|
|
39
|
+
except DatafileSyntaxError as e:
|
|
40
|
+
try:
|
|
41
|
+
if add_context_to_datafile_syntax_errors:
|
|
42
|
+
e.get_context_from_file_contents(s)
|
|
43
|
+
finally:
|
|
44
|
+
raise e
|
|
36
45
|
for node in doc.nodes:
|
|
37
46
|
sql = node.get("sql", "")
|
|
38
47
|
if sql.strip()[0] == "%":
|
tinybird/tb/modules/llm.py
CHANGED
|
@@ -26,7 +26,6 @@ class TestExpectation(BaseModel):
|
|
|
26
26
|
name: str
|
|
27
27
|
description: str
|
|
28
28
|
parameters: str
|
|
29
|
-
expected_result: str
|
|
30
29
|
|
|
31
30
|
|
|
32
31
|
class TestExpectations(BaseModel):
|
|
@@ -69,11 +68,11 @@ class LLM:
|
|
|
69
68
|
except Exception:
|
|
70
69
|
return DataProject(datasources=[], pipes=[])
|
|
71
70
|
|
|
72
|
-
async def generate_sql_sample_data(self, schema: str, rows: int = 20,
|
|
71
|
+
async def generate_sql_sample_data(self, schema: str, rows: int = 20, prompt: str = "") -> str:
|
|
73
72
|
response = await self.user_client._req(
|
|
74
73
|
"/v0/llm/mock",
|
|
75
74
|
method="POST",
|
|
76
|
-
data=f'{{"schema": "{urllib.parse.quote(schema)}", "rows": {rows}, "context": "{
|
|
75
|
+
data=f'{{"schema": "{urllib.parse.quote(schema)}", "rows": {rows}, "context": "{prompt}"}}',
|
|
77
76
|
headers={"Content-Type": "application/json"},
|
|
78
77
|
)
|
|
79
78
|
return response.get("result", "")
|
|
@@ -90,6 +89,8 @@ class LLM:
|
|
|
90
89
|
{"role": "system", "content": create_test_calls_prompt.format(context=context)},
|
|
91
90
|
{"role": "user", "content": f"Pipe content: {pipe_content}\nPipe params: {pipe_params}"},
|
|
92
91
|
],
|
|
92
|
+
temperature=0.2,
|
|
93
|
+
seed=42,
|
|
93
94
|
response_format=TestExpectations,
|
|
94
95
|
)
|
|
95
96
|
return completion.choices[0].message.parsed or TestExpectations(tests=[])
|
|
@@ -9,7 +9,7 @@ from tinybird.tb.modules.config import CLIConfig
|
|
|
9
9
|
from tinybird.tb.modules.exceptions import CLIException
|
|
10
10
|
|
|
11
11
|
# TODO: Use the official Tinybird image once it's available 'tinybirdco/tinybird-local:latest'
|
|
12
|
-
TB_IMAGE_NAME = "
|
|
12
|
+
TB_IMAGE_NAME = "tinybirdco/tinybird-local:latest"
|
|
13
13
|
TB_CONTAINER_NAME = "tinybird-local"
|
|
14
14
|
TB_LOCAL_PORT = int(os.getenv("TB_LOCAL_PORT", 80))
|
|
15
15
|
TB_LOCAL_HOST = f"http://localhost:{TB_LOCAL_PORT}"
|
tinybird/tb/modules/login.py
CHANGED
|
@@ -11,6 +11,7 @@ import requests
|
|
|
11
11
|
|
|
12
12
|
from tinybird.feedback_manager import FeedbackManager
|
|
13
13
|
from tinybird.tb.modules.cli import CLIConfig, cli
|
|
14
|
+
from tinybird.tb.modules.common import coro
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
class AuthHandler(http.server.SimpleHTTPRequestHandler):
|
|
@@ -115,11 +116,11 @@ def start_server(auth_callback):
|
|
|
115
116
|
"--workspace",
|
|
116
117
|
help="Set the workspace to authenticate to. If not set, the default workspace will be used.",
|
|
117
118
|
)
|
|
118
|
-
|
|
119
|
+
@coro
|
|
120
|
+
async def login(host: str, workspace: str):
|
|
119
121
|
"""Authenticate via browser."""
|
|
120
122
|
auth_event = threading.Event()
|
|
121
123
|
auth_code = [None] # Using a list to store the code, as it's mutable
|
|
122
|
-
config = CLIConfig.get_project_config()
|
|
123
124
|
host = host or "https://api.tinybird.co"
|
|
124
125
|
|
|
125
126
|
def auth_callback(code):
|
|
@@ -150,19 +151,25 @@ def login(host: str, workspace: str):
|
|
|
150
151
|
params = {}
|
|
151
152
|
if workspace:
|
|
152
153
|
params["workspace_id"] = workspace
|
|
153
|
-
response = requests.get(
|
|
154
|
-
f"
|
|
154
|
+
response = requests.get( # noqa: ASYNC210
|
|
155
|
+
f"{host}/v0/user/tokens?{urlencode(params)}",
|
|
155
156
|
headers={"Authorization": f"Bearer {auth_code[0]}"},
|
|
156
157
|
)
|
|
158
|
+
|
|
157
159
|
data = response.json()
|
|
158
160
|
cli_config = CLIConfig.get_project_config()
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
cli_config.
|
|
162
|
-
cli_config.
|
|
163
|
-
|
|
164
|
-
|
|
161
|
+
cli_config.set_token(data["workspace_token"])
|
|
162
|
+
cli_config.set_token_for_host(data["workspace_token"], host)
|
|
163
|
+
cli_config.set_user_token(data["user_token"])
|
|
164
|
+
cli_config.set_host(host)
|
|
165
|
+
|
|
166
|
+
response = await cli_config.get_client().workspace_info()
|
|
167
|
+
for k in ("id", "name", "user_email", "user_id", "scope"):
|
|
168
|
+
if k in response:
|
|
169
|
+
cli_config[k] = response[k]
|
|
170
|
+
|
|
165
171
|
cli_config.persist_to_file()
|
|
172
|
+
|
|
166
173
|
click.echo(FeedbackManager.success(message="✓ Authentication successful!"))
|
|
167
174
|
else:
|
|
168
175
|
click.echo(FeedbackManager.error(message="Authentication failed or timed out."))
|
tinybird/tb/modules/mock.py
CHANGED
|
@@ -15,10 +15,10 @@ from tinybird.tb.modules.local_common import get_tinybird_local_client
|
|
|
15
15
|
@cli.command()
|
|
16
16
|
@click.argument("datasource", type=str)
|
|
17
17
|
@click.option("--rows", type=int, default=10, help="Number of events to send")
|
|
18
|
-
@click.option("--
|
|
18
|
+
@click.option("--prompt", type=str, default="", help="Extra context to use for data generation")
|
|
19
19
|
@click.option("--folder", type=str, default=".", help="Folder where datafiles will be placed")
|
|
20
20
|
@coro
|
|
21
|
-
async def mock(datasource: str, rows: int,
|
|
21
|
+
async def mock(datasource: str, rows: int, prompt: str, folder: str) -> None:
|
|
22
22
|
"""Load sample data into a Data Source.
|
|
23
23
|
|
|
24
24
|
Args:
|
|
@@ -35,15 +35,15 @@ async def mock(datasource: str, rows: int, context: str, folder: str) -> None:
|
|
|
35
35
|
datasource_path = Path("datasources", f"{datasource}.datasource")
|
|
36
36
|
datasource_path = Path(folder) / datasource_path
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
if not
|
|
40
|
-
# load the
|
|
41
|
-
if
|
|
42
|
-
click.echo(FeedbackManager.gray(message=f"Using
|
|
43
|
-
|
|
38
|
+
prompt_path = Path(folder) / "fixtures" / f"{datasource_name}.prompt"
|
|
39
|
+
if not prompt:
|
|
40
|
+
# load the prompt from the fixture.prompt file if it exists
|
|
41
|
+
if prompt_path.exists():
|
|
42
|
+
click.echo(FeedbackManager.gray(message=f"Using prompt for {prompt_path}..."))
|
|
43
|
+
prompt = prompt_path.read_text()
|
|
44
44
|
else:
|
|
45
|
-
click.echo(FeedbackManager.gray(message=f"Overriding
|
|
46
|
-
|
|
45
|
+
click.echo(FeedbackManager.gray(message=f"Overriding prompt for {datasource_name}..."))
|
|
46
|
+
prompt_path.write_text(prompt)
|
|
47
47
|
|
|
48
48
|
click.echo(FeedbackManager.gray(message=f"Creating fixture for {datasource_name}..."))
|
|
49
49
|
datasource_content = datasource_path.read_text()
|
|
@@ -52,7 +52,9 @@ async def mock(datasource: str, rows: int, context: str, folder: str) -> None:
|
|
|
52
52
|
user_client.token = config.get_user_token()
|
|
53
53
|
llm = LLM(client=user_client)
|
|
54
54
|
tb_client = await get_tinybird_local_client(os.path.abspath(folder))
|
|
55
|
-
sql = await llm.generate_sql_sample_data(datasource_content, rows=rows,
|
|
55
|
+
sql = await llm.generate_sql_sample_data(datasource_content, rows=rows, prompt=prompt)
|
|
56
|
+
if os.environ.get('TB_DEBUG', '') != '':
|
|
57
|
+
print(sql)
|
|
56
58
|
result = await tb_client.query(f"{sql} FORMAT JSON")
|
|
57
59
|
data = result.get("data", [])[:rows]
|
|
58
60
|
fixture_name = build_fixture_name(datasource_path.absolute(), datasource_name, datasource_content)
|
|
@@ -60,4 +62,4 @@ async def mock(datasource: str, rows: int, context: str, folder: str) -> None:
|
|
|
60
62
|
click.echo(FeedbackManager.success(message="✓ Done!"))
|
|
61
63
|
|
|
62
64
|
except Exception as e:
|
|
63
|
-
raise CLIException(FeedbackManager.error
|
|
65
|
+
raise CLIException(FeedbackManager.error_exception(error=e))
|
tinybird/tb/modules/test.py
CHANGED
|
@@ -20,6 +20,32 @@ from tinybird.tb.modules.exceptions import CLIException
|
|
|
20
20
|
from tinybird.tb.modules.llm import LLM, TestExpectation
|
|
21
21
|
from tinybird.tb.modules.local_common import get_tinybird_local_client
|
|
22
22
|
|
|
23
|
+
yaml.SafeDumper.org_represent_str = yaml.SafeDumper.represent_str
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def repr_str(dumper, data):
|
|
27
|
+
if "\n" in data:
|
|
28
|
+
return dumper.represent_scalar("tag:yaml.org,2002:str", data, style="|")
|
|
29
|
+
return dumper.org_represent_str(data)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
yaml.add_representer(str, repr_str, Dumper=yaml.SafeDumper)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def generate_test_file(pipe_name: str, tests: List[TestExpectation], folder: Optional[str], mode: str = "w"):
|
|
36
|
+
base = Path("tests")
|
|
37
|
+
if folder:
|
|
38
|
+
base = Path(folder) / base
|
|
39
|
+
|
|
40
|
+
base.mkdir(parents=True, exist_ok=True)
|
|
41
|
+
|
|
42
|
+
yaml_str = yaml.safe_dump(tests, sort_keys=False)
|
|
43
|
+
formatted_yaml = yaml_str.replace("- name:", "\n- name:")
|
|
44
|
+
|
|
45
|
+
path = base / f"{pipe_name}.yaml"
|
|
46
|
+
with open(path, mode) as f:
|
|
47
|
+
f.write(formatted_yaml)
|
|
48
|
+
|
|
23
49
|
|
|
24
50
|
@cli.group()
|
|
25
51
|
@click.pass_context
|
|
@@ -46,16 +72,6 @@ async def test_create(ctx: click.Context, pipe: str, prompt: Optional[str], fold
|
|
|
46
72
|
Create a test for an existing endpoint
|
|
47
73
|
"""
|
|
48
74
|
|
|
49
|
-
def generate_test_file(pipe_name: str, tests: List[TestExpectation]):
|
|
50
|
-
base = Path("tests")
|
|
51
|
-
if folder:
|
|
52
|
-
base = Path(folder) / base
|
|
53
|
-
base.mkdir(parents=True, exist_ok=True)
|
|
54
|
-
|
|
55
|
-
path = base / f"{pipe_name}.yaml"
|
|
56
|
-
with open(path, "w") as f:
|
|
57
|
-
yaml.dump(tests, f)
|
|
58
|
-
|
|
59
75
|
try:
|
|
60
76
|
pipe_path = Path(pipe)
|
|
61
77
|
pipe_name = pipe
|
|
@@ -82,52 +98,128 @@ async def test_create(ctx: click.Context, pipe: str, prompt: Optional[str], fold
|
|
|
82
98
|
)
|
|
83
99
|
valid_test_expectations = []
|
|
84
100
|
for test in test_expectations.tests:
|
|
85
|
-
|
|
101
|
+
valid_test = test.model_dump()
|
|
102
|
+
test_params = (
|
|
103
|
+
valid_test["parameters"] if valid_test["parameters"].startswith("?") else f"?{valid_test['parameters']}"
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
response = None
|
|
86
107
|
try:
|
|
87
|
-
response = await client.
|
|
108
|
+
response = await client._req_raw(f"/v0/pipes/{pipe_name}.ndjson{test_params}")
|
|
88
109
|
except Exception:
|
|
89
110
|
continue
|
|
90
111
|
|
|
91
|
-
|
|
92
|
-
|
|
112
|
+
if response.status_code >= 400:
|
|
113
|
+
valid_test["expected_http_status"] = response.status_code
|
|
114
|
+
valid_test["expected_result"] = response.json()["error"]
|
|
115
|
+
else:
|
|
116
|
+
if "expected_http_status" in valid_test:
|
|
117
|
+
del valid_test["expected_http_status"]
|
|
118
|
+
valid_test["expected_result"] = response.text or ""
|
|
119
|
+
|
|
120
|
+
valid_test_expectations.append(valid_test)
|
|
93
121
|
if valid_test_expectations:
|
|
94
|
-
generate_test_file(pipe_name, valid_test_expectations)
|
|
122
|
+
generate_test_file(pipe_name, valid_test_expectations, folder, mode="a")
|
|
95
123
|
click.echo(FeedbackManager.info(message=f"✓ /tests/{pipe_name}.yaml"))
|
|
96
124
|
click.echo(FeedbackManager.success(message="✓ Done!\n"))
|
|
97
125
|
except Exception as e:
|
|
98
126
|
raise CLIException(FeedbackManager.error_exception(error=e))
|
|
99
127
|
|
|
100
128
|
|
|
129
|
+
@test.command(
|
|
130
|
+
name="update",
|
|
131
|
+
help="Update the test expectations for a file or a test.",
|
|
132
|
+
)
|
|
133
|
+
@click.argument("pipe", type=str)
|
|
134
|
+
@click.option(
|
|
135
|
+
"--folder",
|
|
136
|
+
default=".",
|
|
137
|
+
type=click.Path(exists=True, file_okay=False),
|
|
138
|
+
help="Folder where datafiles will be placed",
|
|
139
|
+
)
|
|
140
|
+
@click.pass_context
|
|
141
|
+
@coro
|
|
142
|
+
async def test_update(ctx: click.Context, pipe: str, folder: Optional[str]) -> None:
|
|
143
|
+
client = await get_tinybird_local_client(os.path.abspath(folder))
|
|
144
|
+
|
|
145
|
+
pipe_tests_path = Path(pipe)
|
|
146
|
+
pipe_name = pipe
|
|
147
|
+
if pipe_tests_path.suffix == ".yaml":
|
|
148
|
+
pipe_name = pipe_tests_path.stem
|
|
149
|
+
else:
|
|
150
|
+
pipe_tests_path = Path("tests", f"{pipe}.yaml")
|
|
151
|
+
|
|
152
|
+
click.echo(FeedbackManager.gray(message=f"\nUpdating tests expectations for {pipe_name} endpoint..."))
|
|
153
|
+
pipe_tests_path = Path(folder) / pipe_tests_path
|
|
154
|
+
pipe_tests_content = yaml.safe_load(pipe_tests_path.read_text())
|
|
155
|
+
for test in pipe_tests_content:
|
|
156
|
+
test_params = test["parameters"] if test["parameters"].startswith("?") else f"?{test['parameters']}"
|
|
157
|
+
response = None
|
|
158
|
+
try:
|
|
159
|
+
response = await client._req_raw(f"/v0/pipes/{pipe_name}.ndjson{test_params}")
|
|
160
|
+
except Exception:
|
|
161
|
+
continue
|
|
162
|
+
|
|
163
|
+
if response.status_code >= 400:
|
|
164
|
+
test["expected_http_status"] = response.status_code
|
|
165
|
+
test["expected_result"] = response.json()["error"]
|
|
166
|
+
else:
|
|
167
|
+
if "expected_http_status" in test:
|
|
168
|
+
del test["expected_http_status"]
|
|
169
|
+
|
|
170
|
+
test["expected_result"] = response.text or ""
|
|
171
|
+
|
|
172
|
+
generate_test_file(pipe_name, pipe_tests_content, folder)
|
|
173
|
+
click.echo(FeedbackManager.info(message=f"✓ /tests/{pipe_name}.yaml"))
|
|
174
|
+
click.echo(FeedbackManager.success(message="✓ Done!\n"))
|
|
175
|
+
|
|
176
|
+
|
|
101
177
|
@test.command(
|
|
102
178
|
name="run",
|
|
103
|
-
help="Run the test suite, a file, or a test.
|
|
179
|
+
help="Run the test suite, a file, or a test.",
|
|
104
180
|
)
|
|
105
|
-
@click.argument("
|
|
181
|
+
@click.argument("name", nargs=-1)
|
|
106
182
|
@click.option(
|
|
107
183
|
"--folder",
|
|
108
184
|
default=".",
|
|
109
185
|
type=click.Path(exists=True, file_okay=False),
|
|
110
186
|
help="Folder where tests will be placed",
|
|
111
187
|
)
|
|
112
|
-
@click.pass_context
|
|
113
188
|
@coro
|
|
114
|
-
async def test_run(
|
|
189
|
+
async def test_run(name: Tuple[str, ...], folder: Optional[str]) -> None:
|
|
115
190
|
client = await get_tinybird_local_client(os.path.abspath(folder))
|
|
116
|
-
|
|
191
|
+
paths = [Path(n) for n in name]
|
|
192
|
+
endpoints = [f"./tests/{p.stem}.yaml" for p in paths]
|
|
193
|
+
file_list: Iterable[str] = endpoints if len(endpoints) > 0 else glob.glob("./tests/**/*.y*ml", recursive=True)
|
|
117
194
|
|
|
118
195
|
async def run_test(test_file):
|
|
119
196
|
test_file_path = Path(test_file)
|
|
120
197
|
test_file_content = yaml.safe_load(test_file_path.read_text())
|
|
121
198
|
for test in test_file_content:
|
|
122
199
|
try:
|
|
123
|
-
|
|
124
|
-
|
|
200
|
+
test_params = test["parameters"] if test["parameters"].startswith("?") else f"?{test['parameters']}"
|
|
201
|
+
|
|
202
|
+
response = None
|
|
203
|
+
try:
|
|
204
|
+
response = await client._req_raw(f"/v0/pipes/{test_file_path.stem}.ndjson{test_params}")
|
|
205
|
+
except Exception:
|
|
206
|
+
raise Exception("Expected to not fail but got an error")
|
|
207
|
+
|
|
208
|
+
expected_result = response.text
|
|
209
|
+
if response.status_code >= 400:
|
|
210
|
+
expected_result = response.json()["error"]
|
|
211
|
+
if "expected_http_status" not in test:
|
|
212
|
+
raise Exception("Expected to not fail but got an error")
|
|
213
|
+
if test["expected_http_status"] != response.status_code:
|
|
214
|
+
raise Exception(f"Expected {test['expected_http_status']} but got {response.status_code}")
|
|
215
|
+
|
|
216
|
+
if test["expected_result"] != expected_result:
|
|
125
217
|
diff = difflib.ndiff(
|
|
126
|
-
test["expected_result"].splitlines(keepends=True),
|
|
218
|
+
test["expected_result"].splitlines(keepends=True), expected_result.splitlines(keepends=True)
|
|
127
219
|
)
|
|
128
220
|
printable_diff = "".join(diff)
|
|
129
221
|
raise Exception(
|
|
130
|
-
f"\nExpected: \n{test['expected_result']}\nGot: \n{
|
|
222
|
+
f"\nExpected: \n{test['expected_result']}\nGot: \n{expected_result}\nDiff: \n{printable_diff}"
|
|
131
223
|
)
|
|
132
224
|
click.echo(FeedbackManager.success(message=f"✓ {test_file_path.name} - {test['name']}"))
|
|
133
225
|
except Exception as e:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: tinybird
|
|
3
|
-
Version: 0.0.1.
|
|
3
|
+
Version: 0.0.1.dev19
|
|
4
4
|
Summary: Tinybird Command Line Tool
|
|
5
5
|
Home-page: https://www.tinybird.co/docs/cli/introduction.html
|
|
6
6
|
Author: Tinybird
|
|
@@ -16,6 +16,7 @@ Requires-Dist: croniter (==1.3.8)
|
|
|
16
16
|
Requires-Dist: docker (==7.1.0)
|
|
17
17
|
Requires-Dist: GitPython (~=3.1.32)
|
|
18
18
|
Requires-Dist: humanfriendly (~=8.2)
|
|
19
|
+
Requires-Dist: prompt-toolkit (==3.0.48)
|
|
19
20
|
Requires-Dist: pydantic (~=2.8.0)
|
|
20
21
|
Requires-Dist: pyperclip (==1.8.2)
|
|
21
22
|
Requires-Dist: pyyaml (<6.1,>=6.0)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
tinybird/__cli__.py,sha256=pgYsVLcqL16wtSn6KtKweNZYoYJdEksTgSvQAW7hH64,250
|
|
2
|
-
tinybird/client.py,sha256=
|
|
2
|
+
tinybird/client.py,sha256=iPtpEhKB3alvo0o4kKMePrt1N7VbwtrKvH10OSWFAaA,51498
|
|
3
3
|
tinybird/config.py,sha256=Z-BX9FrjgsLw1YwcCdF0IztLB97Zpc70VVPplO_pDSY,6089
|
|
4
4
|
tinybird/connectors.py,sha256=lkpVSUmSuViEZBa4QjTK7YmPHUop0a5UFoTrSmlVq6k,15244
|
|
5
5
|
tinybird/context.py,sha256=kutUQ0kCwparowI74_YLXx6wtTzGLRouJ6oGHVBPzBo,1291
|
|
@@ -15,31 +15,32 @@ tinybird/syncasync.py,sha256=fAvq0qkRgqXqXMKwbY2iJNYqLT_r6mDsh1MRpGKrdRU,27763
|
|
|
15
15
|
tinybird/tornado_template.py,sha256=o2HguxrL1Evnt8o3IvrsI8Zm6JtRQ3zhLJKf1XyR3SQ,41965
|
|
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=Rkdf_rWmJCmGhDIUL6WoCdXi5BDn2cZHjhBCqPvzpu8,251
|
|
18
19
|
tinybird/tb/cli.py,sha256=onCxcKvTV4RuokC5V3t82OXWAIwgU6pMWs8rpWOUi_o,815
|
|
19
20
|
tinybird/tb/modules/auth.py,sha256=hynZ-Temot8YBsySUWKSFzZlYadtFPxG3o6lCSu1n6E,9018
|
|
20
|
-
tinybird/tb/modules/build.py,sha256=
|
|
21
|
-
tinybird/tb/modules/build_shell.py,sha256=
|
|
22
|
-
tinybird/tb/modules/cicd.py,sha256=
|
|
23
|
-
tinybird/tb/modules/cli.py,sha256=
|
|
24
|
-
tinybird/tb/modules/common.py,sha256=
|
|
21
|
+
tinybird/tb/modules/build.py,sha256=oVHv9kbim6ZhupLtCIxfgeZOCriqfH4Oz9S5Komv_9c,9409
|
|
22
|
+
tinybird/tb/modules/build_shell.py,sha256=3ec18FWyyF5lWgrf8F2n8-3ngQccJUmN2cwB9-_S_1g,14083
|
|
23
|
+
tinybird/tb/modules/cicd.py,sha256=Xa3M7Egq4eDS6QIhrx8C8Ixf-R7oiZpO-5ZBPqYAKtI,5344
|
|
24
|
+
tinybird/tb/modules/cli.py,sha256=hN5_DijZZ-E2OtLe0tgnu4a_5KCzY9EeOjE-bEJefos,53169
|
|
25
|
+
tinybird/tb/modules/common.py,sha256=XfpzFVT9xJZ_HH7EZRLAjRMkTtz5rP7Njld5-EwMqiA,72400
|
|
25
26
|
tinybird/tb/modules/config.py,sha256=ri4Gwyzqol6-NofTjHnWquuDzJOjHbkaAnboO8JNENY,11499
|
|
26
27
|
tinybird/tb/modules/connection.py,sha256=ZSqBGoRiJedjHKEyB_fr1ybucOHtaad8d7uqGa2Q92M,28668
|
|
27
|
-
tinybird/tb/modules/create.py,sha256=
|
|
28
|
+
tinybird/tb/modules/create.py,sha256=jSVh8ysT5OoQ0EUg3ETU3VtKZayqFWBjPjcQSdYa7Sc,9882
|
|
28
29
|
tinybird/tb/modules/datasource.py,sha256=rX7RnrXl4xxPwGxkj1DgLr521wjNXEixAeAyxMrkJJk,35472
|
|
29
30
|
tinybird/tb/modules/exceptions.py,sha256=4A2sSjCEqKUMqpP3WI00zouCWW4uLaghXXLZBSw04mY,3363
|
|
30
31
|
tinybird/tb/modules/fmt.py,sha256=UszEQO15fdzQ49QEj7Unhu68IKwSuKPsOrKhk2p2TAg,3547
|
|
31
32
|
tinybird/tb/modules/job.py,sha256=eoBVyA24lYIPonU88Jn7FF9hBKz1kScy9_w_oWreuc4,2952
|
|
32
|
-
tinybird/tb/modules/llm.py,sha256=
|
|
33
|
+
tinybird/tb/modules/llm.py,sha256=OcyRzQ0V392XsiGFwrrpjkCuV6AdE6cnhh6cDKPQt7U,3102
|
|
33
34
|
tinybird/tb/modules/local.py,sha256=hV2fvHPaVHVzKwVoVDFAIbJZslOX1_COx96DZrR-dW8,5151
|
|
34
|
-
tinybird/tb/modules/local_common.py,sha256=
|
|
35
|
-
tinybird/tb/modules/login.py,sha256=
|
|
36
|
-
tinybird/tb/modules/mock.py,sha256=
|
|
35
|
+
tinybird/tb/modules/local_common.py,sha256=PsQu0Sg8BruusPzlzvs6dd2WlomvOcks-B37A798-Ls,2133
|
|
36
|
+
tinybird/tb/modules/login.py,sha256=ydyn64fbz3BOlSf5SZuIFvgsCpxQAQir8nJQSdMeq94,6011
|
|
37
|
+
tinybird/tb/modules/mock.py,sha256=cpEmfSv5Osy_YF5eaFcAlpAYi0oVlX1NQH4mnAKvlUI,2884
|
|
37
38
|
tinybird/tb/modules/pipe.py,sha256=P_W5HW1-UEidWlw0pty-n_qYvCAyMlNorBmWzmCP7cU,30906
|
|
38
39
|
tinybird/tb/modules/regions.py,sha256=QjsL5H6Kg-qr0aYVLrvb1STeJ5Sx_sjvbOYO0LrEGMk,166
|
|
39
40
|
tinybird/tb/modules/table.py,sha256=hG-PRDVuFp2uph41WpoLRV1yjp3RI2fi_iGGiI0rdxU,7695
|
|
40
41
|
tinybird/tb/modules/tag.py,sha256=1qQWyk1p3Btv3LzM8VbJG-k7x2-pFuAlYCg3QL6QewI,3480
|
|
41
42
|
tinybird/tb/modules/telemetry.py,sha256=iEGnMuCuNhvF6ln__j6X9MSTwL_0Hm-GgFHHHvhfknk,10466
|
|
42
|
-
tinybird/tb/modules/test.py,sha256=
|
|
43
|
+
tinybird/tb/modules/test.py,sha256=Br0gXS9FrpFEnA6mnSm5fRbYnaw10AL_Z0gbaDfvDoM,8857
|
|
43
44
|
tinybird/tb/modules/token.py,sha256=r0oeG1RpOOzHtqbUaHBiOmhE55HfNIvReAAWyKl9fJg,12695
|
|
44
45
|
tinybird/tb/modules/workspace.py,sha256=FVlh-kbiZp5Gvp6dGFxi0UD8ail77rMamXLhqdVwrZ0,10916
|
|
45
46
|
tinybird/tb/modules/workspace_members.py,sha256=08W0onEYkKLEC5TkAI07cxN9XSquEm7HnL7OkHAVDjo,8715
|
|
@@ -47,15 +48,15 @@ tinybird/tb/modules/datafile/build.py,sha256=rFdK_GerPDgPfyPfZ4EZ0-cQqWfHd6htS0l
|
|
|
47
48
|
tinybird/tb/modules/datafile/build_common.py,sha256=74547h5ja4C66DAwDMabj75FA_BUTJxTJv-24tSFmrs,4551
|
|
48
49
|
tinybird/tb/modules/datafile/build_datasource.py,sha256=fquzEGwk9NL_0K5YYG86Xtvgn4J5YHtRUoKJxbQGO0s,17344
|
|
49
50
|
tinybird/tb/modules/datafile/build_pipe.py,sha256=V5u21NEpSCWNVl46Cdn_I_bOojxpgSrg1MnScPDqx4U,27648
|
|
50
|
-
tinybird/tb/modules/datafile/common.py,sha256=
|
|
51
|
+
tinybird/tb/modules/datafile/common.py,sha256=3CwspnffLUOlbfHMzjEvNebX--5sOC8DUKOC__3_fTA,75702
|
|
51
52
|
tinybird/tb/modules/datafile/diff.py,sha256=-iaP7GvAzZtZSa8jPgVpOFlTRutxgxRBLBcGL1_RFr4,6743
|
|
52
53
|
tinybird/tb/modules/datafile/exceptions.py,sha256=8rw2umdZjtby85QbuRKFO5ETz_eRHwUY5l7eHsy1wnI,556
|
|
53
54
|
tinybird/tb/modules/datafile/fixture.py,sha256=YHlL4tojmPwm343Y8KO6r7d5Bhsk7U3lKP-oLMeBMsY,1771
|
|
54
55
|
tinybird/tb/modules/datafile/format_common.py,sha256=zNWDXvwSKC9_T5e9R92LLj9ekDflVWwsllhGQilZsnY,2184
|
|
55
56
|
tinybird/tb/modules/datafile/format_datasource.py,sha256=tsnCjONISvhFuucKNbIHkT__UmlUbcswx5mwI9hiDQc,6216
|
|
56
57
|
tinybird/tb/modules/datafile/format_pipe.py,sha256=R5tnlEccLn3KX6ehtC_H2sGQNrthuJUiVSN9z_-KGCY,7474
|
|
57
|
-
tinybird/tb/modules/datafile/parse_datasource.py,sha256=
|
|
58
|
-
tinybird/tb/modules/datafile/parse_pipe.py,sha256=
|
|
58
|
+
tinybird/tb/modules/datafile/parse_datasource.py,sha256=mAGN72mviRS0rtpAD0kODhlu-N8j1fX2kp5D1GUzP6U,1464
|
|
59
|
+
tinybird/tb/modules/datafile/parse_pipe.py,sha256=9_j-wB4gsWtrXY8Gypt4EkOH6BepJpZkjFdZfbW-H50,2914
|
|
59
60
|
tinybird/tb/modules/datafile/pipe_checker.py,sha256=cp80Bru41GlyMRvyERpdJNXns2MjmtIAWFnBLF4cPXs,24667
|
|
60
61
|
tinybird/tb/modules/datafile/pull.py,sha256=wBXBAZIruIyCRQZvfYxMc7h1q35NlKF-hFIF-bUm4iY,5956
|
|
61
62
|
tinybird/tb/modules/tinyunit/tinyunit.py,sha256=IkjRCvb8HnNEE84rtl0I1b9gQVpE_zCE8MvFFet51sg,11716
|
|
@@ -66,8 +67,8 @@ tinybird/tb_cli_modules/config.py,sha256=6NTgIdwf0X132A1j6G_YrdPep87ymZ9b5pABabK
|
|
|
66
67
|
tinybird/tb_cli_modules/exceptions.py,sha256=pmucP4kTF4irIt7dXiG-FcnI-o3mvDusPmch1L8RCWk,3367
|
|
67
68
|
tinybird/tb_cli_modules/regions.py,sha256=QjsL5H6Kg-qr0aYVLrvb1STeJ5Sx_sjvbOYO0LrEGMk,166
|
|
68
69
|
tinybird/tb_cli_modules/telemetry.py,sha256=iEGnMuCuNhvF6ln__j6X9MSTwL_0Hm-GgFHHHvhfknk,10466
|
|
69
|
-
tinybird-0.0.1.
|
|
70
|
-
tinybird-0.0.1.
|
|
71
|
-
tinybird-0.0.1.
|
|
72
|
-
tinybird-0.0.1.
|
|
73
|
-
tinybird-0.0.1.
|
|
70
|
+
tinybird-0.0.1.dev19.dist-info/METADATA,sha256=AxNU0bAKXE2-n882WlwLmg8drAgVN4uLrnhCqGHhqxY,2446
|
|
71
|
+
tinybird-0.0.1.dev19.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
|
|
72
|
+
tinybird-0.0.1.dev19.dist-info/entry_points.txt,sha256=LwdHU6TfKx4Qs7BqqtaczEZbImgU7Abe9Lp920zb_fo,43
|
|
73
|
+
tinybird-0.0.1.dev19.dist-info/top_level.txt,sha256=pgw6AzERHBcW3YTi2PW4arjxLkulk2msOz_SomfOEuc,45
|
|
74
|
+
tinybird-0.0.1.dev19.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|