tinybird 0.0.1.dev18__py3-none-any.whl → 0.0.1.dev20__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.

Files changed (49) hide show
  1. tinybird/client.py +24 -4
  2. tinybird/config.py +1 -1
  3. tinybird/feedback_manager.py +8 -30
  4. tinybird/tb/__cli__.py +8 -0
  5. tinybird/tb/modules/auth.py +1 -1
  6. tinybird/tb/modules/build.py +26 -80
  7. tinybird/tb/modules/cicd.py +1 -1
  8. tinybird/tb/modules/cli.py +11 -837
  9. tinybird/tb/modules/common.py +1 -55
  10. tinybird/tb/modules/connection.py +1 -1
  11. tinybird/tb/modules/create.py +18 -7
  12. tinybird/tb/modules/datafile/build.py +142 -971
  13. tinybird/tb/modules/datafile/build_common.py +1 -1
  14. tinybird/tb/modules/datafile/build_datasource.py +1 -1
  15. tinybird/tb/modules/datafile/build_pipe.py +1 -1
  16. tinybird/tb/modules/datafile/common.py +12 -11
  17. tinybird/tb/modules/datafile/diff.py +1 -1
  18. tinybird/tb/modules/datafile/fixture.py +1 -1
  19. tinybird/tb/modules/datafile/format_common.py +0 -7
  20. tinybird/tb/modules/datafile/format_datasource.py +0 -2
  21. tinybird/tb/modules/datafile/format_pipe.py +0 -2
  22. tinybird/tb/modules/datafile/parse_datasource.py +1 -1
  23. tinybird/tb/modules/datafile/parse_pipe.py +1 -1
  24. tinybird/tb/modules/datafile/pull.py +1 -1
  25. tinybird/tb/modules/datasource.py +4 -75
  26. tinybird/tb/modules/feedback_manager.py +1048 -0
  27. tinybird/tb/modules/fmt.py +1 -1
  28. tinybird/tb/modules/job.py +1 -1
  29. tinybird/tb/modules/llm.py +6 -4
  30. tinybird/tb/modules/local.py +26 -21
  31. tinybird/tb/modules/local_common.py +2 -1
  32. tinybird/tb/modules/login.py +18 -11
  33. tinybird/tb/modules/mock.py +18 -7
  34. tinybird/tb/modules/pipe.py +4 -126
  35. tinybird/tb/modules/{build_shell.py → shell.py} +66 -36
  36. tinybird/tb/modules/table.py +88 -5
  37. tinybird/tb/modules/tag.py +2 -2
  38. tinybird/tb/modules/test.py +45 -29
  39. tinybird/tb/modules/tinyunit/tinyunit.py +1 -1
  40. tinybird/tb/modules/token.py +2 -2
  41. tinybird/tb/modules/watch.py +72 -0
  42. tinybird/tb/modules/workspace.py +1 -1
  43. tinybird/tb/modules/workspace_members.py +1 -1
  44. {tinybird-0.0.1.dev18.dist-info → tinybird-0.0.1.dev20.dist-info}/METADATA +1 -1
  45. tinybird-0.0.1.dev20.dist-info/RECORD +76 -0
  46. tinybird-0.0.1.dev18.dist-info/RECORD +0 -73
  47. {tinybird-0.0.1.dev18.dist-info → tinybird-0.0.1.dev20.dist-info}/WHEEL +0 -0
  48. {tinybird-0.0.1.dev18.dist-info → tinybird-0.0.1.dev20.dist-info}/entry_points.txt +0 -0
  49. {tinybird-0.0.1.dev18.dist-info → tinybird-0.0.1.dev20.dist-info}/top_level.txt +0 -0
tinybird/client.py CHANGED
@@ -103,7 +103,7 @@ class TinyB:
103
103
  self.send_telemetry = send_telemetry
104
104
  self.semver = semver
105
105
 
106
- async def _req(
106
+ async def _req_raw(
107
107
  self,
108
108
  endpoint: str,
109
109
  data=None,
@@ -155,10 +155,15 @@ class TinyB:
155
155
  response = await sync_to_async(session.get, thread_sensitive=False)(
156
156
  url, verify=verify_ssl, **kwargs
157
157
  )
158
-
159
158
  except Exception as e:
160
159
  raise e
161
160
 
161
+ if self.send_telemetry:
162
+ try:
163
+ add_telemetry_event("api_request", endpoint=url, token=self.token, status_code=response.status_code)
164
+ except Exception as ex:
165
+ logging.exception(f"Can't send telemetry: {ex}")
166
+
162
167
  logging.debug("== server response ==")
163
168
  logging.debug(response.content)
164
169
  logging.debug("== end ==")
@@ -169,6 +174,21 @@ class TinyB:
169
174
  except Exception as ex:
170
175
  logging.exception(f"Can't send telemetry: {ex}")
171
176
 
177
+ return response
178
+
179
+ async def _req(
180
+ self,
181
+ endpoint: str,
182
+ data=None,
183
+ files=None,
184
+ method: str = "GET",
185
+ retries: int = LIMIT_RETRIES,
186
+ use_token: Optional[str] = None,
187
+ **kwargs,
188
+ ):
189
+ token_to_use = use_token if use_token else self.token
190
+ response = await self._req_raw(endpoint, data, files, method, retries, use_token, **kwargs)
191
+
172
192
  if response.status_code == 403:
173
193
  error = parse_error_response(response)
174
194
  if not token_to_use:
@@ -500,7 +520,7 @@ class TinyB:
500
520
  payload = {"datasource_a": datasource_a, "datasource_b": datasource_b}
501
521
  return await self._req("/v0/datasources/exchange", method="POST", data=payload)
502
522
 
503
- async def datasource_events(self, datasource_name: str, data: Dict[str, Any]):
523
+ async def datasource_events(self, datasource_name: str, data: str):
504
524
  return await self._req(f"/v0/events?name={datasource_name}", method="POST", data=data)
505
525
 
506
526
  async def analyze_pipe_node(
@@ -816,7 +836,7 @@ class TinyB:
816
836
  params = {"with_token": "true" if with_token else "false"}
817
837
  return await self._req(f"/v0/workspaces/{workspace_id}?{urlencode(params)}")
818
838
 
819
- async def workspace_info(self):
839
+ async def workspace_info(self) -> Dict[str, Any]:
820
840
  return await self._req("/v0/workspace")
821
841
 
822
842
  async def wait_for_job(
tinybird/config.py CHANGED
@@ -20,7 +20,7 @@ CURRENT_VERSION = f"{__cli__.__version__}"
20
20
  VERSION = f"{__cli__.__version__} (rev {__revision__})"
21
21
  DEFAULT_UI_HOST = "https://app.tinybird.co"
22
22
  SUPPORTED_CONNECTORS = ["bigquery", "snowflake"]
23
- PROJECT_PATHS = ["datasources", "pipes", "tests"]
23
+ PROJECT_PATHS = ["datasources", "datasources/fixtures", "endpoints", "pipes", "tests", "scripts", "deploy"]
24
24
  DEPRECATED_PROJECT_PATHS = ["endpoints"]
25
25
  MIN_WORKSPACE_ID_LENGTH = 36
26
26
  LEGACY_HOSTS = {
@@ -1,6 +1,5 @@
1
1
  from collections import namedtuple
2
2
  from typing import Any, Callable
3
- import os
4
3
 
5
4
  FeedbackMessage = namedtuple("FeedbackMessage", "message")
6
5
 
@@ -27,16 +26,6 @@ def print_message(message: str, color: str = bcolors.ENDC) -> Callable[..., str]
27
26
  def error_message(message: str) -> Callable[..., str]:
28
27
  return print_message(f"\n** {message}", bcolors.FAIL)
29
28
 
30
- def error_exception(message: Exception) -> Callable[..., str]:
31
- def formatter(**kwargs: Any) -> str:
32
- color = bcolors.FAIL
33
- if isinstance(kwargs.get('error', ''), Exception) and os.environ.get('TB_DEBUG', '') != '':
34
- import traceback
35
- stack_trace = f"Traceback:\n{''.join(traceback.format_tb(kwargs['error'].__traceback__))}"
36
- return f"{color}{message.format(**kwargs)}{stack_trace}{bcolors.ENDC}"
37
- return f"{color}{message.format(**kwargs)}{bcolors.ENDC}"
38
- return formatter
39
-
40
29
 
41
30
  def simple_error_message(message: str) -> Callable[..., str]:
42
31
  return print_message(f"{message}", bcolors.FAIL)
@@ -62,12 +51,8 @@ def prompt_message(message: str) -> Callable[..., str]:
62
51
  return print_message(message, bcolors.HEADER)
63
52
 
64
53
 
65
- def gray_message(message: str) -> Callable[..., str]:
66
- return print_message(message, bcolors.CGREY)
67
-
68
-
69
54
  class FeedbackManager:
70
- error_exception = error_exception("{error}")
55
+ error_exception = error_message("{error}")
71
56
  simple_error_exception = simple_error_message("{error}")
72
57
  error_exception_trace = error_message("{error}\n** Trace:\n{trace}")
73
58
  error_notoken = error_message(
@@ -576,9 +561,9 @@ Ready? """
576
561
  )
577
562
  warning_fixture_not_found = warning_message("** Warning: No fixture found for the datasource {datasource_name}")
578
563
  warning_update_version = warning_message(
579
- '** UPDATE AVAILABLE: please run "pip install tinybird=={latest_version}" to update or `export TB_VERSION_WARNING=0` to skip the check.'
564
+ '** UPDATE AVAILABLE: please run "pip install tinybird-cli=={latest_version}" to update or `export TB_VERSION_WARNING=0` to skip the check.'
580
565
  )
581
- warning_current_version = warning_message("** current: tinybird {current_version}\n")
566
+ warning_current_version = warning_message("** current: tinybird-cli {current_version}\n")
582
567
  warning_confirm_truncate_datasource = prompt_message(
583
568
  "Do you want to truncate {datasource}? Once truncated, your data can't be recovered"
584
569
  )
@@ -686,7 +671,6 @@ Ready? """
686
671
  info_building_dependencies = info_message("** Building dependencies")
687
672
  info_processing_new_resource = info_message("** Running '{name}' {version}")
688
673
  info_dry_processing_new_resource = info_message("** [DRY RUN] Running '{name}' {version}")
689
- info_building_resource = info_message("** Building {name}")
690
674
  info_processing_resource = info_message(
691
675
  "** Running '{name}' => v{version} (remote latest version: v{latest_version})"
692
676
  )
@@ -727,9 +711,9 @@ Ready? """
727
711
  info_removing_pipe = info_message("** Removing pipe {pipe}")
728
712
  info_removing_pipe_not_found = info_message("** {pipe} not found")
729
713
  info_dry_removing_pipe = info_message("** [DRY RUN] Removing pipe {pipe}")
730
- info_path_created = info_message(" /{path}")
731
- info_file_created = info_message(" /{file}")
732
- info_path_already_exists = info_message(" /{path} already exists, skipping")
714
+ info_path_created = info_message("** - /{path} created")
715
+ info_file_created = info_message("** - {file} created")
716
+ info_path_already_exists = info_message("** - /{path} already exists, skipping")
733
717
  info_dottinyb_already_ignored = info_message("** - '.tinyb' already in .gitignore, skipping")
734
718
  info_dotdifftemp_already_ignored = info_message("** - '.diff_tmp' not found or already in .gitignore, skipping")
735
719
  info_dottinyenv_already_exists = info_message("** - '.tinyenv' already exists, skipping")
@@ -803,7 +787,7 @@ Ready? """
803
787
  info_diff_resources_for_git_init = info_message(
804
788
  "** Checking diffs between remote Workspace and local. Hint: use 'tb diff' to check if your Data Project and Workspace synced"
805
789
  )
806
- info_cicd_file_generated = info_message(" {file_path}")
790
+ info_cicd_file_generated = info_message("** File {file_path} generated for CI/CD")
807
791
  info_available_git_providers = info_message("** List of available providers:")
808
792
  info_git_release_init_without_diffs = info_message("** No diffs detected for '{workspace}'")
809
793
  info_deployment_detecting_changes_header = info_message("\n** Detecting changes from last commit ...")
@@ -950,7 +934,7 @@ Ready? """
950
934
  success_create = success_message("** '{name}' created")
951
935
  success_delete = success_message("** '{name}' deleted")
952
936
  success_dynamodb_initial_load = success_message("** Initial load of DynamoDB table started: {job_url}")
953
- success_progress_blocks = success_message(" Done!")
937
+ success_progress_blocks = success_message("** \N{FRONT-FACING BABY CHICK} done")
954
938
  success_now_using_config = success_message("** Now using {name} ({id})")
955
939
  success_connector_config = success_message(
956
940
  "** {connector} configuration written to {file_name} file, consider adding it to .gitignore"
@@ -1036,9 +1020,3 @@ Ready? """
1036
1020
  success_tag_removed = success_message("** Tag '{tag_name}' removed!")
1037
1021
 
1038
1022
  debug_running_file = print_message("** Running {file}", bcolors.CGREY)
1039
-
1040
- success = success_message("{message}")
1041
- info = info_message("{message}")
1042
- highlight = info_highlight_message("{message}")
1043
- error = error_message("{message}")
1044
- gray = gray_message("{message}")
tinybird/tb/__cli__.py ADDED
@@ -0,0 +1,8 @@
1
+
2
+ __name__ = 'tinybird'
3
+ __description__ = 'Tinybird Command Line Tool'
4
+ __url__ = 'https://www.tinybird.co/docs/cli/introduction.html'
5
+ __author__ = 'Tinybird'
6
+ __author_email__ = 'support@tinybird.co'
7
+ __version__ = '0.0.1.dev20'
8
+ __revision__ = '6125327'
@@ -10,7 +10,6 @@ import click
10
10
  import humanfriendly.tables
11
11
 
12
12
  from tinybird.config import get_display_host
13
- from tinybird.feedback_manager import FeedbackManager
14
13
  from tinybird.tb.modules.cli import cli
15
14
  from tinybird.tb.modules.common import (
16
15
  configure_connector,
@@ -23,6 +22,7 @@ from tinybird.tb.modules.common import (
23
22
  )
24
23
  from tinybird.tb.modules.config import CLIConfig, ConfigValueOrigin
25
24
  from tinybird.tb.modules.exceptions import CLIAuthException
25
+ from tinybird.tb.modules.feedback_manager import FeedbackManager
26
26
  from tinybird.tb.modules.regions import Region
27
27
 
28
28
 
@@ -3,17 +3,13 @@ import os
3
3
  import threading
4
4
  import time
5
5
  from pathlib import Path
6
- from typing import Any, Awaitable, Callable, List, Union
6
+ from typing import List
7
7
 
8
8
  import click
9
- from watchdog.events import FileSystemEventHandler
10
- from watchdog.observers import Observer
11
9
 
12
10
  import tinybird.context as context
13
11
  from tinybird.client import TinyB
14
12
  from tinybird.config import FeatureFlags
15
- from tinybird.feedback_manager import FeedbackManager
16
- from tinybird.tb.modules.build_shell import BuildShell, print_table_formatted
17
13
  from tinybird.tb.modules.cli import cli
18
14
  from tinybird.tb.modules.common import push_data
19
15
  from tinybird.tb.modules.datafile.build import folder_build
@@ -22,69 +18,10 @@ from tinybird.tb.modules.datafile.exceptions import ParseException
22
18
  from tinybird.tb.modules.datafile.fixture import build_fixture_name, get_fixture_dir
23
19
  from tinybird.tb.modules.datafile.parse_datasource import parse_datasource
24
20
  from tinybird.tb.modules.datafile.parse_pipe import parse_pipe
21
+ from tinybird.tb.modules.feedback_manager import FeedbackManager
25
22
  from tinybird.tb.modules.local_common import get_tinybird_local_client
26
-
27
-
28
- class FileChangeHandler(FileSystemEventHandler):
29
- def __init__(self, filenames: List[str], process: Callable[[List[str]], None], build_ok: bool):
30
- self.filenames = filenames
31
- self.process = process
32
- self.build_ok = build_ok
33
-
34
- def on_modified(self, event: Any) -> None:
35
- is_not_vendor = "vendor/" not in event.src_path
36
- if (
37
- is_not_vendor
38
- and not event.is_directory
39
- and any(event.src_path.endswith(ext) for ext in [".datasource", ".pipe", ".ndjson"])
40
- ):
41
- filename = event.src_path.split("/")[-1]
42
- click.echo(FeedbackManager.highlight(message=f"\n\n⟲ Changes detected in {filename}\n"))
43
- try:
44
- to_process = [event.src_path] if self.build_ok else self.filenames
45
- self.process(to_process)
46
- self.build_ok = True
47
- except Exception as e:
48
- click.echo(FeedbackManager.error_exception(error=e))
49
-
50
-
51
- def watch_files(
52
- filenames: List[str],
53
- process: Union[Callable[[List[str]], None], Callable[[List[str]], Awaitable[None]]],
54
- shell: BuildShell,
55
- folder: str,
56
- build_ok: bool,
57
- ) -> None:
58
- # Handle both sync and async process functions
59
- async def process_wrapper(files: List[str]) -> None:
60
- click.echo("⚡ Rebuilding...")
61
- time_start = time.time()
62
- if asyncio.iscoroutinefunction(process):
63
- await process(files, watch=True)
64
- else:
65
- process(files, watch=True)
66
- time_end = time.time()
67
- elapsed_time = time_end - time_start
68
- click.echo(
69
- FeedbackManager.success(message="\n✓ ")
70
- + FeedbackManager.gray(message=f"Rebuild completed in {elapsed_time:.1f}s")
71
- )
72
- shell.reprint_prompt()
73
-
74
- event_handler = FileChangeHandler(filenames, lambda f: asyncio.run(process_wrapper(f)), build_ok)
75
- observer = Observer()
76
-
77
- observer.schedule(event_handler, path=folder, recursive=True)
78
-
79
- observer.start()
80
-
81
- try:
82
- while True:
83
- time.sleep(1)
84
- except KeyboardInterrupt:
85
- observer.stop()
86
-
87
- observer.join()
23
+ from tinybird.tb.modules.shell import Shell, print_table_formatted
24
+ from tinybird.tb.modules.watch import watch_files
88
25
 
89
26
 
90
27
  @cli.command()
@@ -119,7 +56,7 @@ def build(
119
56
 
120
57
  for filename in filenames:
121
58
  if os.path.isdir(filename):
122
- process(filenames=get_project_filenames(filename))
59
+ check_filenames(filenames=get_project_filenames(filename))
123
60
 
124
61
  file_suffix = Path(filename).suffix
125
62
  if file_suffix == incl_suffix:
@@ -149,7 +86,7 @@ def build(
149
86
  name = "_".join(fixture_path.stem.split("_")[:-1])
150
87
  ds_path = Path(folder) / "datasources" / f"{name}.datasource"
151
88
  if ds_path.exists():
152
- await append_datasource({}, tb_client, name, str(fixture_path), silent=True)
89
+ await append_datasource(tb_client, name, str(fixture_path), silent=True)
153
90
 
154
91
  if watch:
155
92
  if filename.endswith(".datasource"):
@@ -157,7 +94,7 @@ def build(
157
94
  name = build_fixture_name(filename, ds_path.stem, ds_path.read_text())
158
95
  fixture_path = get_fixture_dir() / f"{name}.ndjson"
159
96
  if fixture_path.exists():
160
- await append_datasource({}, tb_client, ds_path.stem, str(fixture_path), silent=True)
97
+ await append_datasource(tb_client, ds_path.stem, str(fixture_path), silent=True)
161
98
  if not filename.endswith(".ndjson"):
162
99
  await build_and_print_resource(tb_client, filename)
163
100
 
@@ -179,7 +116,7 @@ def build(
179
116
  name = build_fixture_name(filename, ds_path.stem, ds_path.read_text())
180
117
  fixture_path = get_fixture_dir() / f"{name}.ndjson"
181
118
  if fixture_path.exists():
182
- await append_datasource({}, tb_client, ds_path.stem, str(fixture_path), silent=True)
119
+ await append_datasource(tb_client, ds_path.stem, str(fixture_path), silent=True)
183
120
  click.echo(FeedbackManager.success(message=f"\n✓ Build completed in {elapsed_time:.1f}s\n"))
184
121
  ok = True
185
122
  except Exception as e:
@@ -193,22 +130,36 @@ def build(
193
130
  paths = [Path(f) for f in get_project_filenames(folder, with_vendor=True)]
194
131
 
195
132
  def is_vendor(f: Path) -> bool:
196
- return "vendor/" in f.parts
133
+ return f.parts[0] == "vendor"
197
134
 
198
135
  def get_vendor_workspace(f: Path) -> str:
199
136
  return f.parts[1]
200
137
 
138
+ def is_endpoint(f: Path) -> bool:
139
+ return f.suffix == ".pipe" and not is_vendor(f) and f.parts[0] == "endpoints"
140
+
141
+ def is_pipe(f: Path) -> bool:
142
+ return f.suffix == ".pipe" and not is_vendor(f)
143
+
201
144
  datasource_paths = [f for f in paths if f.suffix == ".datasource"]
202
145
  datasources = [f.stem for f in datasource_paths if not is_vendor(f)]
203
146
  shared_datasources = [f"{get_vendor_workspace(f)}.{f.stem}" for f in datasource_paths if is_vendor(f)]
204
- pipes = [f.stem for f in paths if f.suffix == ".pipe" and not is_vendor(f)]
205
- shell = BuildShell(folder=folder, client=tb_client, datasources=datasources + shared_datasources, pipes=pipes)
147
+ pipes = [f.stem for f in paths if is_pipe(f) and not is_endpoint(f)]
148
+ endpoints = [f.stem for f in paths if is_endpoint(f)]
149
+ shell = Shell(
150
+ folder=folder,
151
+ client=tb_client,
152
+ datasources=datasources,
153
+ shared_datasources=shared_datasources,
154
+ pipes=pipes,
155
+ endpoints=endpoints,
156
+ )
206
157
  click.echo(FeedbackManager.highlight(message="◎ Watching for changes..."))
207
158
  watcher_thread = threading.Thread(
208
159
  target=watch_files, args=(filenames, process, shell, folder, build_ok), daemon=True
209
160
  )
210
161
  watcher_thread.start()
211
- shell.run_shell()
162
+ shell.run()
212
163
 
213
164
 
214
165
  async def build_and_print_resource(tb_client: TinyB, filename: str):
@@ -220,21 +171,16 @@ async def build_and_print_resource(tb_client: TinyB, filename: str):
220
171
 
221
172
 
222
173
  async def append_datasource(
223
- ctx: click.Context,
224
174
  tb_client: TinyB,
225
175
  datasource_name: str,
226
176
  url: str,
227
177
  silent: bool = False,
228
178
  ):
229
179
  await push_data(
230
- ctx,
231
180
  tb_client,
232
181
  datasource_name,
233
182
  url,
234
- connector=None,
235
- sql=None,
236
183
  mode="append",
237
- ignore_empty=False,
238
184
  concurrency=1,
239
185
  silent=silent,
240
186
  )
@@ -6,7 +6,7 @@ from typing import Any, Dict, List, Optional, Type, Union
6
6
  import click
7
7
  from tornado.template import Template
8
8
 
9
- from tinybird.feedback_manager import FeedbackManager
9
+ from tinybird.tb.modules.feedback_manager import FeedbackManager
10
10
 
11
11
 
12
12
  class Provider(Enum):