tinybird 0.0.1.dev14__py3-none-any.whl → 0.0.1.dev16__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.

@@ -36,6 +36,7 @@ def parse_datasource(
36
36
  ) from None
37
37
 
38
38
  if len(doc.nodes) > 1:
39
+ # TODO(eclbg): Turn this into a custom exception with a better message
39
40
  raise ValueError(f"{filename}: datasources can't have more than one node")
40
41
 
41
42
  return doc
@@ -1,12 +1,15 @@
1
1
  import asyncio
2
- from datetime import datetime
3
- from typing import Awaitable, Callable, List
2
+ import json
3
+ import urllib.parse
4
+ from copy import deepcopy
5
+ from typing import Awaitable, Callable, List, Optional
4
6
 
5
7
  from openai import OpenAI
6
8
  from pydantic import BaseModel
7
9
 
8
10
  from tinybird.client import TinyB
9
- from tinybird.tb.modules.prompts import create_project_prompt, sample_data_sql_prompt
11
+ from tinybird.prompts import create_test_calls_prompt
12
+ from tinybird.tb.modules.config import CLIConfig
10
13
 
11
14
 
12
15
  class DataFile(BaseModel):
@@ -19,9 +22,25 @@ class DataProject(BaseModel):
19
22
  pipes: List[DataFile]
20
23
 
21
24
 
25
+ class TestExpectation(BaseModel):
26
+ name: str
27
+ description: str
28
+ parameters: str
29
+ expected_result: str
30
+
31
+
32
+ class TestExpectations(BaseModel):
33
+ tests: List[TestExpectation]
34
+
35
+
22
36
  class LLM:
23
- def __init__(self, key: str):
24
- self.client = OpenAI(api_key=key)
37
+ def __init__(self, client: TinyB, api_key: Optional[str] = None):
38
+ self.client = client
39
+ user_token = CLIConfig.get_project_config().get_user_token()
40
+ user_client = deepcopy(client)
41
+ user_client.token = user_token
42
+ self.user_client = user_client
43
+ self.openai = OpenAI(api_key=api_key) if api_key else None
25
44
 
26
45
  async def _execute(self, action_fn: Callable[[], Awaitable[str]], checker_fn: Callable[[str], bool]):
27
46
  is_valid = False
@@ -38,36 +57,39 @@ class LLM:
38
57
  return result
39
58
 
40
59
  async def create_project(self, prompt: str) -> DataProject:
41
- completion = self.client.beta.chat.completions.parse(
42
- model="gpt-4o",
43
- messages=[{"role": "system", "content": create_project_prompt}, {"role": "user", "content": prompt}],
44
- response_format=DataProject,
45
- )
46
- return completion.choices[0].message.parsed or DataProject(datasources=[], pipes=[])
47
-
48
- async def generate_sql_sample_data(
49
- self, tb_client: TinyB, datasource_content: str, row_count: int = 20, context: str = ""
50
- ) -> str:
51
- async def action_fn():
52
- response = self.client.chat.completions.create(
53
- model="gpt-4o-mini",
54
- messages=[
55
- {
56
- "role": "system",
57
- "content": sample_data_sql_prompt.format(
58
- current_datetime=datetime.now().isoformat(), row_count=row_count, context=context
59
- ),
60
- },
61
- {"role": "user", "content": datasource_content},
62
- ],
60
+ try:
61
+ response = await self.user_client._req(
62
+ "/v0/llm/create",
63
+ method="POST",
64
+ data=f'{{"prompt": {json.dumps(prompt)}}}',
65
+ headers={"Content-Type": "application/json"},
63
66
  )
64
- return response.choices[0].message.content or ""
65
67
 
66
- async def checker_fn(sql: str):
67
- try:
68
- result = await tb_client.query(f"DESCRIBE ({sql}) FORMAT JSON")
69
- return len(result.get("data", [])) > 0
70
- except Exception:
71
- return False
68
+ return DataProject.model_validate(response.get("result", {}))
69
+ except Exception:
70
+ return DataProject(datasources=[], pipes=[])
71
+
72
+ async def generate_sql_sample_data(self, schema: str, rows: int = 20, context: str = "") -> str:
73
+ response = await self.user_client._req(
74
+ "/v0/llm/mock",
75
+ method="POST",
76
+ data=f'{{"schema": "{urllib.parse.quote(schema)}", "rows": {rows}, "context": "{urllib.parse.quote(context)}"}}',
77
+ headers={"Content-Type": "application/json"},
78
+ )
79
+ return response.get("result", "")
80
+
81
+ async def create_test_commands(
82
+ self, pipe_content: str, pipe_params: set[str], context: str = ""
83
+ ) -> TestExpectations:
84
+ if not self.openai:
85
+ raise ValueError("OpenAI API key is not set")
72
86
 
73
- return await self._execute(action_fn, checker_fn)
87
+ completion = self.openai.beta.chat.completions.parse(
88
+ model="gpt-4o",
89
+ messages=[
90
+ {"role": "system", "content": create_test_calls_prompt.format(context=context)},
91
+ {"role": "user", "content": f"Pipe content: {pipe_content}\nPipe params: {pipe_params}"},
92
+ ],
93
+ response_format=TestExpectations,
94
+ )
95
+ return completion.choices[0].message.parsed or TestExpectations(tests=[])
@@ -1,25 +1,14 @@
1
- import hashlib
2
1
  import os
3
2
  import time
4
- from typing import Optional
5
3
 
6
4
  import click
7
- import requests
8
5
 
9
6
  import docker
10
7
  from tinybird.feedback_manager import FeedbackManager
11
8
  from tinybird.tb.modules.cli import cli
12
- from tinybird.tb.modules.common import (
13
- coro,
14
- )
15
- from tinybird.tb.modules.config import CLIConfig
9
+ from tinybird.tb.modules.common import coro
16
10
  from tinybird.tb.modules.exceptions import CLIException
17
-
18
- # TODO: Use the official Tinybird image once it's available 'tinybirdco/tinybird-local:latest'
19
- TB_IMAGE_NAME = "registry.gitlab.com/tinybird/analytics/tinybird-local-jammy-3.11:latest"
20
- TB_CONTAINER_NAME = "tinybird-local"
21
- TB_LOCAL_PORT = int(os.getenv("TB_LOCAL_PORT", 80))
22
- TB_LOCAL_HOST = f"http://localhost:{TB_LOCAL_PORT}"
11
+ from tinybird.tb.modules.local_common import TB_CONTAINER_NAME, TB_IMAGE_NAME, TB_LOCAL_PORT
23
12
 
24
13
 
25
14
  def start_tinybird_local(
@@ -113,40 +102,6 @@ def remove_tinybird_local(docker_client):
113
102
  pass
114
103
 
115
104
 
116
- async def get_tinybird_local_client(path: Optional[str] = None):
117
- """Get a Tinybird client connected to the local environment."""
118
- config = CLIConfig.get_project_config()
119
- try:
120
- # ruff: noqa: ASYNC210
121
- tokens = requests.get(f"{TB_LOCAL_HOST}/tokens").json()
122
- except Exception:
123
- raise CLIException("Tinybird local is not running. Please run `tb local start` first.")
124
-
125
- user_token = tokens["user_token"]
126
- token = tokens["workspace_admin_token"]
127
- # Create a new workspace if path is provided. This is used to isolate the build in a different workspace.
128
- if path:
129
- folder_hash = hashlib.sha256(path.encode()).hexdigest()
130
- user_client = config.get_client(host=TB_LOCAL_HOST, token=user_token)
131
-
132
- ws_name = f"Tinybird_Local_Build_{folder_hash}"
133
-
134
- user_workspaces = await user_client.user_workspaces()
135
- ws = next((ws for ws in user_workspaces["workspaces"] if ws["name"] == ws_name), None)
136
- if not ws:
137
- await user_client.create_workspace(ws_name, template=None)
138
- user_workspaces = await user_client.user_workspaces()
139
- ws = next((ws for ws in user_workspaces["workspaces"] if ws["name"] == ws_name), None)
140
-
141
- token = ws["token"]
142
-
143
- config.set_token(token)
144
- config.set_host(TB_LOCAL_HOST)
145
- config.set_user_token(user_token)
146
- config.persist_to_file()
147
- return config.get_client(host=TB_LOCAL_HOST, token=token)
148
-
149
-
150
105
  @cli.command()
151
106
  def upgrade():
152
107
  """Upgrade Tinybird CLI to the latest version"""
@@ -0,0 +1,54 @@
1
+ import hashlib
2
+ import os
3
+ from typing import Optional
4
+
5
+ import requests
6
+
7
+ from tinybird.client import TinyB
8
+ from tinybird.tb.modules.config import CLIConfig
9
+ from tinybird.tb.modules.exceptions import CLIException
10
+
11
+ # TODO: Use the official Tinybird image once it's available 'tinybirdco/tinybird-local:latest'
12
+ TB_IMAGE_NAME = "registry.gitlab.com/tinybird/analytics/tinybird-local-jammy-3.11:latest"
13
+ TB_CONTAINER_NAME = "tinybird-local"
14
+ TB_LOCAL_PORT = int(os.getenv("TB_LOCAL_PORT", 80))
15
+ TB_LOCAL_HOST = f"http://localhost:{TB_LOCAL_PORT}"
16
+
17
+
18
+ async def get_tinybird_local_client(path: Optional[str] = None) -> TinyB:
19
+ """Get a Tinybird client connected to the local environment."""
20
+ config = CLIConfig.get_project_config()
21
+ try:
22
+ # ruff: noqa: ASYNC210
23
+ tokens = requests.get(f"{TB_LOCAL_HOST}/tokens").json()
24
+ except Exception:
25
+ raise CLIException("Tinybird local is not running. Please run `tb local start` first.")
26
+
27
+ user_token = tokens["user_token"]
28
+ default_token = tokens["workspace_admin_token"]
29
+ # Create a new workspace if path is provided. This is used to isolate the build in a different workspace.
30
+ path = path or os.getcwd()
31
+ if path:
32
+ folder_hash = hashlib.sha256(path.encode()).hexdigest()
33
+ user_client = config.get_client(host=TB_LOCAL_HOST, token=user_token)
34
+
35
+ ws_name = f"Tinybird_Local_Build_{folder_hash}"
36
+
37
+ user_workspaces = await user_client.user_workspaces()
38
+ ws = next((ws for ws in user_workspaces["workspaces"] if ws["name"] == ws_name), None)
39
+ if not ws:
40
+ await user_client.create_workspace(ws_name, template=None)
41
+ user_workspaces = await user_client.user_workspaces()
42
+ ws = next((ws for ws in user_workspaces["workspaces"] if ws["name"] == ws_name), None)
43
+
44
+ ws_token = ws["token"]
45
+
46
+ config.set_token(ws_token)
47
+ config.set_token_for_host(TB_LOCAL_HOST, ws_token)
48
+ config.set_host(TB_LOCAL_HOST)
49
+ else:
50
+ config.set_token(default_token)
51
+ config.set_token_for_host(TB_LOCAL_HOST, default_token)
52
+
53
+ config.set_user_token(user_token)
54
+ return config.get_client(host=TB_LOCAL_HOST)
@@ -162,7 +162,6 @@ def login(host: str, workspace: str):
162
162
  cli_config.set_token_for_host(workspace_token, host)
163
163
  cli_config.set_user_token(user_token)
164
164
  config.set_host(host)
165
-
166
165
  cli_config.persist_to_file()
167
166
  click.echo(FeedbackManager.success(message="✓ Authentication successful!"))
168
167
  else:
@@ -9,7 +9,7 @@ from tinybird.tb.modules.common import CLIException, coro
9
9
  from tinybird.tb.modules.config import CLIConfig
10
10
  from tinybird.tb.modules.datafile.fixture import build_fixture_name, persist_fixture
11
11
  from tinybird.tb.modules.llm import LLM
12
- from tinybird.tb.modules.local import get_tinybird_local_client
12
+ from tinybird.tb.modules.local_common import get_tinybird_local_client
13
13
 
14
14
 
15
15
  @cli.command()
@@ -45,13 +45,14 @@ async def mock(datasource: str, rows: int, context: str, folder: str) -> None:
45
45
  click.echo(FeedbackManager.gray(message=f"Overriding context for {datasource_name}..."))
46
46
  context_path.write_text(context)
47
47
 
48
-
49
48
  click.echo(FeedbackManager.gray(message=f"Creating fixture for {datasource_name}..."))
50
49
  datasource_content = datasource_path.read_text()
51
- llm_config = CLIConfig.get_llm_config()
52
- llm = LLM(key=llm_config["api_key"])
50
+ config = CLIConfig.get_project_config()
51
+ user_client = config.get_client()
52
+ user_client.token = config.get_user_token()
53
+ llm = LLM(client=user_client)
53
54
  tb_client = await get_tinybird_local_client(os.path.abspath(folder))
54
- sql = await llm.generate_sql_sample_data(tb_client, datasource_content, row_count=rows, context=context)
55
+ sql = await llm.generate_sql_sample_data(datasource_content, rows=rows, context=context)
55
56
  result = await tb_client.query(f"{sql} FORMAT JSON")
56
57
  data = result.get("data", [])[:rows]
57
58
  fixture_name = build_fixture_name(datasource_path.absolute(), datasource_name, datasource_content)
@@ -3,24 +3,22 @@
3
3
  # - If it makes sense and only when strictly necessary, you can create utility functions in this file.
4
4
  # - But please, **do not** interleave utility functions and command definitions.
5
5
 
6
+ import difflib
6
7
  import glob
7
- from typing import Any, Dict, Iterable, List, Tuple
8
+ import os
9
+ from pathlib import Path
10
+ from typing import Iterable, List, Optional, Tuple
8
11
 
9
12
  import click
13
+ import yaml
10
14
 
11
- from tinybird.client import AuthNoTokenException
12
15
  from tinybird.feedback_manager import FeedbackManager
13
16
  from tinybird.tb.modules.cli import cli
14
- from tinybird.tb.modules.common import coro, create_tb_client, gather_with_concurrency
17
+ from tinybird.tb.modules.common import coro
15
18
  from tinybird.tb.modules.config import CLIConfig
16
19
  from tinybird.tb.modules.exceptions import CLIException
17
- from tinybird.tb.modules.tinyunit.tinyunit import (
18
- TestSummaryResults,
19
- generate_file,
20
- parse_file,
21
- run_test_file,
22
- test_run_summary,
23
- )
20
+ from tinybird.tb.modules.llm import LLM, TestExpectation
21
+ from tinybird.tb.modules.local_common import get_tinybird_local_client
24
22
 
25
23
 
26
24
  @cli.group()
@@ -30,78 +28,111 @@ def test(ctx: click.Context) -> None:
30
28
 
31
29
 
32
30
  @test.command(
33
- name="run",
34
- help="Run the test suite, a file, or a test. To skip test to run in branches and CI put them in a 'skip_in_branch' folder.",
31
+ name="create",
32
+ help="Create a test for an existing endpoint",
35
33
  )
36
- @click.argument("file", nargs=-1)
37
- @click.option("-v", "--verbose", is_flag=True, default=False, help="Enable verbose (show results)", type=bool)
38
- @click.option("--fail", "only_fail", is_flag=True, default=False, help="Showy onl failed/error tests", type=bool)
39
- @click.option("-c", "--concurrency", help="How many test to run concurrently", default=1, type=click.IntRange(1, 10))
34
+ @click.argument("pipe", type=str)
35
+ @click.option(
36
+ "--folder",
37
+ default=".",
38
+ type=click.Path(exists=True, file_okay=False),
39
+ help="Folder where datafiles will be placed",
40
+ )
41
+ @click.option("--prompt", type=str, default=None, help="Prompt to be used to create the test")
40
42
  @click.pass_context
41
43
  @coro
42
- async def test_run(ctx: click.Context, file: Tuple[str, ...], verbose: bool, only_fail: bool, concurrency: int) -> None:
43
- results: List[TestSummaryResults] = []
44
+ async def test_create(ctx: click.Context, pipe: str, prompt: Optional[str], folder: Optional[str]) -> None:
45
+ """
46
+ Create a test for an existing endpoint
47
+ """
48
+
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)
44
58
 
45
59
  try:
46
- tb_client = create_tb_client(ctx)
47
- config = CLIConfig.get_project_config()
48
- if config.get("token") is None:
49
- raise AuthNoTokenException
50
- workspaces: List[Dict[str, Any]] = (await tb_client.user_workspaces_and_branches()).get("workspaces", [])
51
- current_ws: Dict[str, Any] = next(
52
- (workspace for workspace in workspaces if config and workspace.get("id", ".") == config.get("id", "..")), {}
60
+ pipe_path = Path(pipe)
61
+ pipe_name = pipe
62
+ if pipe_path.suffix == ".pipe":
63
+ pipe_name = pipe_path.stem
64
+ else:
65
+ pipe_path = Path("endpoints", f"{pipe}.pipe")
66
+ if not pipe_path.exists():
67
+ pipe_path = Path("pipes", f"{pipe}.pipe")
68
+
69
+ click.echo(FeedbackManager.gray(message=f"\nCreating tests for {pipe_name} endpoint..."))
70
+ pipe_path = Path(folder) / pipe_path
71
+ pipe_content = pipe_path.read_text()
72
+
73
+ client = await get_tinybird_local_client(os.path.abspath(folder))
74
+ pipe_nodes = await client._req(f"/v0/pipes/{pipe_name}")
75
+ pipe_params = set([param["name"] for node in pipe_nodes["nodes"] for param in node["params"]])
76
+
77
+ llm_config = CLIConfig.get_llm_config()
78
+ llm = LLM(client=client, api_key=llm_config["api_key"])
79
+
80
+ test_expectations = await llm.create_test_commands(
81
+ pipe_content=pipe_content, pipe_params=pipe_params, context=prompt
53
82
  )
83
+ valid_test_expectations = []
84
+ for test in test_expectations.tests:
85
+ test_params = test.parameters if test.parameters.startswith("?") else f"?{test.parameters}"
86
+ try:
87
+ response = await client._req(f"/v0/pipes/{pipe_name}.ndjson{test_params}")
88
+ except Exception:
89
+ continue
90
+
91
+ test.expected_result = response
92
+ valid_test_expectations.append(test.model_dump())
93
+ if valid_test_expectations:
94
+ generate_test_file(pipe_name, valid_test_expectations)
95
+ click.echo(FeedbackManager.info(message=f"✓ /tests/{pipe_name}.yaml"))
96
+ click.echo(FeedbackManager.success(message="✓ Done!\n"))
54
97
  except Exception as e:
55
98
  raise CLIException(FeedbackManager.error_exception(error=e))
56
99
 
57
- file_list: Iterable[str] = file if len(file) > 0 else glob.glob("./tests/**/*.y*ml", recursive=True)
58
- click.echo(FeedbackManager.info_skipping_resource(resource="regression.yaml"))
59
- file_list = [f for f in file_list if not f.endswith("regression.yaml")]
60
- final_file_list = []
61
- for f in file_list:
62
- if "skip_in_branch" in f and current_ws and current_ws.get("is_branch"):
63
- click.echo(FeedbackManager.info_skipping_resource(resource=f))
64
- else:
65
- final_file_list.append(f)
66
- file_list = final_file_list
67
-
68
- async def run_test(tb_client, test_file, results):
69
- try:
70
- test_result = await run_test_file(tb_client, test_file)
71
- results.append(TestSummaryResults(filename=test_file, results=test_result, semver=tb_client.semver))
72
- except Exception as e:
73
- if verbose:
74
- click.echo(FeedbackManager.error_exception(error=e))
75
- raise CLIException(FeedbackManager.error_running_test(file=test_file))
76
-
77
- test_tasks = [run_test(tb_client, test_file, results) for test_file in file_list]
78
- await gather_with_concurrency(concurrency, *test_tasks)
79
-
80
- if len(results) <= 0:
81
- click.echo(FeedbackManager.warning_no_test_results())
82
- else:
83
- test_run_summary(results, only_fail=only_fail, verbose_level=int(verbose))
84
-
85
-
86
- @test.command(name="init", help="Initialize a file list with a simple test suite.")
87
- @click.argument("files", nargs=-1)
88
- @click.option("--force", is_flag=True, default=False, help="Override existing files")
89
- @click.pass_context
90
- @coro
91
- async def test_init(ctx: click.Context, files: Tuple[str, ...], force: bool) -> None:
92
- if len(files) == 0:
93
- files = ("tests/default.yaml",)
94
-
95
- for file in files:
96
- generate_file(file, overwrite=force)
97
100
 
98
-
99
- @test.command(name="parse", help="Read the contents of a test file list.")
100
- @click.argument("files", nargs=-1)
101
+ @test.command(
102
+ name="run",
103
+ help="Run the test suite, a file, or a test. To skip test to run in branches and CI put them in a 'skip_in_branch' folder.",
104
+ )
105
+ @click.argument("file", nargs=-1)
106
+ @click.option(
107
+ "--folder",
108
+ default=".",
109
+ type=click.Path(exists=True, file_okay=False),
110
+ help="Folder where tests will be placed",
111
+ )
101
112
  @click.pass_context
102
113
  @coro
103
- async def test_parse(ctx: click.Context, files: Tuple[str, ...]) -> None:
104
- for f in files:
105
- click.echo(f"\nFile: {f}")
106
- for test in parse_file(f):
107
- click.echo(test)
114
+ async def test_run(ctx: click.Context, file: Tuple[str, ...], folder: Optional[str]) -> None:
115
+ client = await get_tinybird_local_client(os.path.abspath(folder))
116
+ file_list: Iterable[str] = file if len(file) > 0 else glob.glob("./tests/**/*.y*ml", recursive=True)
117
+
118
+ async def run_test(test_file):
119
+ test_file_path = Path(test_file)
120
+ test_file_content = yaml.safe_load(test_file_path.read_text())
121
+ for test in test_file_content:
122
+ try:
123
+ response = await client._req(f"/v0/pipes/{test_file_path.stem}.ndjson?{test['parameters']}")
124
+ if test["expected_result"] != response:
125
+ diff = difflib.ndiff(
126
+ test["expected_result"].splitlines(keepends=True), response.splitlines(keepends=True)
127
+ )
128
+ printable_diff = "".join(diff)
129
+ raise Exception(
130
+ f"\nExpected: \n{test['expected_result']}\nGot: \n{response}\nDiff: \n{printable_diff}"
131
+ )
132
+ click.echo(FeedbackManager.success(message=f"✓ {test_file_path.name} - {test['name']}"))
133
+ except Exception as e:
134
+ click.echo(FeedbackManager.error(message=f"✗ {test_file_path.name} - {test['name']}"))
135
+ click.echo(FeedbackManager.error(message=f"Output and expected output are different: \n{e}"))
136
+
137
+ for test_file in file_list:
138
+ await run_test(test_file)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: tinybird
3
- Version: 0.0.1.dev14
3
+ Version: 0.0.1.dev16
4
4
  Summary: Tinybird Command Line Tool
5
5
  Home-page: https://www.tinybird.co/docs/cli/introduction.html
6
6
  Author: Tinybird
@@ -1,12 +1,12 @@
1
1
  tinybird/__cli__.py,sha256=pgYsVLcqL16wtSn6KtKweNZYoYJdEksTgSvQAW7hH64,250
2
- tinybird/check_pypi.py,sha256=RIhZcTg9dk5SFuvV3yVtFgBael_5_VYl4GzOZJojCfU,971
3
- tinybird/client.py,sha256=nd97gD2-8Ap8yDonBcVwk9eXDAL43hmIYdo-Pse43RE,50738
2
+ tinybird/client.py,sha256=_voHMgKOwOhmncSaPH6DL94X6YPqKJJzcY-EKp8xLug,50810
4
3
  tinybird/config.py,sha256=Z-BX9FrjgsLw1YwcCdF0IztLB97Zpc70VVPplO_pDSY,6089
5
4
  tinybird/connectors.py,sha256=lkpVSUmSuViEZBa4QjTK7YmPHUop0a5UFoTrSmlVq6k,15244
6
5
  tinybird/context.py,sha256=kutUQ0kCwparowI74_YLXx6wtTzGLRouJ6oGHVBPzBo,1291
7
6
  tinybird/datatypes.py,sha256=IHyhZ86ib54Vnd1pbod9y2aS8DDvDKZm1HJGlThdbuQ,10460
8
- tinybird/feedback_manager.py,sha256=-nDkg13DoiNk40AztzSJdbldbKhfuTsCZcSOviK9sik,67790
7
+ tinybird/feedback_manager.py,sha256=U_VbVl3RBCPV-qc3g8vz124Mg9XeMmKk055eqiIaBqY,68338
9
8
  tinybird/git_settings.py,sha256=XUL9ZUj59-ZVQJDYmMEq4UpnuuOuQOHGlNcX3JgQHjQ,3954
9
+ tinybird/prompts.py,sha256=pCqN1JAHqdA-PgxBnzjIYpCXjpko89Xu4EHl6ol0iNs,6417
10
10
  tinybird/sql.py,sha256=gfRKjdqEygcE1WOTeQ1QV2Jal8Jzl4RSX8fftu1KSEs,45825
11
11
  tinybird/sql_template.py,sha256=IqYRfUxDYBCoOYjqqvn--_8QXLv9FSRnJ0bInx7q1Xs,93051
12
12
  tinybird/sql_template_fmt.py,sha256=1z-PuqSZXtzso8Z_mPqUc-NxIxUrNUcVIPezNieZk-M,10196
@@ -15,46 +15,47 @@ 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=-ritrHugOxQCW7qtV1hSIW8xEHKWDILawEwlcP9AVyI,816
18
+ tinybird/tb/cli.py,sha256=VS9cy6FfiQkpc1-ps7snwuw7bB_F8Z6FEmucEo3jrzY,849
19
19
  tinybird/tb/modules/auth.py,sha256=hynZ-Temot8YBsySUWKSFzZlYadtFPxG3o6lCSu1n6E,9018
20
20
  tinybird/tb/modules/branch.py,sha256=R1tTUBGyI0p_dt2IAWbuyNOvemhjCIPwYxEmOxL3zOg,38468
21
- tinybird/tb/modules/build.py,sha256=_kiloHo4cGdyQyp7TgCmXGHtmPP0cCL1gq1m-YcwNHQ,11181
21
+ tinybird/tb/modules/build.py,sha256=c9fC9qBSw7__MJreJZIpHGvKD6CgyL0zMtparmJCUgw,8001
22
+ tinybird/tb/modules/build_shell.py,sha256=oqIMZYaQ8cpaE1C09shAsiFUlWb-zwawuf6FLqRotBU,5209
22
23
  tinybird/tb/modules/cicd.py,sha256=KCFfywFfvGRh24GZwqrhICiTK_arHelPs_X4EB-pXIw,7331
23
- tinybird/tb/modules/cli.py,sha256=zXFzafgt88K5CZ5mgkeSU-R3kmA6riPklYtUbTrZ6RA,56219
24
+ tinybird/tb/modules/cli.py,sha256=UAVZfdQhw-zKokKlEL23Uup55adT1JVwcK9HPDv6TJQ,55759
24
25
  tinybird/tb/modules/common.py,sha256=Vubc2AIR8BfEupnT5e1Y8OYGEyvNoIcjo8th-SaUflw,80111
25
26
  tinybird/tb/modules/config.py,sha256=ppWvACHrSLkb5hOoQLYNby2w8jR76-8Kx2NBCst7ntQ,11760
26
27
  tinybird/tb/modules/connection.py,sha256=ZSqBGoRiJedjHKEyB_fr1ybucOHtaad8d7uqGa2Q92M,28668
27
- tinybird/tb/modules/create.py,sha256=Ky5LOyDJLgaHyWDt8un100QxKgNiQpEal-QzIW0V85I,6590
28
+ tinybird/tb/modules/create.py,sha256=BiqeuRtDM9dfIB3wwFrO5SNqdafr1hWDwma4oNHYris,42432
28
29
  tinybird/tb/modules/datasource.py,sha256=tjcf5o-HYIdTkb_c1ErGUFIE-W6G992vsvCuDGcxb9Q,35818
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=TvJJ9BlKISAb1SVI-pnHp_PcHcxGfTyjxOE_qAz90Ck,2441
33
- tinybird/tb/modules/local.py,sha256=sImiZwUMsvJRGBVZovOGBqxXo0SBWYwpZ7b8zVG_QNc,6943
34
- tinybird/tb/modules/login.py,sha256=rxPjVtaW0r8hgx2wRISKPSfprSUXVzn6XE_YAxF9oz0,5850
35
- tinybird/tb/modules/mock.py,sha256=RohmEhNfudVryn2pJrI4fASE74inovNxzN0ew85Y830,2747
33
+ tinybird/tb/modules/llm.py,sha256=duwf3r1caLaKzuOJC_stI_Hx54ncr-5uWaWeZ3HN65o,3099
34
+ tinybird/tb/modules/local.py,sha256=hV2fvHPaVHVzKwVoVDFAIbJZslOX1_COx96DZrR-dW8,5151
35
+ tinybird/tb/modules/local_common.py,sha256=Z2JYIE5HIJVNuzdIv1_NFxSEs8DOsLoCzbmT1iy0-HU,2172
36
+ tinybird/tb/modules/login.py,sha256=vIeysdttfGDBMkf_i3cqAVNR5s0X0D6exATcRsDdWiA,5849
37
+ tinybird/tb/modules/mock.py,sha256=5xhR_djr1-_JwJPI-Oy1exI3ndz7srmmvlei0DMQCAk,2824
36
38
  tinybird/tb/modules/pipe.py,sha256=9wnfKbp2FkmLiJgVk3qbra76ktwsUTXghu6j9cCEahQ,31058
37
- tinybird/tb/modules/prompts.py,sha256=g0cBW2ePzuftib02wV82VIcAZd59buAAusnirAbzqVE,8662
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=psINFpSYT1eGgy32-_4q6CJ7LOcdwBpAfasMA0_tNOU,4330
43
+ tinybird/tb/modules/test.py,sha256=wWOg9FFyAiJkF7qjYapJUne5SVyXwmmhStfpCAZwZ80,5383
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
46
- tinybird/tb/modules/datafile/build.py,sha256=bo5T-_9LWsw4dZoHDO2bgn4hpSNOK5u_RiiYlRGLToA,91948
47
+ tinybird/tb/modules/datafile/build.py,sha256=rFdK_GerPDgPfyPfZ4EZ0-cQqWfHd6htS0ls-Yy7khk,92491
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
- tinybird/tb/modules/datafile/build_pipe.py,sha256=wxqvVY3vIlG2_IAX8__mevhxqGkOxQ4-YyoWE6v2OxE,27465
50
- tinybird/tb/modules/datafile/common.py,sha256=q0XPpNE-l011Um3TXh3BmkSkUlYP5Ydkn24jXLq1I9Y,34239
50
+ tinybird/tb/modules/datafile/build_pipe.py,sha256=sSSl1rQMkR4uUbFCxK_aDXZi3JwKV64YZlBdBWgGKjo,27657
51
+ tinybird/tb/modules/datafile/common.py,sha256=NkoCdj4p-Ak3n80DJB5a33Ucw2WTcSYa8iqw4KsRZGs,81082
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=9pp0fJ2Da-7pznaHq5OV7NvzswK0RuXOPHU4kXNRMnA,1111
58
+ tinybird/tb/modules/datafile/parse_datasource.py,sha256=YKt4Sy830p2jqXW5jN1Bf60AM5o4gZ3QcZjxnyWIeBg,1190
58
59
  tinybird/tb/modules/datafile/parse_pipe.py,sha256=STgA12LOLUnnb_cvVvZeEE4ka-nfk0jsNzxJhWj94cY,2599
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
@@ -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.dev14.dist-info/METADATA,sha256=ZuulDALTiwqGmBBXo45rpSNQnzPtV9JId3zHN2q2Si4,2405
70
- tinybird-0.0.1.dev14.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
71
- tinybird-0.0.1.dev14.dist-info/entry_points.txt,sha256=LwdHU6TfKx4Qs7BqqtaczEZbImgU7Abe9Lp920zb_fo,43
72
- tinybird-0.0.1.dev14.dist-info/top_level.txt,sha256=pgw6AzERHBcW3YTi2PW4arjxLkulk2msOz_SomfOEuc,45
73
- tinybird-0.0.1.dev14.dist-info/RECORD,,
70
+ tinybird-0.0.1.dev16.dist-info/METADATA,sha256=Mo5LrkfYVPxIL2oqfKIjc2uUG41aYEzeVyTvWYI_abY,2405
71
+ tinybird-0.0.1.dev16.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
72
+ tinybird-0.0.1.dev16.dist-info/entry_points.txt,sha256=LwdHU6TfKx4Qs7BqqtaczEZbImgU7Abe9Lp920zb_fo,43
73
+ tinybird-0.0.1.dev16.dist-info/top_level.txt,sha256=pgw6AzERHBcW3YTi2PW4arjxLkulk2msOz_SomfOEuc,45
74
+ tinybird-0.0.1.dev16.dist-info/RECORD,,
tinybird/check_pypi.py DELETED
@@ -1,25 +0,0 @@
1
- from typing import Optional
2
-
3
- import requests
4
-
5
- from tinybird.feedback_manager import FeedbackManager
6
- from tinybird.syncasync import sync_to_async
7
- from tinybird.tb_cli_modules.common import CLIException, getenv_bool
8
-
9
- PYPY_URL = "https://pypi.org/pypi/tinybird/json"
10
- requests_get = sync_to_async(requests.get, thread_sensitive=False)
11
-
12
-
13
- class CheckPypi:
14
- async def get_latest_version(self) -> Optional[str]:
15
- version: Optional[str] = None
16
- try:
17
- disable_ssl: bool = getenv_bool("TB_DISABLE_SSL_CHECKS", False)
18
- response: requests.Response = await requests_get(PYPY_URL, verify=not disable_ssl)
19
- if response.status_code != 200:
20
- raise CLIException(FeedbackManager.error_exception(error=response.content.decode("utf-8")))
21
- version = response.json()["info"]["version"]
22
- except Exception as e:
23
- raise CLIException(FeedbackManager.error_exception(error=str(e)))
24
-
25
- return version