tinybird 0.0.1.dev290__tar.gz → 0.0.1.dev292__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 (150) hide show
  1. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/PKG-INFO +1 -1
  2. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/context.py +3 -2
  3. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/datafile/common.py +2 -2
  4. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/sql.py +30 -22
  5. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/__cli__.py +2 -2
  6. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/agent/agent.py +10 -0
  7. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/agent/explore_agent.py +5 -0
  8. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/agent/mock_agent.py +5 -1
  9. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/agent/prompts.py +49 -2
  10. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/agent/tools/run_command.py +9 -0
  11. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/cli.py +26 -3
  12. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/datafile/diff.py +2 -2
  13. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/datafile/pull.py +51 -38
  14. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/deployment_common.py +8 -0
  15. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/project.py +10 -1
  16. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird.egg-info/PKG-INFO +1 -1
  17. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/setup.cfg +0 -0
  18. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/__cli__.py +0 -0
  19. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/ch_utils/constants.py +0 -0
  20. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/ch_utils/engine.py +0 -0
  21. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/check_pypi.py +0 -0
  22. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/client.py +0 -0
  23. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/config.py +0 -0
  24. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/connectors.py +0 -0
  25. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/datafile/exceptions.py +0 -0
  26. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/datafile/parse_connection.py +0 -0
  27. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/datafile/parse_datasource.py +0 -0
  28. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/datafile/parse_pipe.py +0 -0
  29. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/datatypes.py +0 -0
  30. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/feedback_manager.py +0 -0
  31. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/git_settings.py +0 -0
  32. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/prompts.py +0 -0
  33. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/service_datasources.py +0 -0
  34. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/sql_template.py +0 -0
  35. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/sql_template_fmt.py +0 -0
  36. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/sql_toolset.py +0 -0
  37. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/syncasync.py +0 -0
  38. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/check_pypi.py +0 -0
  39. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/cli.py +0 -0
  40. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/client.py +0 -0
  41. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/config.py +0 -0
  42. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/agent/__init__.py +0 -0
  43. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/agent/animations.py +0 -0
  44. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/agent/banner.py +0 -0
  45. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/agent/command_agent.py +0 -0
  46. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/agent/compactor.py +0 -0
  47. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/agent/file_agent.py +0 -0
  48. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/agent/memory.py +0 -0
  49. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/agent/models.py +0 -0
  50. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/agent/testing_agent.py +0 -0
  51. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/agent/tools/__init__.py +0 -0
  52. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/agent/tools/analyze.py +0 -0
  53. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/agent/tools/append.py +0 -0
  54. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/agent/tools/build.py +0 -0
  55. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/agent/tools/datafile.py +0 -0
  56. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/agent/tools/deploy.py +0 -0
  57. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/agent/tools/deploy_check.py +0 -0
  58. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/agent/tools/diff_resource.py +0 -0
  59. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/agent/tools/execute_query.py +0 -0
  60. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/agent/tools/file.py +0 -0
  61. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/agent/tools/get_endpoint_stats.py +0 -0
  62. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/agent/tools/get_openapi_definition.py +0 -0
  63. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/agent/tools/mock.py +0 -0
  64. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/agent/tools/plan.py +0 -0
  65. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/agent/tools/request_endpoint.py +0 -0
  66. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/agent/tools/secret.py +0 -0
  67. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/agent/tools/test.py +0 -0
  68. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/agent/utils.py +0 -0
  69. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/build.py +0 -0
  70. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/build_common.py +0 -0
  71. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/cicd.py +0 -0
  72. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/common.py +0 -0
  73. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/config.py +0 -0
  74. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/connection.py +0 -0
  75. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/copy.py +0 -0
  76. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/create.py +0 -0
  77. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/datafile/build.py +0 -0
  78. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/datafile/build_common.py +0 -0
  79. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/datafile/build_datasource.py +0 -0
  80. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/datafile/build_pipe.py +0 -0
  81. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/datafile/fixture.py +0 -0
  82. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/datafile/format_common.py +0 -0
  83. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/datafile/format_datasource.py +0 -0
  84. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/datafile/format_pipe.py +0 -0
  85. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/datafile/pipe_checker.py +0 -0
  86. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/datafile/playground.py +0 -0
  87. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/datasource.py +0 -0
  88. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/deployment.py +0 -0
  89. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/deprecations.py +0 -0
  90. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/dev_server.py +0 -0
  91. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/endpoint.py +0 -0
  92. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/exceptions.py +0 -0
  93. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/feedback_manager.py +0 -0
  94. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/info.py +0 -0
  95. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/infra.py +0 -0
  96. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/job.py +0 -0
  97. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/llm.py +0 -0
  98. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/llm_utils.py +0 -0
  99. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/local.py +0 -0
  100. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/local_common.py +0 -0
  101. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/login.py +0 -0
  102. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/login_common.py +0 -0
  103. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/logout.py +0 -0
  104. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/materialization.py +0 -0
  105. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/mock.py +0 -0
  106. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/mock_common.py +0 -0
  107. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/open.py +0 -0
  108. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/pipe.py +0 -0
  109. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/regions.py +0 -0
  110. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/secret.py +0 -0
  111. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/secret_common.py +0 -0
  112. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/shell.py +0 -0
  113. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/sink.py +0 -0
  114. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/table.py +0 -0
  115. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/telemetry.py +0 -0
  116. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/test.py +0 -0
  117. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/test_common.py +0 -0
  118. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/tinyunit/tinyunit.py +0 -0
  119. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/tinyunit/tinyunit_lib.py +0 -0
  120. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/token.py +0 -0
  121. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/watch.py +0 -0
  122. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/workspace.py +0 -0
  123. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb/modules/workspace_members.py +0 -0
  124. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb_cli.py +0 -0
  125. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb_cli_modules/auth.py +0 -0
  126. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb_cli_modules/branch.py +0 -0
  127. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb_cli_modules/cicd.py +0 -0
  128. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb_cli_modules/cli.py +0 -0
  129. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb_cli_modules/common.py +0 -0
  130. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb_cli_modules/config.py +0 -0
  131. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb_cli_modules/connection.py +0 -0
  132. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb_cli_modules/datasource.py +0 -0
  133. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb_cli_modules/exceptions.py +0 -0
  134. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb_cli_modules/fmt.py +0 -0
  135. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb_cli_modules/job.py +0 -0
  136. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb_cli_modules/pipe.py +0 -0
  137. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb_cli_modules/regions.py +0 -0
  138. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb_cli_modules/tag.py +0 -0
  139. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb_cli_modules/telemetry.py +0 -0
  140. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb_cli_modules/test.py +0 -0
  141. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb_cli_modules/tinyunit/tinyunit.py +0 -0
  142. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb_cli_modules/tinyunit/tinyunit_lib.py +0 -0
  143. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb_cli_modules/workspace.py +0 -0
  144. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tb_cli_modules/workspace_members.py +0 -0
  145. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird/tornado_template.py +0 -0
  146. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird.egg-info/SOURCES.txt +0 -0
  147. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird.egg-info/dependency_links.txt +0 -0
  148. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird.egg-info/entry_points.txt +0 -0
  149. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird.egg-info/requires.txt +0 -0
  150. {tinybird-0.0.1.dev290 → tinybird-0.0.1.dev292}/tinybird.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: tinybird
3
- Version: 0.0.1.dev290
3
+ Version: 0.0.1.dev292
4
4
  Summary: Tinybird Command Line Tool
5
5
  Home-page: https://www.tinybird.co/docs/forward/commands
6
6
  Author: Tinybird
@@ -3,10 +3,11 @@ from typing import TYPE_CHECKING
3
3
 
4
4
  # Avoid circular import error
5
5
  if TYPE_CHECKING:
6
- from tinybird.user import User
6
+ from hfi.hfi_workspace_data import HfiWorkspaceData
7
+
7
8
 
8
9
  workspace_id: ContextVar[str] = ContextVar("workspace_id")
9
- workspace: ContextVar["User"] = ContextVar("workspace")
10
+ hfi_workspace_data: ContextVar["HfiWorkspaceData"] = ContextVar("hfi_workspace_data")
10
11
  table_id: ContextVar[str] = ContextVar("table_id")
11
12
  hfi_frequency: ContextVar[float] = ContextVar("hfi_frequency")
12
13
  hfi_frequency_gatherer: ContextVar[float] = ContextVar("hfi_frequency_gatherer")
@@ -1870,7 +1870,7 @@ def parse(
1870
1870
 
1871
1871
  for entries in args:
1872
1872
  # In case they specify multiple workspaces, handle both line-separated and comma-separated values
1873
- lines = entries.splitlines()
1873
+ lines = _unquote(entries).splitlines()
1874
1874
  for line in lines:
1875
1875
  # Split by comma and strip whitespace from each workspace name
1876
1876
  workspaces = [workspace.strip().rstrip(",") for workspace in line.split(",") if workspace.strip()]
@@ -1957,7 +1957,7 @@ def parse(
1957
1957
  "import_query": assign_var("import_query"), # Deprecated, BQ and SFK
1958
1958
  "import_table_arn": assign_var("import_table_arn"), # Only for DynamoDB
1959
1959
  "import_export_bucket": assign_var("import_export_bucket"), # For DynamoDB
1960
- "shared_with": shared_with, # Not supported yet
1960
+ "shared_with": shared_with,
1961
1961
  "export_service": export_service, # Deprecated
1962
1962
  "forward_query": sql("forward_query"),
1963
1963
  "backfill": assign_var("backfill", allowed_values={"skip"}),
@@ -7,6 +7,9 @@ from typing import Any, Dict, Iterable, List, Optional
7
7
 
8
8
  valid_chars_name: str = string.ascii_letters + string.digits + "._`*<>+-'"
9
9
  valid_chars_fn: str = valid_chars_name + "[](),=!?:/ \n\t\r"
10
+ # Use sets for O(1) membership checks in hot loops
11
+ _VALID_CHARS_NAME_SET = set(valid_chars_name)
12
+ _VALID_CHARS_FN_SET = set(valid_chars_fn)
10
13
 
11
14
  INDEX_WHITELIST = ["minmax", "set", "bloom_filter", "ngrambf_v1", "tokenbf_v1"]
12
15
  INDEX_SUPPORTED_TYPES = {
@@ -28,6 +31,15 @@ INDEX_SUPPORTED_TYPES = {
28
31
  "ngrambf_v1": ["String", "FixedString", "Map"],
29
32
  }
30
33
 
34
+ # Precompiled regexes used across functions to avoid recompilation overhead
35
+ _RE_FORMAT = re.compile(r"\s+format\s+(\w+)\s*$", re.IGNORECASE)
36
+ _RE_REMOVE_FORMAT = re.compile(r"\s+(format)\s+(\w+)\s*$", re.IGNORECASE)
37
+ _RE_TRY_FIX_NULLABLE_SAF = re.compile(r"SimpleAggregateFunction\((\w+),\s*(?!(?:Nullable))([\w,.()]+)\)")
38
+ _RE_INDEX_ENTRY = re.compile(
39
+ r"(\w+)\s+([\w\s*\[\]\*\(\),\'\"-><.]+)\s+TYPE\s+(\w+)(?:\(([\w\s*.,]+)\))?(?:\s+GRANULARITY\s+(\d+))?"
40
+ )
41
+ _RE_REPLICATED_MT = re.compile(r"Replicated(.*)MergeTree\(([^\)]*)\)(.*)")
42
+
31
43
 
32
44
  @dataclass
33
45
  class TableIndex:
@@ -137,10 +149,9 @@ def get_format(sql: str) -> Optional[str]:
137
149
  >>> get_format('select * from test formAt JSON')
138
150
  'JSON'
139
151
  """
140
- FORMAT_RE = r"\s+format\s+(\w+)\s*$"
141
152
  sql = sql.strip()
142
- format = re.findall(FORMAT_RE, sql, re.IGNORECASE)
143
- return format[0] if format else None
153
+ match = _RE_FORMAT.findall(sql)
154
+ return match[0] if match else None
144
155
 
145
156
 
146
157
  def get_format_group(sql: str) -> str:
@@ -151,10 +162,9 @@ def get_format_group(sql: str) -> str:
151
162
  >>> get_format_group('select * from test formAt JSON')
152
163
  ' formAt JSON'
153
164
  """
154
- FORMAT_RE = r"\s+format\s+(\w+)\s*$"
155
165
  sql = sql.strip()
156
- format = re.search(FORMAT_RE, sql, re.IGNORECASE)
157
- return format.group() if format else ""
166
+ match = _RE_FORMAT.search(sql)
167
+ return match.group() if match else ""
158
168
 
159
169
 
160
170
  def wrap_finalize_aggregation(sql: str, describe_result: Dict[str, Any], fm_group: Optional[str] = None) -> str:
@@ -186,9 +196,8 @@ def remove_format(sql: str) -> str:
186
196
  >>> remove_format('select * from test formAt JSON')
187
197
  'select * from test'
188
198
  """
189
- FORMAT_RE = r"\s+(format)\s+(\w+)\s*$"
190
199
  sql = sql.strip()
191
- return re.sub(FORMAT_RE, "", sql, flags=re.IGNORECASE)
200
+ return _RE_REMOVE_FORMAT.sub("", sql)
192
201
 
193
202
 
194
203
  def col_name(name: str, backquotes: bool = True) -> str:
@@ -220,7 +229,7 @@ def try_to_fix_nullable_in_simple_aggregating_function(t: str) -> Optional[str]:
220
229
  # as it is done with other aggregate functions.
221
230
  # If not, the aggregation could return incorrect results.
222
231
  result = None
223
- if match := re.search(r"SimpleAggregateFunction\((\w+),\s*(?!(?:Nullable))([\w,.()]+)\)", t):
232
+ if match := _RE_TRY_FIX_NULLABLE_SAF.search(t):
224
233
  fn = match.group(1)
225
234
  inner_type = match.group(2)
226
235
  result = f"SimpleAggregateFunction({fn}, Nullable({inner_type}))"
@@ -342,10 +351,7 @@ def parse_indexes_structure(indexes: Optional[List[str]]) -> List[TableIndex]:
342
351
  if index.count("TYPE") != 1:
343
352
  raise ValueError("invalid INDEX format. Usage: `name expr TYPE type_full GRANULARITY granularity`")
344
353
 
345
- match = re.match(
346
- r"(\w+)\s+([\w\s*\[\]\*\(\),\'\"-><.]+)\s+TYPE\s+(\w+)(?:\(([\w\s*.,]+)\))?(?:\s+GRANULARITY\s+(\d+))?",
347
- index,
348
- )
354
+ match = _RE_INDEX_ENTRY.match(index)
349
355
  if match:
350
356
  index_name, a, index_type, value, granularity = match.groups()
351
357
  index_expr = f"{index_type}({value})" if value else index_type
@@ -552,7 +558,7 @@ def clean_comments(schema_to_clean: str) -> str:
552
558
  if i + 1 < len(line) and line[i] == "-" and line[i + 1] == "-" and not inside_json_path:
553
559
  return line[:i].strip()
554
560
 
555
- if not inside_json_path and line[i:].startswith("`json:"):
561
+ if not inside_json_path and line.startswith("`json:", i):
556
562
  inside_json_path = True
557
563
  elif inside_json_path and line[i] == "`":
558
564
  inside_json_path = False
@@ -562,12 +568,12 @@ def clean_comments(schema_to_clean: str) -> str:
562
568
  if schema_to_clean is None:
563
569
  return schema_to_clean
564
570
 
565
- cleaned_schema = ""
571
+ out_lines: List[str] = []
566
572
  for line in schema_to_clean.splitlines():
567
573
  cleaned_line = clean_line_comments(line)
568
574
  if cleaned_line:
569
- cleaned_schema += cleaned_line + "\n"
570
- return cleaned_schema.strip()
575
+ out_lines.append(cleaned_line)
576
+ return "\n".join(out_lines).strip()
571
577
 
572
578
 
573
579
  SyntaxExpr = namedtuple("SyntaxExpr", ["name", "regex"])
@@ -608,9 +614,9 @@ def _parse_table_structure(schema: str) -> List[Dict[str, Any]]:
608
614
 
609
615
  # Find the first SyntaxExpr in lookup that matches the schema at the current offset
610
616
  def lookahead_matches(lookup: Iterable) -> Optional[SyntaxExpr]:
617
+ # Use substring to preserve semantics of patterns anchored to end ($)
611
618
  s = schema[i:]
612
- match = next((x for x in lookup if x.regex.match(s)), None)
613
- return match
619
+ return next((x for x in lookup if x.regex.match(s)), None)
614
620
 
615
621
  def advance_single_char() -> None:
616
622
  nonlocal i, line, pos
@@ -647,7 +653,7 @@ def _parse_table_structure(schema: str) -> List[Dict[str, Any]]:
647
653
  c = schema[i]
648
654
  if c in " \t\r\n":
649
655
  return schema[begin:i]
650
- if c not in valid_chars_name:
656
+ if c not in _VALID_CHARS_NAME_SET:
651
657
  raise ValueError(
652
658
  format_parse_error(schema, i, pos, "wrong value, please check the schema syntax", line=line)
653
659
  )
@@ -677,7 +683,9 @@ def _parse_table_structure(schema: str) -> List[Dict[str, Any]]:
677
683
  context_stack.append("(")
678
684
  elif context is None and lookahead_matches(lookup):
679
685
  return schema[begin:i].strip(" \t\r\n")
680
- elif (context is None and c not in valid_chars_fn) or (context == "(" and c not in valid_chars_fn):
686
+ elif (context is None and c not in _VALID_CHARS_FN_SET) or (
687
+ context == "(" and c not in _VALID_CHARS_FN_SET
688
+ ):
681
689
  raise ValueError(
682
690
  format_parse_error(schema, i, pos, "wrong value, please check the schema syntax", line=line)
683
691
  )
@@ -884,7 +892,7 @@ def engine_replicated_to_local(engine: str) -> str:
884
892
  if "Replicated" not in engine:
885
893
  return engine
886
894
 
887
- return re.sub(r"Replicated(.*)MergeTree\(([^\)]*)\)(.*)", _replace, engine.strip())
895
+ return _RE_REPLICATED_MT.sub(_replace, engine.strip())
888
896
 
889
897
 
890
898
  def engine_patch_replicated_engine(engine: str, engine_full: Optional[str], new_table_name: str) -> Optional[str]:
@@ -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.dev290'
8
- __revision__ = '4d1cca4'
7
+ __version__ = '0.0.1.dev292'
8
+ __revision__ = '78dba1d'
@@ -34,10 +34,12 @@ from tinybird.tb.modules.agent.mock_agent import MockAgent
34
34
  from tinybird.tb.modules.agent.models import create_model
35
35
  from tinybird.tb.modules.agent.prompts import (
36
36
  agent_system_prompt,
37
+ fixtures_prompt,
37
38
  load_custom_project_rules,
38
39
  resources_prompt,
39
40
  secrets_prompt,
40
41
  service_datasources_prompt,
42
+ vendor_files_prompt,
41
43
  )
42
44
  from tinybird.tb.modules.agent.testing_agent import TestingAgent
43
45
  from tinybird.tb.modules.agent.tools.analyze import analyze_file, analyze_url
@@ -322,6 +324,14 @@ class TinybirdAgent:
322
324
  def get_project_files(ctx: RunContext[TinybirdAgentContext]) -> str:
323
325
  return resources_prompt(self.project)
324
326
 
327
+ @self.agent.instructions
328
+ def get_vendor_files(ctx: RunContext[TinybirdAgentContext]) -> str:
329
+ return vendor_files_prompt(self.project)
330
+
331
+ @self.agent.instructions
332
+ def get_fixture_files(ctx: RunContext[TinybirdAgentContext]) -> str:
333
+ return fixtures_prompt(self.project)
334
+
325
335
  @self.agent.instructions
326
336
  def get_service_datasources(ctx: RunContext[TinybirdAgentContext]) -> str:
327
337
  return service_datasources_prompt()
@@ -12,6 +12,7 @@ from tinybird.tb.modules.agent.prompts import (
12
12
  resources_prompt,
13
13
  service_datasources_prompt,
14
14
  tone_and_style_instructions,
15
+ vendor_files_prompt,
15
16
  )
16
17
  from tinybird.tb.modules.agent.tools.diff_resource import diff_resource
17
18
  from tinybird.tb.modules.agent.tools.execute_query import execute_query
@@ -76,6 +77,10 @@ Once you finish the task, return a valid response for the task to complete.
76
77
  def get_project_files(ctx: RunContext[TinybirdAgentContext]) -> str:
77
78
  return resources_prompt(self.project)
78
79
 
80
+ @self.agent.instructions
81
+ def get_vendor_files(ctx: RunContext[TinybirdAgentContext]) -> str:
82
+ return vendor_files_prompt(self.project)
83
+
79
84
  @self.agent.instructions
80
85
  def get_service_datasources(ctx: RunContext[TinybirdAgentContext]) -> str:
81
86
  return service_datasources_prompt()
@@ -6,7 +6,7 @@ from pydantic_ai.usage import Usage
6
6
 
7
7
  from tinybird.tb.modules.agent.animations import ThinkingAnimation
8
8
  from tinybird.tb.modules.agent.models import create_model
9
- from tinybird.tb.modules.agent.prompts import resources_prompt
9
+ from tinybird.tb.modules.agent.prompts import fixtures_prompt, resources_prompt
10
10
  from tinybird.tb.modules.agent.tools.mock import generate_mock_fixture
11
11
  from tinybird.tb.modules.agent.utils import TinybirdAgentContext
12
12
  from tinybird.tb.modules.project import Project
@@ -194,6 +194,10 @@ Today is {datetime.now().strftime("%Y-%m-%d")}
194
194
  def get_project_files(ctx: RunContext[TinybirdAgentContext]) -> str:
195
195
  return resources_prompt(self.project)
196
196
 
197
+ @self.agent.instructions
198
+ def get_fixture_files(ctx: RunContext[TinybirdAgentContext]) -> str:
199
+ return fixtures_prompt(self.project)
200
+
197
201
  def run(self, task: str, deps: TinybirdAgentContext, usage: Usage):
198
202
  result = self.agent.run_sync(
199
203
  task,
@@ -35,8 +35,10 @@ available_commands = [
35
35
  "`tb sink ls`: List all sinks",
36
36
  "`tb workspace current`: Show the current workspace",
37
37
  "`tb workspace clear --yes`: Delete all resources in the workspace (Only available in Tinybird Local)",
38
+ "`tb workspace ls`: List all workspaces",
38
39
  "`tb local start --skip-new-version`: Start Tinybird Local container in non-interactive mode",
39
40
  "`tb local restart --skip-new-version --yes`: Restart Tinybird Local container in non-interactive mode",
41
+ "`tb pull --only-vendored`: Pull only the vendored datasources from other workspaces",
40
42
  ]
41
43
 
42
44
  plan_instructions = """
@@ -166,7 +168,6 @@ SQL >
166
168
 
167
169
  def resources_prompt(project: Project) -> str:
168
170
  files = project.get_project_files()
169
- fixture_files = project.get_fixture_files()
170
171
 
171
172
  resources_content = "# Existing resources in the project:\n"
172
173
  if files:
@@ -184,7 +185,36 @@ def resources_prompt(project: Project) -> str:
184
185
  else:
185
186
  resources_content += "No resources found"
186
187
 
188
+ return resources_content
189
+
190
+
191
+ def vendor_files_prompt(project: Project) -> str:
192
+ files = project.get_vendored_files()
193
+ content = "# Datasources shared from other workspaces:\n"
194
+ if files:
195
+ resources: list[dict[str, Any]] = []
196
+ for filename in files:
197
+ file_path = Path(filename)
198
+ workspace_name = file_path.parent.parent.name
199
+ resource = {
200
+ "path": str(file_path.relative_to(project.folder)),
201
+ "type": get_resource_type(file_path),
202
+ "name": f"{workspace_name}.{file_path.stem}",
203
+ "content": file_path.read_text(),
204
+ "origin_workspace": workspace_name,
205
+ }
206
+ resources.append(resource)
207
+ content += format_as_xml(resources, root_tag="resources", item_tag="resource")
208
+ else:
209
+ content += "No datasources shared from other workspaces"
210
+
211
+ return content
212
+
213
+
214
+ def fixtures_prompt(project: Project) -> str:
215
+ fixture_files = project.get_fixture_files()
187
216
  fixture_content = "# Fixture files in the project:\n"
217
+
188
218
  if fixture_files:
189
219
  fixtures: list[dict[str, Any]] = []
190
220
  for filename in fixture_files:
@@ -199,7 +229,7 @@ def resources_prompt(project: Project) -> str:
199
229
  else:
200
230
  fixture_content += "No fixture files found"
201
231
 
202
- return resources_content + "\n" + fixture_content
232
+ return fixture_content
203
233
 
204
234
 
205
235
  def service_datasources_prompt() -> str:
@@ -954,6 +984,23 @@ After changes have been deployed and promoted, if you want to deploy other chang
954
984
  If after running a deployment, the error contains a recommended forward query, use it to update the .datasource file.
955
985
  </dev_notes>
956
986
 
987
+ ## Sharing datasources with other workspaces
988
+ To share a Data Source, in the .datasource file you want to share, add the destination workspace(s). For example:
989
+
990
+ ```
991
+ SHARED_WITH >
992
+ destination_workspace,
993
+ other_destination_workspace
994
+ ```
995
+
996
+ ## Working with shared datasources:
997
+
998
+ The following limitations apply to shared datasources:
999
+ - Shared datasources are read-only.
1000
+ - You can't share a shared datasource, only the original.
1001
+ - You can't check the quarantine of a shared datasource.
1002
+ - You can't create a Materialized View from a shared datasource.
1003
+
957
1004
  # Working with any type of pipe file:
958
1005
  {pipe_instructions}
959
1006
  {pipe_example}
@@ -3,6 +3,7 @@ import subprocess
3
3
  import click
4
4
  from pydantic_ai import RunContext
5
5
 
6
+ from tinybird.tb.modules.agent.prompts import available_commands
6
7
  from tinybird.tb.modules.agent.utils import (
7
8
  AgentRunCancelled,
8
9
  SubAgentRunCancelled,
@@ -20,6 +21,14 @@ def run_command(ctx: RunContext[TinybirdAgentContext], command: str):
20
21
  command (str): The command to run. Required. Examples: `tb --local sql "select 1"`, `tb --cloud datasource ls`, `tb --help`
21
22
  """
22
23
  try:
24
+ clean_commands = [cmd.split(":")[0].replace("`", "").split("[")[0] for cmd in available_commands]
25
+ available_commands_to_cloud = [f"tb --cloud {command.replace('tb ', '')}" for command in clean_commands]
26
+ available_commands_to_local = [f"tb --local {command.replace('tb ', '')}" for command in clean_commands]
27
+ all_commands = clean_commands + available_commands_to_cloud + available_commands_to_local
28
+
29
+ if not any(cmd.startswith(command) for cmd in all_commands):
30
+ raise SubAgentRunCancelled(f"Command {command} not found in the list of available commands")
31
+
23
32
  ctx.deps.thinking_animation.stop()
24
33
  force_confirmation = " deploy" in command.lower() or " truncate" in command.lower()
25
34
  confirmation = show_confirmation(
@@ -212,16 +212,39 @@ def cli(
212
212
 
213
213
 
214
214
  @cli.command(hidden=True)
215
+ @click.option("--only-vendored", is_flag=True, default=False, help="Only update vendored files")
215
216
  @click.option("-f", "--force", is_flag=True, default=False, help="Override existing files")
216
217
  @click.option("--fmt", is_flag=True, default=False, help="Format files before saving")
217
218
  @click.pass_context
218
- def pull(ctx: Context, force: bool, fmt: bool) -> None:
219
+ def pull(ctx: Context, only_vendored: bool, force: bool, fmt: bool) -> None:
219
220
  """Retrieve latest versions for project files from Tinybird."""
220
221
 
221
222
  client = ctx.ensure_object(dict)["client"]
222
223
  project = ctx.ensure_object(dict)["project"]
223
224
 
224
- return folder_pull(client, project.path, force, fmt=fmt)
225
+ if only_vendored:
226
+ force = True
227
+
228
+ written_files = folder_pull(client, project.path, force, only_vendored=only_vendored, fmt=fmt)
229
+
230
+ if only_vendored:
231
+ for_user_to_delete = set(project.get_vendored_files()) - set(written_files)
232
+ if for_user_to_delete:
233
+ # TODO(eclbg): this prints the full path of the files. Let's print the relative path from the project root
234
+ display_paths = []
235
+ for full_path in for_user_to_delete:
236
+ try:
237
+ display_paths.append(str(Path(full_path).relative_to(project.path)))
238
+ except:
239
+ display_paths.append(full_path)
240
+ click.echo(
241
+ FeedbackManager.warning(
242
+ message=(
243
+ f"This workspace no longer has access to the following files: {display_paths}. "
244
+ "Please remove them manually to be able to deploy."
245
+ )
246
+ )
247
+ )
225
248
 
226
249
 
227
250
  @cli.command()
@@ -392,7 +415,7 @@ def create_ctx_client(
392
415
  if not command or command in commands_without_ctx_client:
393
416
  return None
394
417
 
395
- commands_always_cloud = ["pull", "infra"]
418
+ commands_always_cloud = ["infra"]
396
419
  commands_always_local = ["build", "dev"]
397
420
  command_always_test = ["test"]
398
421
 
@@ -121,12 +121,12 @@ def diff_command(
121
121
  if filenames:
122
122
  if len(filenames) == 1:
123
123
  filenames = [filenames[0], *get_project_filenames(filenames[0])]
124
- folder_pull(client, target_dir, True, verbose=False)
124
+ folder_pull(client, target_dir, True, only_vendored=False, verbose=False)
125
125
  else:
126
126
  filenames = get_project_filenames(".")
127
127
  if verbose:
128
128
  click.echo("Saving remote resources in .diff_tmp folder.\n")
129
- folder_pull(client, target_dir, True, verbose=verbose, progress_bar=progress_bar)
129
+ folder_pull(client, target_dir, True, verbose=verbose, only_vendored=False, progress_bar=progress_bar)
130
130
 
131
131
  remote_datasources: List[Dict[str, Any]] = client.datasources()
132
132
  remote_pipes: List[Dict[str, Any]] = client.pipes()
@@ -13,10 +13,11 @@ def folder_pull(
13
13
  client: TinyB,
14
14
  folder: str,
15
15
  force: bool,
16
+ only_vendored: bool,
16
17
  verbose: bool = True,
17
18
  progress_bar: bool = False,
18
19
  fmt: bool = False,
19
- ):
20
+ ) -> list[str]:
20
21
  def get_file_folder(extension: str, resource_type: Optional[str]):
21
22
  if extension == "datasource":
22
23
  return "datasources"
@@ -40,8 +41,8 @@ def folder_pull(
40
41
  get_resource_function: str,
41
42
  progress_bar: bool = False,
42
43
  fmt: bool = False,
43
- ):
44
- def write_resource(k: dict[str, Any]):
44
+ ) -> list[Path]:
45
+ def write_resource(k: dict[str, Any]) -> Optional[Path]:
45
46
  name = f"{k['name']}.{extension}"
46
47
  try:
47
48
  resource = getattr(client, get_resource_function)(k["name"])
@@ -72,59 +73,71 @@ def folder_pull(
72
73
  with open(f, "w") as fd:
73
74
  if resource_to_write:
74
75
  fd.write(resource_to_write)
76
+ return f
75
77
  else:
76
78
  if verbose:
77
79
  click.echo(FeedbackManager.info_skip_already_exists())
80
+ return None
78
81
  except Exception as e:
79
82
  raise click.ClickException(FeedbackManager.error_exception(error=e))
80
83
 
81
84
  if progress_bar:
85
+ written_files = []
82
86
  with click.progressbar(resources, label=f"Pulling {extension}s") as resources: # type: ignore
83
87
  for k in resources:
84
- write_resource(k)
88
+ written_file = write_resource(k)
89
+ if written_file:
90
+ written_files.append(written_file)
85
91
  else:
86
- tasks = [write_resource(k) for k in resources]
87
- _gather_with_concurrency(5, *tasks)
92
+ written_files = list(filter(None, (write_resource(k) for k in resources)))
93
+
94
+ return written_files
88
95
 
89
96
  try:
90
97
  datasources = client.datasources()
91
98
  pipes = client.pipes()
92
99
  connections = client.connections()
93
100
 
94
- write_files(
95
- resources=datasources,
96
- extension="datasource",
97
- get_resource_function="datasource_file",
98
- progress_bar=progress_bar,
99
- fmt=fmt,
100
- )
101
- write_files(
102
- resources=pipes,
103
- extension="pipe",
104
- get_resource_function="pipe_file",
105
- progress_bar=progress_bar,
106
- fmt=fmt,
107
- )
108
- write_files(
109
- resources=connections,
110
- extension="connection",
111
- get_resource_function="connection_file",
112
- progress_bar=progress_bar,
113
- fmt=fmt,
114
- )
115
- return
101
+ if only_vendored:
102
+ written_files = write_files(
103
+ resources=[ds for ds in datasources if "shared_from" in ds],
104
+ extension="datasource",
105
+ get_resource_function="datasource_file",
106
+ progress_bar=progress_bar,
107
+ fmt=fmt,
108
+ )
109
+ else:
110
+ written_files = []
111
+ written_files.extend(
112
+ write_files(
113
+ resources=datasources,
114
+ extension="datasource",
115
+ get_resource_function="datasource_file",
116
+ progress_bar=progress_bar,
117
+ fmt=fmt,
118
+ )
119
+ )
120
+ written_files.extend(
121
+ write_files(
122
+ resources=pipes,
123
+ extension="pipe",
124
+ get_resource_function="pipe_file",
125
+ progress_bar=progress_bar,
126
+ fmt=fmt,
127
+ )
128
+ )
129
+ written_files.extend(
130
+ write_files(
131
+ resources=connections,
132
+ extension="connection",
133
+ get_resource_function="connection_file",
134
+ progress_bar=progress_bar,
135
+ fmt=fmt,
136
+ )
137
+ )
138
+ return [str(wf) for wf in written_files]
116
139
 
117
140
  except AuthNoTokenException:
118
141
  raise
119
142
  except Exception as e:
120
143
  raise click.ClickException(FeedbackManager.error_pull(error=str(e)))
121
-
122
-
123
- def _gather_with_concurrency(n, *tasks):
124
- results = []
125
- for task in tasks:
126
- if callable(task):
127
- results.append(task())
128
- else:
129
- results.append(task)
130
- return results
@@ -222,6 +222,7 @@ def create_deployment(
222
222
  # TODO: This code is duplicated in build_server.py
223
223
  # Should be refactored to be shared
224
224
  MULTIPART_BOUNDARY_DATA_PROJECT = "data_project://"
225
+ MULTIPART_BOUNDARY_DATA_PROJECT_VENDORED = "data_project_vendored://"
225
226
  DATAFILE_TYPE_TO_CONTENT_TYPE = {
226
227
  ".datasource": "text/plain",
227
228
  ".pipe": "text/plain",
@@ -248,6 +249,13 @@ def create_deployment(
248
249
  with open(file_path, "rb") as fd:
249
250
  content_type = DATAFILE_TYPE_TO_CONTENT_TYPE.get(Path(file_path).suffix, "application/unknown")
250
251
  files.append((MULTIPART_BOUNDARY_DATA_PROJECT, (relative_path, fd.read().decode("utf-8"), content_type)))
252
+ for file_path in project.get_vendored_files():
253
+ relative_path = Path(file_path).relative_to(project.path).as_posix()
254
+ with open(file_path, "rb") as fd:
255
+ content_type = DATAFILE_TYPE_TO_CONTENT_TYPE.get(Path(file_path).suffix, "application/unknown")
256
+ files.append(
257
+ (MULTIPART_BOUNDARY_DATA_PROJECT_VENDORED, (relative_path, fd.read().decode("utf-8"), content_type))
258
+ )
251
259
 
252
260
  deployment = None
253
261
  try:
@@ -37,11 +37,13 @@ class Project:
37
37
  def has_deeper_level(self) -> bool:
38
38
  """Check if there are folders with depth greater than max_depth in project path.
39
39
 
40
+ Does not consider the vendor directory.
41
+
40
42
  Returns:
41
43
  bool: True if there are folders deeper than max_depth, False otherwise
42
44
  """
43
45
  for obj in glob.glob(f"{self.path}{'/*' * (self.max_depth - 1)}/*", recursive=False):
44
- if Path(obj).is_dir():
46
+ if Path(obj).is_dir() and self.vendor_path not in obj:
45
47
  return True
46
48
  return False
47
49
 
@@ -54,6 +56,13 @@ class Project:
54
56
  project_files.append(project_file)
55
57
  return project_files
56
58
 
59
+ def get_vendored_files(self) -> List[str]:
60
+ vendored_files: List[str] = []
61
+ for extension in self.extensions:
62
+ for level in range(3):
63
+ vendored_files.extend(glob.glob(f"{self.vendor_path}{'/*' * level}/*.{extension}", recursive=True))
64
+ return vendored_files
65
+
57
66
  def get_fixture_files(self) -> List[str]:
58
67
  fixture_files: List[str] = []
59
68
  for extension in [
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: tinybird
3
- Version: 0.0.1.dev290
3
+ Version: 0.0.1.dev292
4
4
  Summary: Tinybird Command Line Tool
5
5
  Home-page: https://www.tinybird.co/docs/forward/commands
6
6
  Author: Tinybird