tinybird 0.0.1.dev47__tar.gz → 0.0.1.dev49__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of tinybird might be problematic. Click here for more details.

Files changed (102) hide show
  1. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/PKG-INFO +1 -1
  2. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/client.py +1 -6
  3. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/prompts.py +8 -61
  4. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb/__cli__.py +2 -2
  5. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb/cli.py +0 -1
  6. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb/modules/build.py +22 -9
  7. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb/modules/cli.py +18 -63
  8. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb/modules/create.py +17 -12
  9. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb/modules/datafile/build.py +1 -2
  10. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb/modules/datafile/common.py +8 -10
  11. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb/modules/datasource.py +33 -0
  12. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb/modules/deployment.py +65 -14
  13. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb/modules/feedback_manager.py +1 -3
  14. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb/modules/local_common.py +8 -12
  15. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb/modules/login.py +1 -1
  16. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb/modules/mock.py +2 -2
  17. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb/modules/project.py +2 -2
  18. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb/modules/shell.py +50 -17
  19. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb/modules/test.py +9 -8
  20. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb/modules/workspace.py +1 -1
  21. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird.egg-info/PKG-INFO +1 -1
  22. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird.egg-info/SOURCES.txt +0 -1
  23. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird.egg-info/top_level.txt +1 -0
  24. tinybird-0.0.1.dev47/tinybird/tb/modules/update.py +0 -182
  25. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/setup.cfg +0 -0
  26. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/__cli__.py +0 -0
  27. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/ch_utils/constants.py +0 -0
  28. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/ch_utils/engine.py +0 -0
  29. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/check_pypi.py +0 -0
  30. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/config.py +0 -0
  31. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/connectors.py +0 -0
  32. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/context.py +0 -0
  33. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/datafile.py +0 -0
  34. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/datatypes.py +0 -0
  35. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/feedback_manager.py +0 -0
  36. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/git_settings.py +0 -0
  37. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/sql.py +0 -0
  38. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/sql_template.py +0 -0
  39. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/sql_template_fmt.py +0 -0
  40. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/sql_toolset.py +0 -0
  41. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/syncasync.py +0 -0
  42. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb/modules/auth.py +0 -0
  43. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb/modules/cicd.py +0 -0
  44. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb/modules/common.py +0 -0
  45. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb/modules/config.py +0 -0
  46. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb/modules/copy.py +0 -0
  47. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb/modules/datafile/build_common.py +0 -0
  48. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb/modules/datafile/build_datasource.py +0 -0
  49. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb/modules/datafile/build_pipe.py +0 -0
  50. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb/modules/datafile/diff.py +0 -0
  51. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb/modules/datafile/exceptions.py +0 -0
  52. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb/modules/datafile/fixture.py +0 -0
  53. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb/modules/datafile/format_common.py +0 -0
  54. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb/modules/datafile/format_datasource.py +0 -0
  55. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb/modules/datafile/format_pipe.py +0 -0
  56. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb/modules/datafile/parse_datasource.py +0 -0
  57. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb/modules/datafile/parse_pipe.py +0 -0
  58. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb/modules/datafile/pipe_checker.py +0 -0
  59. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb/modules/datafile/pull.py +0 -0
  60. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb/modules/endpoint.py +0 -0
  61. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb/modules/exceptions.py +0 -0
  62. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb/modules/fmt.py +0 -0
  63. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb/modules/job.py +0 -0
  64. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb/modules/llm.py +0 -0
  65. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb/modules/llm_utils.py +0 -0
  66. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb/modules/local.py +0 -0
  67. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb/modules/materialization.py +0 -0
  68. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb/modules/pipe.py +0 -0
  69. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb/modules/regions.py +0 -0
  70. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb/modules/table.py +0 -0
  71. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb/modules/tag.py +0 -0
  72. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb/modules/telemetry.py +0 -0
  73. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb/modules/tinyunit/tinyunit.py +0 -0
  74. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb/modules/tinyunit/tinyunit_lib.py +0 -0
  75. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb/modules/token.py +0 -0
  76. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb/modules/watch.py +0 -0
  77. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb/modules/workspace_members.py +0 -0
  78. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb_cli.py +0 -0
  79. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb_cli_modules/auth.py +0 -0
  80. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb_cli_modules/branch.py +0 -0
  81. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb_cli_modules/cicd.py +0 -0
  82. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb_cli_modules/cli.py +0 -0
  83. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb_cli_modules/common.py +0 -0
  84. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb_cli_modules/config.py +0 -0
  85. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb_cli_modules/connection.py +0 -0
  86. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb_cli_modules/datasource.py +0 -0
  87. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb_cli_modules/exceptions.py +0 -0
  88. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb_cli_modules/fmt.py +0 -0
  89. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb_cli_modules/job.py +0 -0
  90. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb_cli_modules/pipe.py +0 -0
  91. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb_cli_modules/regions.py +0 -0
  92. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb_cli_modules/tag.py +0 -0
  93. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb_cli_modules/telemetry.py +0 -0
  94. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb_cli_modules/test.py +0 -0
  95. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb_cli_modules/tinyunit/tinyunit.py +0 -0
  96. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb_cli_modules/tinyunit/tinyunit_lib.py +0 -0
  97. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb_cli_modules/workspace.py +0 -0
  98. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tb_cli_modules/workspace_members.py +0 -0
  99. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird/tornado_template.py +0 -0
  100. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird.egg-info/dependency_links.txt +0 -0
  101. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird.egg-info/entry_points.txt +0 -0
  102. {tinybird-0.0.1.dev47 → tinybird-0.0.1.dev49}/tinybird.egg-info/requires.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: tinybird
3
- Version: 0.0.1.dev47
3
+ Version: 0.0.1.dev49
4
4
  Summary: Tinybird Command Line Tool
5
5
  Home-page: https://www.tinybird.co/docs/cli/introduction.html
6
6
  Author: Tinybird
@@ -697,12 +697,7 @@ class TinyB:
697
697
  if local_host != self.host:
698
698
  return data
699
699
 
700
- local_workspaces = [
701
- x
702
- for x in data["workspaces"]
703
- if x["name"].startswith("Tinybird_Local_") and not x["name"].startswith("Tinybird_Local_Build_")
704
- ]
705
-
700
+ local_workspaces = [x for x in data["workspaces"] if not x["name"].startswith("Tinybird_Local_")]
706
701
  return {**data, "workspaces": local_workspaces}
707
702
 
708
703
  async def user_workspaces_and_branches(self):
@@ -651,32 +651,6 @@ DATASOURCE sales_by_hour
651
651
  """
652
652
 
653
653
 
654
- def ask_prompt(existing_resources: str) -> str:
655
- return """
656
- You are a Tinybird expert. You will be given a prompt to ask questions about Tinybird resources.
657
- <existing_resources>{existing_resources}</existing_resources>
658
- {datasource_instructions}
659
- {pipe_instructions}
660
- {sql_instructions}
661
- {datasource_example}
662
- {pipe_example}
663
- {copy_pipe_instructions}
664
- {materialized_pipe_instructions}
665
-
666
- The previous instructions are explanations of how things work in Tinybird. Answer in natural language.
667
-
668
- """.format(
669
- existing_resources=existing_resources,
670
- datasource_instructions=datasource_instructions,
671
- datasource_example=datasource_example,
672
- pipe_instructions=pipe_instructions,
673
- pipe_example=pipe_example,
674
- sql_instructions=sql_instructions,
675
- copy_pipe_instructions=copy_pipe_instructions,
676
- materialized_pipe_instructions=materialized_pipe_instructions,
677
- )
678
-
679
-
680
654
  datasource_instructions = """
681
655
  <datasource_file_instructions>
682
656
  - Content cannot be empty.
@@ -790,39 +764,6 @@ sql_instructions = """
790
764
  )
791
765
 
792
766
 
793
- def update_prompt(existing_resources: str) -> str:
794
- return """
795
- You are a Tinybird expert. You will be given a prompt to update the existing Tinybird resources: datasources and/or pipes.
796
- You will return the resources that need to be updated.
797
- <existing_resources>{existing_resources}</existing_resources>
798
- {datasource_instructions}
799
- {pipe_instructions}
800
- {sql_instructions}
801
- {datasource_example}
802
- {pipe_example}
803
- {copy_pipe_instructions}
804
- {materialized_pipe_instructions}
805
- Use the following format to generate the response and do not wrap it in any other text, including the <response> tag.
806
- <response>
807
- <resource>
808
- <type>[datasource or pipe]</type>
809
- <name>[resource name here]</name>
810
- <content>[resource content here]</content>
811
- </resource>
812
- </response>
813
-
814
- """.format(
815
- existing_resources=existing_resources,
816
- datasource_instructions=datasource_instructions,
817
- pipe_instructions=pipe_instructions,
818
- sql_instructions=sql_instructions,
819
- datasource_example=datasource_example,
820
- pipe_example=pipe_example,
821
- copy_pipe_instructions=copy_pipe_instructions,
822
- materialized_pipe_instructions=materialized_pipe_instructions,
823
- )
824
-
825
-
826
767
  def rules_prompt(source: Optional[str] = None) -> str:
827
768
  base_command = source or "tb"
828
769
  return """
@@ -837,7 +778,8 @@ You have commands at your disposal to develop a tinybird project:
837
778
  - {base_command} --build endpoint data <pipe_name>: to get the data of an endpoint. You can pass parameters to the endpoint like this: {base_command} --build endpoint data <pipe_name> --param1 value1 --param2 value2
838
779
  - {base_command} --build token ls: to list all the tokens
839
780
  There are other commands that you can use, but these are the most common ones. Run `{base_command} -h` to see all the commands if needed.
840
- When you need to check resources or data in the Tinybird environment that you updated with the build command, add always the --build flag before the command. Example: {base_command} --build datasource ls
781
+ When you need to work with resources or data in the Tinybird environment that you updated with the build command, add always the --build flag before the command. Example: {base_command} --build datasource ls
782
+ When you need to work with resources or data in production, add always the --prod flag before the command. Example: {base_command} --prod datasource ls
841
783
  </command_calling>
842
784
  <development_instructions>
843
785
  - When asking to create a tinybird data project, if the needed folders are not already created, use the following structure:
@@ -854,7 +796,12 @@ When you need to check resources or data in the Tinybird environment that you up
854
796
  - The format of the generated api endpoint urls is: http://localhost:80/v0/pipe/<pipe_name>.json?token=<token>
855
797
  - Before running the tests, remember to have the project built with `{base_command} build` with the latest changes.
856
798
  </development_instructions>
857
-
799
+ When asking for ingesting data, adding data or appending data do the following depending on the environment you want to work with:
800
+ <ingest_data_instructions>
801
+ - When building locally, create a .ndjson file with the data you want to ingest and do `{base_command} build` to ingest the data in the build env.
802
+ - When appending data in production, use `{base_command} --prod datasource append <datasource_name> <file_name>`
803
+ - When you have a response that says “there are rows in quarantine”, do `{base_command} --build|--prod datasource data <datasource_name>_quarantine` to understand what is the problem.
804
+ </ingest_data_instructions>
858
805
  <datasource_file_instructions>
859
806
  Follow these instructions when creating or updating .datasource files:
860
807
  {datasource_instructions}
@@ -4,5 +4,5 @@ __description__ = 'Tinybird Command Line Tool'
4
4
  __url__ = 'https://www.tinybird.co/docs/cli/introduction.html'
5
5
  __author__ = 'Tinybird'
6
6
  __author_email__ = 'support@tinybird.co'
7
- __version__ = '0.0.1.dev47'
8
- __revision__ = '05e3b46'
7
+ __version__ = '0.0.1.dev49'
8
+ __revision__ = 'a0a2378'
@@ -23,7 +23,6 @@ import tinybird.tb.modules.pipe
23
23
  import tinybird.tb.modules.tag
24
24
  import tinybird.tb.modules.test
25
25
  import tinybird.tb.modules.token
26
- import tinybird.tb.modules.update
27
26
  import tinybird.tb.modules.workspace
28
27
  import tinybird.tb.modules.workspace_members
29
28
 
@@ -1,6 +1,7 @@
1
1
  import asyncio
2
2
  import json
3
3
  import logging
4
+ import sys
4
5
  import threading
5
6
  import time
6
7
  from pathlib import Path
@@ -15,7 +16,6 @@ from tinybird.tb.modules.common import push_data
15
16
  from tinybird.tb.modules.datafile.build import folder_build
16
17
  from tinybird.tb.modules.datafile.fixture import build_fixture_name, get_fixture_dir
17
18
  from tinybird.tb.modules.feedback_manager import FeedbackManager
18
- from tinybird.tb.modules.local_common import get_tinybird_local_client
19
19
  from tinybird.tb.modules.project import Project
20
20
  from tinybird.tb.modules.shell import Shell, print_table_formatted
21
21
  from tinybird.tb.modules.watch import watch_project
@@ -29,7 +29,7 @@ def build(ctx: click.Context, watch: bool) -> None:
29
29
  Validate and build the project server side.
30
30
  """
31
31
  project: Project = ctx.ensure_object(dict)["project"]
32
- tb_client = asyncio.run(get_tinybird_local_client(str(project.path), build=True))
32
+ tb_client: TinyB = ctx.ensure_object(dict)["client"]
33
33
  click.echo(FeedbackManager.highlight_building_project())
34
34
  time_start = time.time()
35
35
 
@@ -40,15 +40,21 @@ def build(ctx: click.Context, watch: bool) -> None:
40
40
  build_project(project, tb_client, file_changed)
41
41
  try:
42
42
  if file_changed:
43
- asyncio.run(folder_build(project, filenames=[file_changed]))
44
- build_and_print_resource(tb_client, file_changed, diff)
43
+ asyncio.run(folder_build(project, tb_client, filenames=[file_changed]))
44
+ show_data(tb_client, file_changed, diff)
45
45
  except Exception:
46
46
  pass
47
47
 
48
- process()
49
- time_end = time.time()
50
- elapsed_time = time_end - time_start
51
- click.echo(FeedbackManager.success(message=f"\n✓ Build completed in {elapsed_time:.1f}s"))
48
+ try:
49
+ process()
50
+ except click.ClickException as e:
51
+ click.echo(e)
52
+ if not watch:
53
+ sys.exit(1)
54
+ else:
55
+ time_end = time.time()
56
+ elapsed_time = time_end - time_start
57
+ click.echo(FeedbackManager.success(message=f"\n✓ Build completed in {elapsed_time:.1f}s"))
52
58
 
53
59
  if watch:
54
60
  shell = Shell(project=project, tb_client=tb_client)
@@ -137,9 +143,16 @@ def build_project(project: Project, tb_client: TinyB, file_changed: Optional[str
137
143
 
138
144
  if fixture_path.exists():
139
145
  append_fixture(tb_client, ds_name, str(fixture_path))
146
+
140
147
  except Exception:
141
148
  pass
142
149
 
150
+ feedback = result.get("feedback", [])
151
+ for f in feedback:
152
+ click.echo(
153
+ FeedbackManager.warning(message=f"△ {f.get('level')}: {f.get('resource')}: {f.get('message')}")
154
+ )
155
+
143
156
  elif build_result == "failed":
144
157
  build_errors = result.get("errors")
145
158
  error = True
@@ -198,7 +211,7 @@ def rebuild_fixture(project: Project, tb_client: TinyB, fixture: str) -> None:
198
211
  click.echo(FeedbackManager.error_exception(error=e))
199
212
 
200
213
 
201
- def build_and_print_resource(tb_client: TinyB, filename: str, diff: Optional[str] = None):
214
+ def show_data(tb_client: TinyB, filename: str, diff: Optional[str] = None):
202
215
  table_name = diff
203
216
  resource_path = Path(filename)
204
217
  resource_name = resource_path.stem
@@ -7,7 +7,6 @@ import json
7
7
  import logging
8
8
  import os
9
9
  from os import getcwd
10
- from pathlib import Path
11
10
  from typing import Any, Callable, Dict, List, Optional, Tuple, Union
12
11
 
13
12
  import click
@@ -20,7 +19,6 @@ from tinybird.client import (
20
19
  TinyB,
21
20
  )
22
21
  from tinybird.config import get_config
23
- from tinybird.prompts import ask_prompt
24
22
  from tinybird.tb import __cli__
25
23
  from tinybird.tb.modules.common import (
26
24
  CatchAuthExceptions,
@@ -37,7 +35,6 @@ from tinybird.tb.modules.datafile.build import build_graph
37
35
  from tinybird.tb.modules.datafile.diff import diff_command
38
36
  from tinybird.tb.modules.datafile.pull import folder_pull
39
37
  from tinybird.tb.modules.feedback_manager import FeedbackManager
40
- from tinybird.tb.modules.llm import LLM
41
38
  from tinybird.tb.modules.local_common import get_tinybird_local_client
42
39
  from tinybird.tb.modules.project import Project
43
40
 
@@ -124,13 +121,9 @@ async def cli(
124
121
 
125
122
  logging.debug("debug enabled")
126
123
 
127
- skip_client = ctx.invoked_subcommand in ["auth", "check", "login", "local", "build"]
128
- client = await create_ctx_client(config, prod, build, skip_client, project)
124
+ client = await create_ctx_client(ctx, config, prod, build, project)
129
125
 
130
126
  if client:
131
- if not build:
132
- target = config.get("name", "production") if prod else "Tinybird local"
133
- click.echo(FeedbackManager.gray(message=f"Running against {target}\n"))
134
127
  ctx.ensure_object(dict)["client"] = client
135
128
 
136
129
  ctx.ensure_object(dict)["project"] = project
@@ -358,56 +351,6 @@ async def sql(
358
351
  click.echo(FeedbackManager.info_no_rows())
359
352
 
360
353
 
361
- @cli.command(hidden=True)
362
- @click.argument("prompt")
363
- @click.option("--folder", default=os.getcwd(), help="The folder to use for the project")
364
- @coro
365
- async def ask(prompt: str, folder: str) -> None:
366
- """Ask things about your data project."""
367
- try:
368
- config = CLIConfig.get_project_config(folder)
369
- user_token = config.get_user_token()
370
-
371
- if not user_token:
372
- raise CLIException("This action requires authentication. Run 'tb login' first.")
373
-
374
- datasource_paths = [
375
- Path(folder) / "datasources" / f
376
- for f in os.listdir(Path(folder) / "datasources")
377
- if f.endswith(".datasource")
378
- ]
379
- pipe_folders = ["endpoints", "pipes", "copies", "materializations", "sinks"]
380
- pipe_paths = [
381
- Path(folder) / pipe_folder / f
382
- for pipe_folder in pipe_folders
383
- if (Path(folder) / pipe_folder).exists()
384
- for f in os.listdir(Path(folder) / pipe_folder)
385
- if f.endswith(".pipe")
386
- ]
387
- resources_xml = "\n".join(
388
- [
389
- f"<resource><type>{resource_type}</type><name>{resource_name}</name><content>{resource_content}</content></resource>"
390
- for resource_type, resource_name, resource_content in [
391
- ("datasource", ds.stem, ds.read_text()) for ds in datasource_paths
392
- ]
393
- + [
394
- (
395
- "pipe",
396
- pipe.stem,
397
- pipe.read_text(),
398
- )
399
- for pipe in pipe_paths
400
- ]
401
- ]
402
- )
403
-
404
- client = config.get_client()
405
- llm = LLM(user_token=user_token, host=client.host)
406
- click.echo(llm.ask(system_prompt=ask_prompt(resources_xml), prompt=prompt))
407
- except Exception as e:
408
- raise CLIException(FeedbackManager.error_exception(error=e))
409
-
410
-
411
354
  def __patch_click_output():
412
355
  import re
413
356
 
@@ -425,7 +368,7 @@ def __patch_click_output():
425
368
  if isinstance(substitution, str):
426
369
  msg = re.sub(pattern, substitution, str(msg))
427
370
  else:
428
- msg = re.sub(pattern, lambda m: substitution(m.group(0)), str(msg)) # noqa
371
+ msg = re.sub(pattern, lambda m: substitution(m.group(0)), str(msg)) # noqa: B023
429
372
  return msg
430
373
 
431
374
  def _obfuscate_echo(msg: Any, *args: Any, **kwargs: Any) -> None:
@@ -445,11 +388,23 @@ def __unpatch_click_output():
445
388
  click.secho = __old_click_secho
446
389
 
447
390
 
448
- async def create_ctx_client(config: Dict[str, Any], prod: bool, build: bool, skip_client: bool, project: Project):
449
- if skip_client:
391
+ async def create_ctx_client(ctx: Context, config: Dict[str, Any], prod: bool, build: bool, project: Project):
392
+ commands_without_ctx_client = ["auth", "check", "login", "local"]
393
+ command = ctx.invoked_subcommand
394
+ if command in commands_without_ctx_client:
450
395
  return None
451
396
 
452
- if prod:
397
+ commands_always_prod = ["pull"]
398
+ commands_always_build = ["build", "test"]
399
+ commands_always_local = ["create", "mock"]
400
+ if (
401
+ (prod or command in commands_always_prod)
402
+ and command not in commands_always_build
403
+ and command not in commands_always_local
404
+ ):
405
+ click.echo(FeedbackManager.gray(message=f"Running against {config.get('name') or 'production'}"))
453
406
  return _get_tb_client(config.get("token", None), config["host"])
454
-
407
+ build = command in commands_always_build or build
408
+ if not build and command not in commands_always_local and command not in commands_always_build:
409
+ click.echo(FeedbackManager.gray(message="Running against Tinybird local\n"))
455
410
  return await get_tinybird_local_client(str(project.path), build=build)
@@ -17,7 +17,6 @@ from tinybird.tb.modules.exceptions import CLIException
17
17
  from tinybird.tb.modules.feedback_manager import FeedbackManager
18
18
  from tinybird.tb.modules.llm import LLM
19
19
  from tinybird.tb.modules.llm_utils import extract_xml, parse_xml
20
- from tinybird.tb.modules.local_common import get_tinybird_local_client
21
20
  from tinybird.tb.modules.project import Project
22
21
 
23
22
 
@@ -41,8 +40,8 @@ from tinybird.tb.modules.project import Project
41
40
  help="Folder where datafiles will be placed",
42
41
  )
43
42
  @click.option("--rows", type=int, default=10, help="Number of events to send")
44
- @click.option("--cursor", default=False, is_flag=True, help="Create .cursorrules file with Tinybird rules")
45
43
  @click.option("--source", type=str, default="tb", help="Source of the command")
44
+ @click.option("--skip", is_flag=True, default=False, help="Skip following up on the generated data")
46
45
  @click.pass_context
47
46
  @coro
48
47
  async def create(
@@ -51,11 +50,12 @@ async def create(
51
50
  prompt: Optional[str],
52
51
  folder: Optional[str],
53
52
  rows: int,
54
- cursor: bool,
55
53
  source: str,
54
+ skip: bool,
56
55
  ) -> None:
57
56
  """Initialize a new project."""
58
57
  project: Project = ctx.ensure_object(dict)["project"]
58
+ local_client: TinyB = ctx.ensure_object(dict)["client"]
59
59
  folder = folder or getcwd()
60
60
  folder_path = Path(folder)
61
61
  if not folder_path.exists():
@@ -78,7 +78,6 @@ async def create(
78
78
  )
79
79
  )
80
80
  return
81
- local_client = await get_tinybird_local_client(folder)
82
81
 
83
82
  if not validate_project_structure(folder):
84
83
  click.echo(FeedbackManager.highlight(message="\n» Creating new project structure..."))
@@ -88,7 +87,7 @@ async def create(
88
87
  result = ""
89
88
  if data or prompt:
90
89
  click.echo(FeedbackManager.highlight(message="\n» Creating resources..."))
91
- result = await create_resources(local_client, tb_client, user_token, data, prompt, folder)
90
+ result = await create_resources(local_client, tb_client, user_token, data, prompt, folder, skip)
92
91
  click.echo(FeedbackManager.success(message="✓ Done!\n"))
93
92
 
94
93
  if not already_has_cicd(folder):
@@ -97,6 +96,11 @@ async def create(
97
96
  await init_cicd(data_project_dir=os.path.relpath(folder))
98
97
  click.echo(FeedbackManager.success(message="✓ Done!\n"))
99
98
 
99
+ if not already_has_cursor_rules(folder):
100
+ click.echo(FeedbackManager.highlight(message="\n» Creating .cursorrules..."))
101
+ create_rules(folder, source, "cursor")
102
+ click.echo(FeedbackManager.success(message="✓ Done!\n"))
103
+
100
104
  if should_generate_fixtures(result):
101
105
  click.echo(FeedbackManager.highlight(message="\n» Generating fixtures..."))
102
106
 
@@ -130,12 +134,6 @@ async def create(
130
134
  if data:
131
135
  persist_fixture(fixture_name, data, folder)
132
136
  click.echo(FeedbackManager.info(message=f"✓ /fixtures/{datasource_name}"))
133
-
134
- if cursor:
135
- click.echo(FeedbackManager.highlight(message="\n» Creating .cursorrules..."))
136
- create_rules(folder, source, "cursor")
137
- click.echo(FeedbackManager.success(message="✓ Done!\n"))
138
-
139
137
  except Exception as e:
140
138
  click.echo(FeedbackManager.error(message=f"Error: {str(e)}"))
141
139
 
@@ -158,6 +156,11 @@ def already_has_cicd(folder: str) -> bool:
158
156
  return any((Path(folder) / path).exists() for path in ci_cd_paths)
159
157
 
160
158
 
159
+ def already_has_cursor_rules(folder: str) -> bool:
160
+ cursor_rules_paths = (".cursorrules", ".windsurfrules")
161
+ return any((Path(folder) / path).exists() for path in cursor_rules_paths)
162
+
163
+
161
164
  def create_project_structure(folder: str):
162
165
  folder_path = Path(folder)
163
166
  for x in PROJECT_PATHS:
@@ -176,6 +179,7 @@ async def create_resources(
176
179
  data: Optional[str],
177
180
  prompt: Optional[str],
178
181
  folder: str,
182
+ skip: bool,
179
183
  ):
180
184
  result = ""
181
185
  folder_path = Path(folder)
@@ -285,7 +289,8 @@ TYPE ENDPOINT
285
289
  content = pipe["content"].replace("```", "")
286
290
  pipe_path = generate_pipe_file(pipe["name"], content, folder)
287
291
  generated_paths.append(pipe_path)
288
-
292
+ if skip:
293
+ break
289
294
  iterations += 1
290
295
 
291
296
  if iterations == 10:
@@ -42,12 +42,12 @@ from tinybird.tb.modules.datafile.exceptions import AlreadyExistsException, Incl
42
42
  from tinybird.tb.modules.datafile.parse_datasource import parse_datasource
43
43
  from tinybird.tb.modules.datafile.parse_pipe import parse_pipe
44
44
  from tinybird.tb.modules.feedback_manager import FeedbackManager
45
- from tinybird.tb.modules.local_common import get_tinybird_local_client
46
45
  from tinybird.tb.modules.project import Project
47
46
 
48
47
 
49
48
  async def folder_build(
50
49
  project: Project,
50
+ tb_client: TinyB,
51
51
  filenames: Optional[List[str]] = None,
52
52
  is_internal: bool = False,
53
53
  is_vendor: bool = False,
@@ -67,7 +67,6 @@ async def folder_build(
67
67
  fork = False
68
68
  release_created = False
69
69
  folder = str(project.path)
70
- tb_client = await get_tinybird_local_client(folder)
71
70
  datasources: List[Dict[str, Any]] = await tb_client.datasources()
72
71
  pipes: List[Dict[str, Any]] = await tb_client.pipes(dependencies=True)
73
72
 
@@ -207,7 +207,7 @@ class Datafile:
207
207
  # [x] SQL in all nodes
208
208
  # [x] Materialized nodes have target datasource
209
209
  # [x] Only one materialized node
210
- # [ ] Only one node of any specific type
210
+ # [x] Only one node of any specific type
211
211
  # [ ] ...
212
212
  repeated_node_names = [
213
213
  name for name, count in filter(lambda x: x[1] > 1, Counter(n["name"] for n in self.nodes).items())
@@ -219,16 +219,14 @@ class Datafile:
219
219
  for node in self.nodes:
220
220
  if "sql" not in node:
221
221
  raise DatafileValidationError(f"SQL missing for node {repr(node['name'])}")
222
- materialized_nodes_count = 0
222
+ non_standard_nodes_count = 0
223
223
  for node in self.nodes:
224
- if node.get("type", "").lower() == "materialized":
225
- materialized_nodes_count += 1
226
- if materialized_nodes_count > 1:
227
- raise DatafileValidationError("Multiple materialized nodes in pipe. There can only be one")
228
- if "datasource" not in node:
229
- raise DatafileValidationError(
230
- f"Materialized node {repr(node['name'])} missing target datasource"
231
- )
224
+ if node.get("type", "").lower() not in {PipeNodeTypes.STANDARD, ""}:
225
+ non_standard_nodes_count += 1
226
+ if non_standard_nodes_count > 1:
227
+ raise DatafileValidationError("Multiple non-standard nodes in pipe. There can only be one")
228
+ if node.get("type", "").lower() == PipeNodeTypes.MATERIALIZED and "datasource" not in node:
229
+ raise DatafileValidationError(f"Materialized node {repr(node['name'])} missing target datasource")
232
230
  elif self.kind == DatafileKind.datasource:
233
231
  # TODO(eclbg):
234
232
  # [x] Just one node
@@ -358,3 +358,36 @@ async def datasource_delete_rows(ctx, datasource_name, sql_condition, yes, wait,
358
358
  raise CLIDatasourceException(FeedbackManager.error_datasource_does_not_exist(datasource=datasource_name))
359
359
  except Exception as e:
360
360
  raise CLIDatasourceException(FeedbackManager.error_exception(error=e))
361
+
362
+
363
+ @datasource.command(
364
+ name="data",
365
+ context_settings=dict(
366
+ allow_extra_args=True,
367
+ ignore_unknown_options=True,
368
+ ),
369
+ )
370
+ @click.argument("datasource")
371
+ @click.option("--limit", type=int, default=5, help="Limit the number of rows to return")
372
+ @click.pass_context
373
+ @coro
374
+ async def datasource_data(ctx: Context, datasource: str, limit: int):
375
+ """Print data returned by an endpoint
376
+
377
+ Syntax: tb datasource data <datasource_name>
378
+ """
379
+
380
+ client: TinyB = ctx.ensure_object(dict)["client"]
381
+ try:
382
+ res = await client.query(f"SELECT * FROM {datasource} LIMIT {limit} FORMAT JSON")
383
+ except AuthNoTokenException:
384
+ raise
385
+ except Exception as e:
386
+ raise CLIDatasourceException(FeedbackManager.error_exception(error=str(e)))
387
+
388
+ if not res["data"]:
389
+ click.echo(FeedbackManager.info_no_rows())
390
+ else:
391
+ echo_safe_humanfriendly_tables_format_smart_table(
392
+ data=[d.values() for d in res["data"]], column_names=res["data"][0].keys()
393
+ )