tinybird 0.0.1.dev259__py3-none-any.whl → 0.0.1.dev261__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.

@@ -1372,6 +1372,8 @@ def parse(
1372
1372
  default_node: Optional[str] = None,
1373
1373
  basepath: str = ".",
1374
1374
  replace_includes: bool = True,
1375
+ # TODO(eclbg): I think we could remove `skip_eval` in Forward, and pin it to False. This would let us remove some
1376
+ # other functions like `eval_var` that obscure things a bit.
1375
1377
  skip_eval: bool = False,
1376
1378
  ) -> ParseResult:
1377
1379
  lines = list(StringIO(s, newline=None))
@@ -1482,11 +1484,18 @@ def parse(
1482
1484
 
1483
1485
  parser_state.current_node["indexes"] = indexes
1484
1486
 
1485
- def assign_var(v: str) -> Callable[[VarArg(str), KwArg(Any)], None]:
1487
+ def assign_var(v: str, allowed_values: Optional[set[str]] = None) -> Callable[[VarArg(str), KwArg(Any)], None]:
1486
1488
  @multiline_not_supported
1487
1489
  def _f(*args: str, **kwargs: Any):
1488
1490
  s = _unquote((" ".join(args)).strip())
1489
- parser_state.current_node[v.lower()] = eval_var(s, skip=skip_eval)
1491
+ val = eval_var(s, skip=skip_eval)
1492
+ if allowed_values and val.lower() not in {v.lower() for v in allowed_values}:
1493
+ raise DatafileSyntaxError(
1494
+ f"{val} is not an allowed value for {kwargs['cmd'].upper()}. Use one of: {allowed_values}",
1495
+ lineno=kwargs["lineno"],
1496
+ pos=1,
1497
+ )
1498
+ parser_state.current_node[v.lower()] = val
1490
1499
 
1491
1500
  return _f
1492
1501
 
@@ -1804,6 +1813,7 @@ def parse(
1804
1813
  "shared_with": shared_with, # Not supported yet
1805
1814
  "export_service": export_service, # Deprecated
1806
1815
  "forward_query": sql("forward_query"),
1816
+ "backfill": assign_var("backfill", allowed_values={"skip"}),
1807
1817
  # ENGINE_* commands are added dynamically after this dict's definition
1808
1818
  },
1809
1819
  DatafileKind.pipe: {
tinybird/sql_toolset.py CHANGED
@@ -456,6 +456,10 @@ def replace_tables(
456
456
  sql, None, output_one_line=output_one_line, timestamp=timestamp, function_allow_list=hashable_list
457
457
  )
458
458
 
459
+ # Fix for empty database names in JOINs - remove empty backticks like ``.table_name
460
+ # that are generated when chquery.replace_tables processes tuples with empty database names
461
+ sql = sql.replace("``.", "")
462
+
459
463
  return sql
460
464
 
461
465
 
tinybird/tb/__cli__.py CHANGED
@@ -4,5 +4,5 @@ __description__ = 'Tinybird Command Line Tool'
4
4
  __url__ = 'https://www.tinybird.co/docs/forward/commands'
5
5
  __author__ = 'Tinybird'
6
6
  __author_email__ = 'support@tinybird.co'
7
- __version__ = '0.0.1.dev259'
8
- __revision__ = '5b5805f'
7
+ __version__ = '0.0.1.dev261'
8
+ __revision__ = '80be54a'
@@ -17,11 +17,11 @@ from tinybird.tb.modules.agent.animations import ThinkingAnimation
17
17
  from tinybird.tb.modules.agent.banner import display_banner
18
18
  from tinybird.tb.modules.agent.memory import clear_history, clear_messages, load_messages, save_messages
19
19
  from tinybird.tb.modules.agent.models import create_model, model_costs
20
- from tinybird.tb.modules.agent.prompts import agent_system_prompt, resources_prompt
20
+ from tinybird.tb.modules.agent.prompts import agent_system_prompt, load_custom_project_rules, resources_prompt
21
21
  from tinybird.tb.modules.agent.tools.analyze import analyze_file, analyze_url
22
22
  from tinybird.tb.modules.agent.tools.append import append_file, append_url
23
23
  from tinybird.tb.modules.agent.tools.build import build
24
- from tinybird.tb.modules.agent.tools.create_datafile import create_datafile
24
+ from tinybird.tb.modules.agent.tools.create_datafile import create_datafile, rename_datafile_or_fixture
25
25
  from tinybird.tb.modules.agent.tools.deploy import deploy
26
26
  from tinybird.tb.modules.agent.tools.deploy_check import deploy_check
27
27
  from tinybird.tb.modules.agent.tools.diff_resource import diff_resource
@@ -75,6 +75,12 @@ class TinybirdAgent:
75
75
  tools=[
76
76
  Tool(preview_datafile, docstring_format="google", require_parameter_descriptions=True, takes_ctx=False),
77
77
  Tool(create_datafile, docstring_format="google", require_parameter_descriptions=True, takes_ctx=True),
78
+ Tool(
79
+ rename_datafile_or_fixture,
80
+ docstring_format="google",
81
+ require_parameter_descriptions=True,
82
+ takes_ctx=True,
83
+ ),
78
84
  Tool(plan, docstring_format="google", require_parameter_descriptions=True, takes_ctx=True),
79
85
  Tool(build, docstring_format="google", require_parameter_descriptions=True, takes_ctx=True),
80
86
  Tool(deploy, docstring_format="google", require_parameter_descriptions=True, takes_ctx=True),
@@ -99,7 +105,7 @@ class TinybirdAgent:
99
105
  Tool(create_tests_tool, docstring_format="google", require_parameter_descriptions=True, takes_ctx=True),
100
106
  Tool(run_tests_tool, docstring_format="google", require_parameter_descriptions=True, takes_ctx=True),
101
107
  ],
102
- # history_processors=[self._keep_recent_messages],
108
+ history_processors=[self._context_aware_processor],
103
109
  )
104
110
 
105
111
  @self.agent.instructions
@@ -127,9 +133,22 @@ class TinybirdAgent:
127
133
  def add_message(self, message: ModelMessage) -> None:
128
134
  self.messages.append(message)
129
135
 
130
- def _keep_recent_messages(self, messages: list[ModelMessage]) -> list[ModelMessage]:
131
- """Keep only the last 5 messages to manage token usage."""
132
- return messages[-5:] if len(messages) > 5 else messages
136
+ def _context_aware_processor(
137
+ self,
138
+ ctx: RunContext[TinybirdAgentContext],
139
+ messages: list[ModelMessage],
140
+ ) -> list[ModelMessage]:
141
+ # Access current usage
142
+ if not ctx.usage:
143
+ return messages
144
+
145
+ current_tokens = ctx.usage.total_tokens or 0
146
+
147
+ # Filter messages based on context
148
+ if current_tokens < 200_000:
149
+ return messages
150
+
151
+ return messages[-10:] # Keep only recent messages when token usage is high
133
152
 
134
153
  def _build_agent_deps(self, config: dict[str, Any]) -> TinybirdAgentContext:
135
154
  client = TinyB(token=self.token, host=self.host)
@@ -170,7 +189,7 @@ class TinybirdAgent:
170
189
  )
171
190
 
172
191
  def run(self, user_prompt: str, config: dict[str, Any]) -> None:
173
- user_prompt = f"{user_prompt}\n\n{resources_prompt(self.project)}"
192
+ user_prompt = f"{user_prompt}\n\n{load_custom_project_rules(self.project.folder)}"
174
193
  self.thinking_animation.start()
175
194
  result = self.agent.run_sync(
176
195
  user_prompt,
@@ -185,7 +204,7 @@ class TinybirdAgent:
185
204
  self._echo_usage(config, result)
186
205
 
187
206
  async def run_iter(self, user_prompt: str, config: dict[str, Any]) -> None:
188
- user_prompt = f"{user_prompt}\n\n"
207
+ user_prompt = f"{user_prompt}\n\n{load_custom_project_rules(self.project.folder)}"
189
208
  self.thinking_animation.start()
190
209
  deps = self._build_agent_deps(config)
191
210
 
@@ -353,10 +372,17 @@ def run_agent(
353
372
  sys.exit(1)
354
373
 
355
374
 
356
- def build_project(config: dict[str, Any], project: Project, silent: bool = True, test: bool = True) -> None:
375
+ def build_project(
376
+ config: dict[str, Any], project: Project, silent: bool = True, test: bool = True, load_fixtures: bool = False
377
+ ) -> None:
357
378
  local_client = get_tinybird_local_client(config, test=test, silent=silent)
358
379
  build_error = build_process(
359
- project=project, tb_client=local_client, watch=False, silent=silent, exit_on_error=False
380
+ project=project,
381
+ tb_client=local_client,
382
+ watch=False,
383
+ silent=silent,
384
+ exit_on_error=False,
385
+ load_fixtures=load_fixtures,
360
386
  )
361
387
  if build_error:
362
388
  raise CLIBuildException(build_error)
@@ -531,8 +531,8 @@ You are a Tinybird Code, an agentic CLI that can help users to work with Tinybir
531
531
  You are an interactive CLI tool that helps users with data engineering tasks. Use the instructions below and the tools available to you to assist the user.
532
532
 
533
533
  # Tone and style
534
- You should be concise, direct, and to the point.
535
- Remember that your output will be displayed on a command line interface. Your responses can use Github-flavored markdown for formatting. Do not use emojis.
534
+ You should be concise, direct, and to the point. Maintain a professional tone. Do not use emojis.
535
+ Remember that your output will be displayed on a command line interface. Your responses can use Github-flavored markdown for formatting.
536
536
  Output text to communicate with the user; all text you output outside of tool use is displayed to the user. Only use tools to complete tasks. Never use tools like Bash or code comments as means to communicate with the user during the session.
537
537
  If you cannot or will not help the user with something, please do not say why or what it could lead to, since this comes across as preachy and annoying. Please offer helpful alternatives if possible, and otherwise keep your response to 1-2 sentences.
538
538
  IMPORTANT: You should minimize output tokens as much as possible while maintaining helpfulness, quality, and accuracy. Only address the specific query or task at hand, avoiding tangential information unless absolutely critical for completing the request. If you can answer in 1-3 sentences or a short paragraph, please do.
@@ -568,9 +568,10 @@ You have access to the following tools:
568
568
  13. `request_endpoint` - Request an endpoint against Tinybird Cloud or Local.
569
569
  14. `diff_resource` - Diff the content of a resource in Tinybird Cloud vs Tinybird Local vs Project local file.
570
570
  15. `create_tests` - Create tests for an endpoint.
571
+ 16. `rename_datafile_or_fixture` - Rename a datafile or fixture.
571
572
 
572
573
  # When creating or updating datafiles:
573
- 1. Use `plan` tool to plan the creation or update of resources.
574
+ 1. Use `plan` tool to plan the creation, update or rename of resources.
574
575
  2. If the user confirms the plan, go from 3 to 7 steps until all the resources are created, updated or skipped.
575
576
  3. Use `preview_datafile` tool to preview the content of a datafile.
576
577
  4. Without asking, use the `create_datafile` tool to create the datafile, because it will ask for confirmation before creating the file.
@@ -580,7 +581,7 @@ You have access to the following tools:
580
581
  8. If the datafile was created successfully, but the built failed, try to fix the error and repeat the process.
581
582
 
582
583
  # When creating a landing datasource given a .ndjson file:
583
- - If the user does not specify anything about the desired schema, create a schema like this:
584
+ - If the user does not specify anything about the desired schema, create a schema like this (sorting key not needed in this case)
584
585
  SCHEMA >
585
586
  `data` String `json:$`
586
587
 
@@ -674,3 +675,12 @@ GCS: {gcs_connection_example}
674
675
  # Info
675
676
  Today is {datetime.now().strftime("%Y-%m-%d")}
676
677
  """
678
+
679
+
680
+ def load_custom_project_rules(folder: str) -> str:
681
+ tinybird_rules = Path(folder).joinpath("TINYBIRD.md")
682
+
683
+ if not tinybird_rules.exists():
684
+ return ""
685
+
686
+ return f"# Custom Project Rulesd defined by the user\n\n{tinybird_rules.read_text()}"
@@ -1,16 +1,19 @@
1
1
  import json
2
- from pathlib import Path
3
2
  from urllib.parse import urlparse
4
3
 
5
4
  import click
6
5
  from pydantic_ai import RunContext
7
6
 
8
- from tinybird.tb.modules.agent.utils import AgentRunCancelled, TinybirdAgentContext
7
+ from tinybird.tb.modules.agent.utils import (
8
+ AgentRunCancelled,
9
+ TinybirdAgentContext,
10
+ copy_fixture_to_project_folder_if_needed,
11
+ )
9
12
  from tinybird.tb.modules.feedback_manager import FeedbackManager
10
13
 
11
14
 
12
15
  def analyze_file(ctx: RunContext[TinybirdAgentContext], fixture_pathname: str):
13
- """Analyze a fixture data file present in the project folder
16
+ """Analyze a fixture data file present in the project folder or outside by copying it to the project folder before analyzing
14
17
 
15
18
  Args:
16
19
  fixture_pathname (str): a path or an external url to a fixture file. Required.
@@ -20,8 +23,15 @@ def analyze_file(ctx: RunContext[TinybirdAgentContext], fixture_pathname: str):
20
23
  """
21
24
  try:
22
25
  ctx.deps.thinking_animation.stop()
23
- click.echo(FeedbackManager.highlight(message=f"» Analyzing {fixture_pathname}..."))
24
- fixture_path = Path(ctx.deps.folder) / fixture_pathname.lstrip("/")
26
+ fixture_path_or_error = copy_fixture_to_project_folder_if_needed(ctx, fixture_pathname)
27
+
28
+ if isinstance(fixture_path_or_error, str):
29
+ ctx.deps.thinking_animation.start()
30
+ return fixture_path_or_error
31
+
32
+ fixture_path = fixture_path_or_error
33
+
34
+ click.echo(FeedbackManager.highlight(message=f"» Analyzing {fixture_path.name}..."))
25
35
 
26
36
  if not fixture_path.exists():
27
37
  click.echo(FeedbackManager.error(message=f"No fixture data found for {fixture_pathname}."))
@@ -36,7 +46,7 @@ def analyze_file(ctx: RunContext[TinybirdAgentContext], fixture_pathname: str):
36
46
  data = response["preview"]["data"][:10]
37
47
  columns = response["analysis"]["columns"]
38
48
 
39
- return f"#Result of analysis of {fixture_pathname}:\n##Columns:\n{json.dumps(columns)}\n##Data sample:\n{json.dumps(data)}"
49
+ return f"#Result of analysis of {fixture_path.name}:\n##Columns:\n{json.dumps(columns)}\n##Data sample:\n{json.dumps(data)}"
40
50
  except AgentRunCancelled as e:
41
51
  raise e
42
52
  except Exception as e:
@@ -1,14 +1,22 @@
1
+ import os
2
+
1
3
  import click
2
4
  from pydantic_ai import RunContext
3
5
 
4
- from tinybird.tb.modules.agent.utils import AgentRunCancelled, TinybirdAgentContext, show_confirmation, show_input
6
+ from tinybird.tb.modules.agent.utils import (
7
+ AgentRunCancelled,
8
+ TinybirdAgentContext,
9
+ copy_fixture_to_project_folder_if_needed,
10
+ show_confirmation,
11
+ show_input,
12
+ )
5
13
  from tinybird.tb.modules.feedback_manager import FeedbackManager
6
14
 
7
15
 
8
16
  def append_file(
9
17
  ctx: RunContext[TinybirdAgentContext], datasource_name: str, fixture_pathname: str, cloud: bool = False
10
18
  ) -> str:
11
- """Append existing fixture to a datasource
19
+ """Append a fixture file to a datasource
12
20
 
13
21
  Args:
14
22
  datasource_name: Name of the datasource to append fixture to
@@ -20,6 +28,14 @@ def append_file(
20
28
  """
21
29
  try:
22
30
  ctx.deps.thinking_animation.stop()
31
+ fixture_path_or_error = copy_fixture_to_project_folder_if_needed(ctx, fixture_pathname)
32
+
33
+ if isinstance(fixture_path_or_error, str):
34
+ ctx.deps.thinking_animation.start()
35
+ return fixture_path_or_error
36
+
37
+ fixture_path = fixture_path_or_error
38
+ fixture_pathname = os.path.relpath(fixture_path, ctx.deps.folder)
23
39
  cloud_or_local = "Cloud" if cloud else "Local"
24
40
  confirmation = show_confirmation(
25
41
  title=f"Append fixture {fixture_pathname} to datasource '{datasource_name}' in Tinybird {cloud_or_local}?",
@@ -11,7 +11,7 @@ def build(ctx: RunContext[TinybirdAgentContext]) -> str:
11
11
  try:
12
12
  ctx.deps.thinking_animation.stop()
13
13
  click.echo(FeedbackManager.highlight(message="» Building project..."))
14
- ctx.deps.build_project(test=False, silent=False)
14
+ ctx.deps.build_project(test=False, silent=False, load_fixtures=False)
15
15
  ctx.deps.thinking_animation.start()
16
16
  return "Project built successfully"
17
17
  except CLIBuildException as e:
@@ -52,7 +52,7 @@ def create_datafile(ctx: RunContext[TinybirdAgentContext], resource: Datafile) -
52
52
  folder_path.mkdir(parents=True, exist_ok=True)
53
53
  path.touch(exist_ok=True)
54
54
  path.write_text(resource.content)
55
- ctx.deps.build_project(test=False, silent=True)
55
+ ctx.deps.build_project(test=False, silent=True, load_fixtures=False)
56
56
  action_text = "created" if not exists else "updated"
57
57
  click.echo(FeedbackManager.success(message=f"✓ {resource.pathname} {action_text}"))
58
58
  ctx.deps.thinking_animation.start()
@@ -122,3 +122,59 @@ def create_file(ctx: RunContext[TinybirdAgentContext], path: str, content: str):
122
122
  f.write(content)
123
123
 
124
124
  return "ok"
125
+
126
+
127
+ def rename_datafile_or_fixture(ctx: RunContext[TinybirdAgentContext], path: str, new_path: str):
128
+ """Renames a datafile or fixture.
129
+
130
+ Args:
131
+ path (str): The path to the file to rename. Required.
132
+ new_path (str): The new path to the file. Required.
133
+ """
134
+ try:
135
+ ctx.deps.thinking_animation.stop()
136
+ confirmation = show_confirmation(
137
+ title=f"Rename '{path}' to '{new_path}'?",
138
+ skip_confirmation=ctx.deps.dangerously_skip_permissions,
139
+ )
140
+
141
+ if confirmation == "review":
142
+ feedback = show_input(ctx.deps.workspace_name)
143
+ ctx.deps.thinking_animation.start()
144
+ return f"User did not confirm the proposed changes and gave the following feedback: {feedback}"
145
+
146
+ click.echo(FeedbackManager.highlight(message=f"» Renaming file {path} to {new_path}..."))
147
+ new_path_full = Path(ctx.deps.folder) / new_path.removeprefix("/")
148
+
149
+ if new_path_full.exists():
150
+ click.echo(FeedbackManager.error(message=f"Error: File {new_path} already exists"))
151
+ ctx.deps.thinking_animation.start()
152
+ return f"Error: File {new_path} already exists"
153
+
154
+ parent_path = new_path_full.parent
155
+ parent_path.mkdir(parents=True, exist_ok=True)
156
+ os.rename(Path(ctx.deps.folder) / path.removeprefix("/"), new_path_full)
157
+ is_datafile = (".connection", ".datasource", ".pipe")
158
+
159
+ if new_path_full.suffix in is_datafile:
160
+ ctx.deps.build_project(test=False, silent=True, load_fixtures=False)
161
+
162
+ click.echo(FeedbackManager.success(message=f"✓ {new_path} created"))
163
+ ctx.deps.thinking_animation.start()
164
+ return f"Renamed file from {path} to {new_path}"
165
+ except AgentRunCancelled as e:
166
+ raise e
167
+ except FileNotFoundError:
168
+ ctx.deps.thinking_animation.start()
169
+ click.echo(FeedbackManager.error(message=f"Error: File {path} not found"))
170
+ return f"Error: File {path} not found (double check the file path)"
171
+ except CLIBuildException as e:
172
+ ctx.deps.thinking_animation.stop()
173
+ click.echo(FeedbackManager.error(message=e))
174
+ ctx.deps.thinking_animation.start()
175
+ return f"Error building project: {e}"
176
+ except Exception as e:
177
+ ctx.deps.thinking_animation.stop()
178
+ click.echo(FeedbackManager.error(message=e))
179
+ ctx.deps.thinking_animation.start()
180
+ return f"Error renaming {path} to {new_path}: {e}"
@@ -1,7 +1,9 @@
1
1
  import difflib
2
2
  import os
3
+ import shutil
3
4
  from contextlib import contextmanager
4
- from typing import Any, Callable, List, Literal, Optional, Tuple
5
+ from pathlib import Path
6
+ from typing import Any, Callable, List, Literal, Optional, Tuple, Union
5
7
 
6
8
  import click
7
9
  from prompt_toolkit import prompt as prompt_toolkit_prompt
@@ -17,8 +19,10 @@ from prompt_toolkit.patch_stdout import patch_stdout as pt_patch_stdout
17
19
  from prompt_toolkit.shortcuts import PromptSession
18
20
  from prompt_toolkit.styles import Style as PromptStyle
19
21
  from pydantic import BaseModel, Field
22
+ from pydantic_ai import RunContext
20
23
 
21
24
  from tinybird.tb.modules.agent.memory import load_history
25
+ from tinybird.tb.modules.feedback_manager import FeedbackManager
22
26
 
23
27
  try:
24
28
  from colorama import Back, Fore, Style, init
@@ -729,3 +733,57 @@ def show_confirmation(title: str, skip_confirmation: bool = False) -> Confirmati
729
733
  return "review"
730
734
 
731
735
  raise AgentRunCancelled(f"User cancelled the operation: {title}")
736
+
737
+
738
+ def copy_fixture_to_project_folder_if_needed(
739
+ ctx: RunContext[TinybirdAgentContext], fixture_pathname: str
740
+ ) -> Union[Path, str]:
741
+ """Copy a fixture data file to the project folder if it's outside the project folder"""
742
+ # Check if the path is absolute and outside the project folder
743
+ input_path = Path(fixture_pathname).expanduser()
744
+ project_folder = Path(ctx.deps.folder)
745
+
746
+ if input_path.is_absolute():
747
+ # Check if the file exists outside the project
748
+ if input_path.exists() and not _is_path_inside_project(input_path, project_folder):
749
+ # Ask for confirmation to copy the file
750
+ click.echo(FeedbackManager.highlight(message=f"» File {fixture_pathname} is outside the project folder."))
751
+
752
+ confirmation = show_confirmation(
753
+ title=f"Copy {input_path.name} to project folder for analysis?",
754
+ skip_confirmation=ctx.deps.dangerously_skip_permissions,
755
+ )
756
+
757
+ if confirmation == "review":
758
+ feedback = show_input(ctx.deps.workspace_name)
759
+ return f"User did not confirm file copy and gave the following feedback: {feedback}"
760
+
761
+ # Copy the file to the project folder
762
+ destination_path = project_folder / "fixtures" / input_path.name
763
+ destination_path.parent.mkdir(parents=True, exist_ok=True)
764
+
765
+ if destination_path.exists():
766
+ return f"Error: File {input_path.name} already exists in the project folder. Please rename the source file or remove the existing file first."
767
+
768
+ shutil.copy2(input_path, destination_path)
769
+ click.echo(FeedbackManager.success(message=f"✓ File copied to {destination_path.name}"))
770
+
771
+ # Update the path to the copied file
772
+ fixture_path = destination_path
773
+ else:
774
+ # File is absolute but inside project or doesn't exist
775
+ fixture_path = input_path
776
+ else:
777
+ # Relative path, treat as before
778
+ fixture_path = project_folder / fixture_pathname.lstrip("/")
779
+
780
+ return fixture_path
781
+
782
+
783
+ def _is_path_inside_project(file_path: Path, project_path: Path) -> bool:
784
+ """Check if a file path is inside the project folder"""
785
+ try:
786
+ file_path.resolve().relative_to(project_path.resolve())
787
+ return True
788
+ except ValueError:
789
+ return False
@@ -27,6 +27,7 @@ def process(
27
27
  silent: bool = False,
28
28
  build_status: Optional[BuildStatus] = None,
29
29
  exit_on_error: bool = True,
30
+ load_fixtures: bool = True,
30
31
  ) -> Optional[str]:
31
32
  time_start = time.time()
32
33
  build_failed = False
@@ -54,7 +55,7 @@ def process(
54
55
  build_status.error = None
55
56
  else:
56
57
  try:
57
- build_result = build_project(project, tb_client, silent)
58
+ build_result = build_project(project, tb_client, silent, load_fixtures)
58
59
  if build_status:
59
60
  build_status.building = False
60
61
  build_status.error = None
@@ -182,7 +183,9 @@ def show_data(tb_client: TinyB, filename: str, diff: Optional[str] = None):
182
183
  click.echo(FeedbackManager.gray(message="\nTest endpoint at ") + FeedbackManager.info(message=endpoint_url))
183
184
 
184
185
 
185
- def build_project(project: Project, tb_client: TinyB, silent: bool = False) -> Optional[bool]:
186
+ def build_project(
187
+ project: Project, tb_client: TinyB, silent: bool = False, load_fixtures: bool = True
188
+ ) -> Optional[bool]:
186
189
  MULTIPART_BOUNDARY_DATA_PROJECT = "data_project://"
187
190
  DATAFILE_TYPE_TO_CONTENT_TYPE = {
188
191
  ".datasource": "text/plain",
@@ -259,31 +262,32 @@ def build_project(project: Project, tb_client: TinyB, silent: bool = False) -> O
259
262
  echo_changes(project, new_connections, ".connection", "created")
260
263
  echo_changes(project, changed_connections, ".connection", "changed")
261
264
  echo_changes(project, deleted_connections, ".connection", "deleted")
262
- try:
263
- for filename in project_files:
264
- if filename.endswith(".datasource"):
265
- ds_path = Path(filename)
266
- ds_name = ds_path.stem
267
- fixture_folder = get_fixture_dir(project.folder)
268
- fixture_extensions = [FixtureExtension.NDJSON, FixtureExtension.CSV]
269
- fixture_path = next(
270
- (
271
- fixture_folder / f"{ds_name}{ext}"
272
- for ext in fixture_extensions
273
- if (fixture_folder / f"{ds_name}{ext}").exists()
274
- ),
275
- None,
276
- )
277
- if not fixture_path:
278
- sql_path = fixture_folder / f"{ds_name}.sql"
279
- if sql_path.exists():
280
- fixture_path = rebuild_fixture_sql(project, tb_client, str(sql_path))
281
-
282
- if fixture_path:
283
- append_fixture(tb_client, ds_name, str(fixture_path))
284
-
285
- except Exception as e:
286
- click.echo(FeedbackManager.error_exception(error=f"Error appending fixtures for '{ds_name}': {e}"))
265
+ if load_fixtures:
266
+ try:
267
+ for filename in project_files:
268
+ if filename.endswith(".datasource"):
269
+ ds_path = Path(filename)
270
+ ds_name = ds_path.stem
271
+ fixture_folder = get_fixture_dir(project.folder)
272
+ fixture_extensions = [FixtureExtension.NDJSON, FixtureExtension.CSV]
273
+ fixture_path = next(
274
+ (
275
+ fixture_folder / f"{ds_name}{ext}"
276
+ for ext in fixture_extensions
277
+ if (fixture_folder / f"{ds_name}{ext}").exists()
278
+ ),
279
+ None,
280
+ )
281
+ if not fixture_path:
282
+ sql_path = fixture_folder / f"{ds_name}.sql"
283
+ if sql_path.exists():
284
+ fixture_path = rebuild_fixture_sql(project, tb_client, str(sql_path))
285
+
286
+ if fixture_path:
287
+ append_fixture(tb_client, ds_name, str(fixture_path))
288
+
289
+ except Exception as e:
290
+ click.echo(FeedbackManager.error_exception(error=f"Error appending fixtures for '{ds_name}': {e}"))
287
291
 
288
292
  feedback = build.get("feedback", [])
289
293
  for f in feedback:
@@ -199,7 +199,7 @@ def cli(
199
199
  if "--help" in sys.argv or "-h" in sys.argv:
200
200
  return
201
201
 
202
- client = create_ctx_client(ctx, config, cloud, staging)
202
+ client = create_ctx_client(ctx, config, cloud, staging, show_warnings=version_warning)
203
203
 
204
204
  if client:
205
205
  ctx.ensure_object(dict)["client"] = client
@@ -372,7 +372,7 @@ def __hide_click_output() -> None:
372
372
  click.secho = silent_secho # type: ignore
373
373
 
374
374
 
375
- def create_ctx_client(ctx: Context, config: Dict[str, Any], cloud: bool, staging: bool):
375
+ def create_ctx_client(ctx: Context, config: Dict[str, Any], cloud: bool, staging: bool, show_warnings: bool = True):
376
376
  commands_without_ctx_client = [
377
377
  "auth",
378
378
  "check",
@@ -399,7 +399,8 @@ def create_ctx_client(ctx: Context, config: Dict[str, Any], cloud: bool, staging
399
399
  command_always_test = ["test"]
400
400
 
401
401
  if (
402
- (cloud or command in commands_always_cloud)
402
+ show_warnings
403
+ and (cloud or command in commands_always_cloud)
403
404
  and command not in commands_always_local
404
405
  and command not in command_always_test
405
406
  ):
@@ -415,10 +416,10 @@ def create_ctx_client(ctx: Context, config: Dict[str, Any], cloud: bool, staging
415
416
  if method:
416
417
  click.echo(FeedbackManager.gray(message=f"Authentication method: {method}"))
417
418
 
418
- return _get_tb_client(config.get("token", None), config["host"], staging=staging)
419
+ return _get_tb_client(config.get("token", ""), config["host"], staging=staging)
419
420
  local = command in commands_always_local
420
421
  test = command in command_always_test
421
- if not local and command not in commands_always_local and command:
422
+ if show_warnings and not local and command not in commands_always_local and command:
422
423
  click.echo(FeedbackManager.gray(message="Running against Tinybird Local"))
423
424
  return get_tinybird_local_client(config, test=test, staging=staging)
424
425
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: tinybird
3
- Version: 0.0.1.dev259
3
+ Version: 0.0.1.dev261
4
4
  Summary: Tinybird Command Line Tool
5
5
  Home-page: https://www.tinybird.co/docs/forward/commands
6
6
  Author: Tinybird
@@ -7,25 +7,25 @@ tinybird/prompts.py,sha256=HoDv9TxPiP8v2XoGTWYxP133dK9CEbXVv4XE5IT339c,45483
7
7
  tinybird/sql.py,sha256=BufnOgclQokDyihtuXesOwHBsebN6wRXIxO5wKRkOwE,48299
8
8
  tinybird/sql_template.py,sha256=AezE1o6_BzbHFi0J9OIqTrXQ5WvoX5eNVq4QCbFjGcs,100338
9
9
  tinybird/sql_template_fmt.py,sha256=KUHdj5rYCYm_rKKdXYSJAE9vIyXUQLB0YSZnUXHeBlY,10196
10
- tinybird/sql_toolset.py,sha256=M2rpLYkgV2W8NnYEYPC1tJdpy4uZHXVF64NBSKLQka4,19549
10
+ tinybird/sql_toolset.py,sha256=19WPr4S3SR--Iw-VPgmDnLmhOZyHhTxnj3-_Yq3OgEU,19767
11
11
  tinybird/syncasync.py,sha256=IPnOx6lMbf9SNddN1eBtssg8vCLHMt76SuZ6YNYm-Yk,27761
12
12
  tinybird/tornado_template.py,sha256=jjNVDMnkYFWXflmT8KU_Ssbo5vR8KQq3EJMk5vYgXRw,41959
13
13
  tinybird/ch_utils/constants.py,sha256=fPgZtwbr1ymxaW7uqVWHKmAbt7uGj3SxCCS3xsEMJqA,4151
14
14
  tinybird/ch_utils/engine.py,sha256=4X1B-iuhdW_mxKnX_m3iCsxgP9RPVgR75g7yH1vsJ6A,40851
15
- tinybird/datafile/common.py,sha256=naXznRSAceD0T05YNj0Fdk7k-6WfKA6dz2GT0tiHj9k,98505
15
+ tinybird/datafile/common.py,sha256=Xd_abjiTAK1oGxg2T59rLePNEdGMwSqe8PnoWEBJLyk,99161
16
16
  tinybird/datafile/exceptions.py,sha256=8rw2umdZjtby85QbuRKFO5ETz_eRHwUY5l7eHsy1wnI,556
17
17
  tinybird/datafile/parse_connection.py,sha256=tRyn2Rpr1TeWet5BXmMoQgaotbGdYep1qiTak_OqC5E,1825
18
18
  tinybird/datafile/parse_datasource.py,sha256=ssW8QeFSgglVFi3sDZj_HgkJiTJ2069v2JgqnH3CkDE,1825
19
19
  tinybird/datafile/parse_pipe.py,sha256=xf4m0Tw44QWJzHzAm7Z7FwUoUUtr7noMYjU1NiWnX0k,3880
20
- tinybird/tb/__cli__.py,sha256=D0G5HjONd9vfaiba7rVUC32Pic_cEL9FIR6-Z3uB_2c,247
20
+ tinybird/tb/__cli__.py,sha256=BEE_KvpSGXwAuGW52ybkT6HEEg736Uvf4PF1IWfSQ2Y,247
21
21
  tinybird/tb/check_pypi.py,sha256=Gp0HkHHDFMSDL6nxKlOY51z7z1Uv-2LRexNTZSHHGmM,552
22
22
  tinybird/tb/cli.py,sha256=FdDFEIayjmsZEVsVSSvRiVYn_FHOVg_zWQzchnzfWho,1008
23
23
  tinybird/tb/client.py,sha256=pJbdkWMXGAqKseNAvdsRRnl_c7I-DCMB0dWCQnG82nU,54146
24
24
  tinybird/tb/config.py,sha256=mhMTGnMB5KcxGoh3dewIr2Jjsa6pHE183gCPAQWyp6o,3973
25
25
  tinybird/tb/modules/build.py,sha256=efD-vamK1NPaDo9R86Hn8be2DYoW0Hh5bZiH7knK5dk,7790
26
- tinybird/tb/modules/build_common.py,sha256=rWhemU8bk0ZE2eiwZDaTmV9cPabDGGlyc2WnRxfhT0M,12859
26
+ tinybird/tb/modules/build_common.py,sha256=dthlaDn_CuwZnedQcUi7iIdDoHWfSbbbGQwiDgNcmC0,13062
27
27
  tinybird/tb/modules/cicd.py,sha256=0KLKccha9IP749QvlXBmzdWv1On3mFwMY4DUcJlBxiE,7326
28
- tinybird/tb/modules/cli.py,sha256=yLRYSk0t2WuyzBclb6HLEXHdRN8ktvypemWiTy5VmDc,16809
28
+ tinybird/tb/modules/cli.py,sha256=baeNPOu72L4aGI6ELsY8e2_eCq8ILOhTfd4qysz3KXk,16910
29
29
  tinybird/tb/modules/common.py,sha256=tj6DR2yOqMMQ0PILwFGXmMogxdrbQCgj36HdSM611rs,82657
30
30
  tinybird/tb/modules/config.py,sha256=gK7rgaWTDd4ZKCrNEg_Uemr26EQjqWt6TjyQKujxOws,11462
31
31
  tinybird/tb/modules/connection.py,sha256=-MY56NUAai6EMC4-wpi7bT0_nz_SA8QzTmHkV7HB1IQ,17810
@@ -69,18 +69,18 @@ tinybird/tb/modules/watch.py,sha256=No0bK1M1_3CYuMaIgylxf7vYFJ72lTJe3brz6xQ-mJo,
69
69
  tinybird/tb/modules/workspace.py,sha256=Q_8HcxMsNg8QG9aBlwcWS2umrDP5IkTIHqqz3sfmGuc,11341
70
70
  tinybird/tb/modules/workspace_members.py,sha256=5JdkJgfuEwbq-t6vxkBhYwgsiTDxF790wsa6Xfif9nk,8608
71
71
  tinybird/tb/modules/agent/__init__.py,sha256=i3oe3vDIWWPaicdCM0zs7D7BJ1W0k7th93ooskHAV00,54
72
- tinybird/tb/modules/agent/agent.py,sha256=1EGRxcIa3QTIO4EXrGAs8svBm4vlm3DiVCZEF5k7mFw,23773
72
+ tinybird/tb/modules/agent/agent.py,sha256=Ye2v5sFDDT1tL54XTcpOid9V8s9EG8hpuTCLNQyxCHk,24502
73
73
  tinybird/tb/modules/agent/animations.py,sha256=4WOC5_2BracttmMCrV0H91tXfWcUzQHBUaIJc5FA7tE,3490
74
74
  tinybird/tb/modules/agent/banner.py,sha256=7f97PeCPW-oW9mReQn3D0by8mnDhoc0VbfebEPRPI7c,3070
75
75
  tinybird/tb/modules/agent/memory.py,sha256=O6Kumn9AyKxcTkhI45yjAUZ3ZIAibLOcNWoiEuLYeqY,3245
76
76
  tinybird/tb/modules/agent/models.py,sha256=LW1D27gjcd_jwFmghEzteCgToDfodX2B6B5S8BYbysw,735
77
- tinybird/tb/modules/agent/prompts.py,sha256=-qks8FvYb8_WpchyjjWi9k8tQEYTjX9fEynuFrFLbzA,27727
78
- tinybird/tb/modules/agent/utils.py,sha256=tVymkZdDINcp5iLPrjEWEsoGmj4SjNbvgsXcYqsMrhU,26888
77
+ tinybird/tb/modules/agent/prompts.py,sha256=dFpbcKFb8u8O4elwV__RcKmjvTIG84mB4fzGnwVKtzg,28124
78
+ tinybird/tb/modules/agent/utils.py,sha256=1X1cjP607StB0NFuT2CsPO7fBU05eI7LGeib-mkFVms,29390
79
79
  tinybird/tb/modules/agent/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
80
- tinybird/tb/modules/agent/tools/analyze.py,sha256=fTKjMCq8ckvvqq8WqpE2aE_gO0XBM-eFPt75WTFC3ik,3565
81
- tinybird/tb/modules/agent/tools/append.py,sha256=b8nCO788MAk1ahITdNrrzMdUDhj-Y6KBwOCj4UmCQxg,5606
82
- tinybird/tb/modules/agent/tools/build.py,sha256=iSsdq42jpP8PC4SOUOLHYzuDrmqU3MDBYwhrT44tBFk,825
83
- tinybird/tb/modules/agent/tools/create_datafile.py,sha256=twsURhfIhQ5sjTwvsxR0rsERjfIcYxwDZpVNWG6MBPI,4423
80
+ tinybird/tb/modules/agent/tools/analyze.py,sha256=CR5LXg4fou-zYEksqnjpJ0icvxJVoKnTctoI1NRvqCM,3873
81
+ tinybird/tb/modules/agent/tools/append.py,sha256=XA8ZeqxZcRL_0ZCd5FyggpWeH53mwTMby4lHV8wQa7c,6039
82
+ tinybird/tb/modules/agent/tools/build.py,sha256=Hm-xDAP9ckMiKquT-DmDg5H0yxZefLOaWKANyoVSaEQ,846
83
+ tinybird/tb/modules/agent/tools/create_datafile.py,sha256=-Luv7GrZ3tEdEFaTFiN2_b2WEkTVl2Tp437qsvNEwZU,6902
84
84
  tinybird/tb/modules/agent/tools/deploy.py,sha256=2hKj6LMYiDea6ventsBjE6ArECGIryTdo3X-LYo5oZI,1248
85
85
  tinybird/tb/modules/agent/tools/deploy_check.py,sha256=2Wr9hQfKPlhqhumOv5TNl_xFctvdq_DHZ2dI2h_LggY,1048
86
86
  tinybird/tb/modules/agent/tools/diff_resource.py,sha256=_9xHcDzCTKk_E1wKQbuktVqV6U9sA0kqYaBxWvtliX0,2613
@@ -113,8 +113,8 @@ tinybird/tb_cli_modules/config.py,sha256=IsgdtFRnUrkY8-Zo32lmk6O7u3bHie1QCxLwgp4
113
113
  tinybird/tb_cli_modules/exceptions.py,sha256=pmucP4kTF4irIt7dXiG-FcnI-o3mvDusPmch1L8RCWk,3367
114
114
  tinybird/tb_cli_modules/regions.py,sha256=QjsL5H6Kg-qr0aYVLrvb1STeJ5Sx_sjvbOYO0LrEGMk,166
115
115
  tinybird/tb_cli_modules/telemetry.py,sha256=Hh2Io8ZPROSunbOLuMvuIFU4TqwWPmQTqal4WS09K1A,10449
116
- tinybird-0.0.1.dev259.dist-info/METADATA,sha256=WTSSsjGRnfYv-YUjstmzMF58_YHbuYjg8OWOoghGaVg,1733
117
- tinybird-0.0.1.dev259.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
118
- tinybird-0.0.1.dev259.dist-info/entry_points.txt,sha256=LwdHU6TfKx4Qs7BqqtaczEZbImgU7Abe9Lp920zb_fo,43
119
- tinybird-0.0.1.dev259.dist-info/top_level.txt,sha256=VqqqEmkAy7UNaD8-V51FCoMMWXjLUlR0IstvK7tJYVY,54
120
- tinybird-0.0.1.dev259.dist-info/RECORD,,
116
+ tinybird-0.0.1.dev261.dist-info/METADATA,sha256=UT0T98Tzjn1lyMQBycvZ9wJabGCNuawuklpyVQpoIhQ,1733
117
+ tinybird-0.0.1.dev261.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
118
+ tinybird-0.0.1.dev261.dist-info/entry_points.txt,sha256=LwdHU6TfKx4Qs7BqqtaczEZbImgU7Abe9Lp920zb_fo,43
119
+ tinybird-0.0.1.dev261.dist-info/top_level.txt,sha256=VqqqEmkAy7UNaD8-V51FCoMMWXjLUlR0IstvK7tJYVY,54
120
+ tinybird-0.0.1.dev261.dist-info/RECORD,,