tinybird 4.6.1.dev0__tar.gz → 4.6.3__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.
Files changed (129) hide show
  1. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/PKG-INFO +15 -2
  2. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/client.py +0 -6
  3. tinybird-4.6.3/tinybird/iterating/__init__.py +0 -0
  4. tinybird-4.6.3/tinybird/iterating/data_branch_modes.py +9 -0
  5. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/sql_template.py +2 -2
  6. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/__cli__.py +2 -2
  7. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/client.py +0 -3
  8. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/branch.py +14 -2
  9. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/cli.py +53 -6
  10. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/common.py +15 -7
  11. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/datafile/pull.py +29 -6
  12. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/preview.py +0 -1
  13. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/query_output.py +3 -1
  14. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb_cli_modules/branch.py +14 -12
  15. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb_cli_modules/common.py +3 -3
  16. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird.egg-info/PKG-INFO +15 -2
  17. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird.egg-info/SOURCES.txt +3 -3
  18. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird.egg-info/requires.txt +1 -1
  19. tinybird-4.6.1.dev0/tinybird/tb/modules/tinyunit/tinyunit_lib.py +0 -65
  20. tinybird-4.6.1.dev0/tinybird/tb_cli_modules/tinyunit/tinyunit_lib.py +0 -65
  21. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/setup.cfg +0 -0
  22. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/__cli__.py +0 -0
  23. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/ch_utils/constants.py +0 -0
  24. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/ch_utils/engine.py +0 -0
  25. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/check_pypi.py +0 -0
  26. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/config.py +0 -0
  27. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/context.py +0 -0
  28. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/datafile/common.py +0 -0
  29. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/datafile/exceptions.py +0 -0
  30. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/datafile/parse_connection.py +0 -0
  31. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/datafile/parse_datasource.py +0 -0
  32. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/datafile/parse_pipe.py +0 -0
  33. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/datatypes.py +0 -0
  34. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/feedback_manager.py +0 -0
  35. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/git_settings.py +0 -0
  36. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/prompts.py +0 -0
  37. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/service_datasources.py +0 -0
  38. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/sql.py +0 -0
  39. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/sql_template_fmt.py +0 -0
  40. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/sql_toolset.py +0 -0
  41. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/syncasync.py +0 -0
  42. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/check_pypi.py +0 -0
  43. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/cli.py +0 -0
  44. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/config.py +0 -0
  45. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/build.py +0 -0
  46. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/build_common.py +0 -0
  47. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/cicd.py +0 -0
  48. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/config.py +0 -0
  49. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/connection.py +0 -0
  50. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/connection_dynamodb.py +0 -0
  51. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/connection_kafka.py +0 -0
  52. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/connection_s3.py +0 -0
  53. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/copy.py +0 -0
  54. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/create.py +0 -0
  55. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/datafile/build.py +0 -0
  56. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/datafile/build_common.py +0 -0
  57. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/datafile/build_datasource.py +0 -0
  58. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/datafile/build_pipe.py +0 -0
  59. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/datafile/diff.py +0 -0
  60. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/datafile/fixture.py +0 -0
  61. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/datafile/format_common.py +0 -0
  62. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/datafile/format_connection.py +0 -0
  63. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/datafile/format_datasource.py +0 -0
  64. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/datafile/format_pipe.py +0 -0
  65. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/datafile/pipe_checker.py +0 -0
  66. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/datafile/playground.py +0 -0
  67. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/datasource.py +0 -0
  68. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/deployment.py +0 -0
  69. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/deployment_common.py +0 -0
  70. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/deprecations.py +0 -0
  71. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/endpoint.py +0 -0
  72. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/exceptions.py +0 -0
  73. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/feedback_manager.py +0 -0
  74. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/fmt.py +0 -0
  75. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/info.py +0 -0
  76. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/infra.py +0 -0
  77. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/job.py +0 -0
  78. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/job_common.py +0 -0
  79. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/llm.py +0 -0
  80. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/llm_utils.py +0 -0
  81. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/local.py +0 -0
  82. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/local_common.py +0 -0
  83. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/local_logs.py +0 -0
  84. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/login.py +0 -0
  85. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/login_common.py +0 -0
  86. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/logout.py +0 -0
  87. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/logs.py +0 -0
  88. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/materialization.py +0 -0
  89. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/open.py +0 -0
  90. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/pipe.py +0 -0
  91. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/project.py +0 -0
  92. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/project_commands.py +0 -0
  93. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/py_project.py +0 -0
  94. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/regions.py +0 -0
  95. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/secret.py +0 -0
  96. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/secret_common.py +0 -0
  97. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/sink.py +0 -0
  98. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/table.py +0 -0
  99. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/telemetry.py +0 -0
  100. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/test.py +0 -0
  101. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/test_common.py +0 -0
  102. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/tinyunit/tinyunit.py +0 -0
  103. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/token.py +0 -0
  104. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/ts_project.py +0 -0
  105. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/watch.py +0 -0
  106. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/workspace.py +0 -0
  107. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb/modules/workspace_members.py +0 -0
  108. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb_cli.py +0 -0
  109. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb_cli_modules/auth.py +0 -0
  110. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb_cli_modules/cicd.py +0 -0
  111. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb_cli_modules/cli.py +0 -0
  112. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb_cli_modules/config.py +0 -0
  113. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb_cli_modules/connection.py +0 -0
  114. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb_cli_modules/datasource.py +0 -0
  115. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb_cli_modules/exceptions.py +0 -0
  116. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb_cli_modules/fmt.py +0 -0
  117. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb_cli_modules/job.py +0 -0
  118. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb_cli_modules/pipe.py +0 -0
  119. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb_cli_modules/regions.py +0 -0
  120. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb_cli_modules/tag.py +0 -0
  121. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb_cli_modules/telemetry.py +0 -0
  122. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb_cli_modules/test.py +0 -0
  123. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb_cli_modules/tinyunit/tinyunit.py +0 -0
  124. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb_cli_modules/workspace.py +0 -0
  125. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tb_cli_modules/workspace_members.py +0 -0
  126. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird/tornado_template.py +0 -0
  127. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird.egg-info/dependency_links.txt +0 -0
  128. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird.egg-info/entry_points.txt +0 -0
  129. {tinybird-4.6.1.dev0 → tinybird-4.6.3}/tinybird.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: tinybird
3
- Version: 4.6.1.dev0
3
+ Version: 4.6.3
4
4
  Summary: Tinybird Command Line Tool
5
5
  Home-page: https://www.tinybird.co/docs/forward/commands
6
6
  Author: Tinybird
@@ -27,7 +27,7 @@ Requires-Dist: requests<3,>=2.28.1
27
27
  Requires-Dist: shandy-sqlfmt==0.11.1
28
28
  Requires-Dist: shandy-sqlfmt[jinjafmt]==0.11.1
29
29
  Requires-Dist: toposort==1.10
30
- Requires-Dist: tornado~=6.0.0
30
+ Requires-Dist: tornado~=6.5.5
31
31
  Requires-Dist: urllib3<2,>=1.26.14
32
32
  Requires-Dist: watchdog==6.0.0
33
33
  Requires-Dist: wheel
@@ -52,6 +52,19 @@ The Tinybird command-line tool allows you to use all the Tinybird functionality
52
52
  Changelog
53
53
  ----------
54
54
 
55
+ 4.6.2
56
+ *******
57
+
58
+ - `Changed` Replaced `branch_data_on_create` with `branch_data_mode` in CLI project config handling. The legacy key now raises an explicit migration error.
59
+ - `Changed` `branch_data_mode` now only accepts `last_partition` as a user-facing value.
60
+ - `Fixed` `dev_mode=local` now warns about branch data mode only when `branch_data_mode` is explicitly set in `tinybird.config.json`.
61
+ - `Changed` `tb branch create` and `tb branch clear` now show a deprecation warning (instead of failing) when `--ignore-datasource` is provided, and continue by ignoring that flag.
62
+
63
+ 4.6.1
64
+ *******
65
+
66
+ - `Changed` `tb pull` to overwrite existing local files when pulling from a Tinybird workspace.
67
+
55
68
  4.6.0
56
69
  *******
57
70
 
@@ -770,14 +770,11 @@ class TinyB:
770
770
  branch_name: str,
771
771
  last_partition: Optional[bool],
772
772
  all: Optional[bool],
773
- ignore_datasources: Optional[List[str]],
774
773
  ):
775
774
  params = {
776
775
  "name": branch_name,
777
776
  "data": LAST_PARTITION if last_partition else (ALL_PARTITIONS if all else ""),
778
777
  }
779
- if ignore_datasources:
780
- params["ignore_datasources"] = ",".join(ignore_datasources)
781
778
  return await self._req(f"/v0/environments?{urlencode(params)}", method="POST", data=b"")
782
779
 
783
780
  async def branch_workspace_data(
@@ -785,7 +782,6 @@ class TinyB:
785
782
  workspace_id: str,
786
783
  last_partition: bool,
787
784
  all: bool,
788
- ignore_datasources: Optional[List[str]] = None,
789
785
  ):
790
786
  params = {}
791
787
  if last_partition:
@@ -793,8 +789,6 @@ class TinyB:
793
789
 
794
790
  if all:
795
791
  params["mode"] = ALL_PARTITIONS
796
- if ignore_datasources:
797
- params["ignore_datasources"] = ",".join(ignore_datasources)
798
792
  url = f"/v0/environments/{workspace_id}/data?{urlencode(params)}"
799
793
  return await self._req(url, method="POST", data=b"")
800
794
 
File without changes
@@ -0,0 +1,9 @@
1
+ from enum import Enum
2
+
3
+
4
+ class DataBranchMode(Enum):
5
+ LAST_PARTITION = "last_partition"
6
+ ALL_PARTITIONS = "all_partitions"
7
+
8
+
9
+ DATA_BRANCH_MODES = [a.value for a in DataBranchMode]
@@ -7,7 +7,7 @@ from collections import deque
7
7
  from datetime import datetime
8
8
  from functools import lru_cache
9
9
  from json import loads
10
- from typing import Any, Callable, Dict, List, Optional, Tuple, Union
10
+ from typing import Any, Callable, Dict, List, Optional, Tuple, Union, cast
11
11
 
12
12
  from tornado import escape
13
13
  from tornado.util import ObjectDict, exec_in, unicode_type
@@ -1499,7 +1499,7 @@ def generate(self, **kwargs) -> Tuple[str, TemplateExecutionResults]:
1499
1499
  )
1500
1500
 
1501
1501
  exec_in(self.compiled, namespace)
1502
- execute = namespace["_tt_execute"]
1502
+ execute = cast(Callable[[], bytes], namespace["_tt_execute"])
1503
1503
  # Clear the traceback module's cache of source data now that
1504
1504
  # we've generated a new template (mainly for this module's
1505
1505
  # unittests, where different tests reuse the same name).
@@ -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__ = '4.6.1.dev0'
8
- __revision__ = '1d9f802'
7
+ __version__ = '4.6.3'
8
+ __revision__ = '78e5916'
@@ -772,14 +772,11 @@ class TinyB:
772
772
  branch_name: str,
773
773
  last_partition: Optional[bool],
774
774
  all: Optional[bool],
775
- ignore_datasources: Optional[List[str]],
776
775
  ):
777
776
  params = {
778
777
  "name": branch_name,
779
778
  "data": LAST_PARTITION if last_partition else (ALL_PARTITIONS if all else ""),
780
779
  }
781
- if ignore_datasources:
782
- params["ignore_datasources"] = ",".join(ignore_datasources)
783
780
  return self._req(f"/v1/environments?{urlencode(params)}", method="POST", data=b"")
784
781
 
785
782
  def branch_workspace_data(
@@ -93,8 +93,14 @@ def branch_ls(sort: bool) -> None:
93
93
  help="Wait for data branch jobs to finish, showing a progress bar. Disabled by default.",
94
94
  )
95
95
  def create_branch(branch_name: Optional[str], last_partition: bool, ignore_datasources: List[str], wait: bool) -> None:
96
+ if ignore_datasources:
97
+ click.echo(
98
+ FeedbackManager.warning_deprecated(
99
+ warning="--ignore-datasource is no longer supported for `tb branch create` and will be ignored."
100
+ )
101
+ )
96
102
  normalized_branch_name = ensure_valid_workspace_name(branch_name) if branch_name else branch_name
97
- create_workspace_branch(normalized_branch_name, last_partition, False, list(ignore_datasources), wait)
103
+ create_workspace_branch(normalized_branch_name, last_partition, False, wait)
98
104
 
99
105
 
100
106
  @branch.command(name="rm", short_help="Removes an branch from the workspace. It can't be recovered.")
@@ -182,6 +188,12 @@ def clear_branch(
182
188
  yes: bool,
183
189
  ) -> None:
184
190
  """Clear a branch by deleting and recreating it."""
191
+ if ignore_datasources:
192
+ click.echo(
193
+ FeedbackManager.warning_deprecated(
194
+ warning="--ignore-datasource is no longer supported for `tb branch clear` and will be ignored."
195
+ )
196
+ )
185
197
  config = CLIConfig.get_project_config()
186
198
  _ = try_update_config_with_remote(config, only_if_needed=True)
187
199
 
@@ -227,7 +239,7 @@ def clear_branch(
227
239
  client = config.get_client(token=current_main_workspace.get("token"))
228
240
  try:
229
241
  client.delete_branch(workspace_to_clear["id"])
230
- response = client.create_workspace_branch(branch_name, last_partition, False, list(ignore_datasources))
242
+ response = client.create_workspace_branch(branch_name, last_partition, False)
231
243
  if wait and "job" in response:
232
244
  job_id = response["job"]["job_id"]
233
245
  job_url = response["job"]["job_url"]
@@ -21,6 +21,7 @@ import humanfriendly
21
21
  import requests
22
22
  from click import Context
23
23
 
24
+ from tinybird.iterating.data_branch_modes import DataBranchMode
24
25
  from tinybird.tb import __cli__
25
26
  from tinybird.tb.check_pypi import CheckPypi
26
27
  from tinybird.tb.client import (
@@ -307,6 +308,37 @@ def get_dev_mode_from_tinybird_config(start_dir: str) -> Optional[str]:
307
308
  return mode
308
309
 
309
310
 
311
+ def _resolve_branch_data_mode(raw: Dict[str, Any]) -> Tuple[Optional[str], bool]:
312
+ if "branch_data_on_create" in raw:
313
+ raise CLIException(
314
+ FeedbackManager.error(message="`branch_data_on_create` has been renamed to `branch_data_mode`.")
315
+ )
316
+
317
+ value = raw.get("branch_data_mode")
318
+ if value is None:
319
+ return DataBranchMode.LAST_PARTITION.value, False
320
+ if not isinstance(value, str):
321
+ raise CLIException(FeedbackManager.error(message="branch_data_mode must be a string."))
322
+
323
+ mode = value.strip().lower()
324
+ if not mode:
325
+ return DataBranchMode.LAST_PARTITION.value, False
326
+ if mode != DataBranchMode.LAST_PARTITION.value:
327
+ allowed = DataBranchMode.LAST_PARTITION.value
328
+ raise CLIException(
329
+ FeedbackManager.error(message=f"Invalid branch_data_mode '{value}'. Allowed values are: {allowed}.")
330
+ )
331
+ return mode, True
332
+
333
+
334
+ def get_branch_data_mode_from_tinybird_config(start_dir: str) -> Tuple[Optional[str], bool]:
335
+ _, raw = _read_project_json_config(start_dir)
336
+ if not isinstance(raw, dict):
337
+ return DataBranchMode.LAST_PARTITION.value, False
338
+
339
+ return _resolve_branch_data_mode(raw)
340
+
341
+
310
342
  def find_project_config_file(start_dir: Path) -> Optional[Path]:
311
343
  current = start_dir.resolve()
312
344
  while True:
@@ -653,6 +685,17 @@ def cli(
653
685
  if tinybird_dev_mode:
654
686
  config["dev_mode"] = tinybird_dev_mode
655
687
 
688
+ branch_data_mode, branch_data_mode_explicit = get_branch_data_mode_from_tinybird_config(os.getcwd())
689
+ if branch_data_mode:
690
+ config["branch_data_mode"] = branch_data_mode
691
+ if branch_data_mode_explicit and config.get("dev_mode") == DEV_MODE_LOCAL:
692
+ click.echo(
693
+ FeedbackManager.warning(
694
+ message="branch_data_mode is set in tinybird.config.json but dev_mode='local'. "
695
+ "Branch data settings only apply to cloud branches."
696
+ )
697
+ )
698
+
656
699
  # Resolve project folder from tinybird.config.json (preferred) or legacy .tinyb cwd.
657
700
  folder_from_config = get_project_folder_from_tinybird_config(os.getcwd())
658
701
  if folder_from_config:
@@ -754,6 +797,12 @@ def cli(
754
797
  FeedbackManager.gray(message=f"Using dev_mode={dev_mode}. Running deploy against Tinybird Cloud main.")
755
798
  )
756
799
 
800
+ _auto_create_branch = (
801
+ not explicit_env_selector
802
+ and ctx.invoked_subcommand == "build"
803
+ and dev_mode == DEV_MODE_BRANCH
804
+ and bool(effective_branch)
805
+ )
757
806
  client = create_ctx_client(
758
807
  ctx,
759
808
  config,
@@ -763,12 +812,8 @@ def cli(
763
812
  project_type=project_type,
764
813
  show_warnings=version_warning,
765
814
  branch=effective_branch,
766
- create_branch_if_missing=(
767
- not explicit_env_selector
768
- and ctx.invoked_subcommand == "build"
769
- and dev_mode == DEV_MODE_BRANCH
770
- and bool(effective_branch)
771
- ),
815
+ create_branch_if_missing=_auto_create_branch,
816
+ branch_data_mode=config.get("branch_data_mode") if _auto_create_branch else None,
772
817
  )
773
818
 
774
819
  if client:
@@ -1052,6 +1097,7 @@ def create_ctx_client(
1052
1097
  show_warnings: bool = True,
1053
1098
  branch: Optional[str] = None,
1054
1099
  create_branch_if_missing: bool = False,
1100
+ branch_data_mode: Optional[str] = None,
1055
1101
  ):
1056
1102
  commands_without_ctx_client = [
1057
1103
  "auth",
@@ -1103,6 +1149,7 @@ def create_ctx_client(
1103
1149
  branch=branch,
1104
1150
  create_branch_if_missing=create_branch_if_missing,
1105
1151
  request_from=project_type,
1152
+ branch_data_mode=branch_data_mode,
1106
1153
  )
1107
1154
  ctx.ensure_object(dict)["branch_created"] = branch_created
1108
1155
  return client
@@ -33,6 +33,7 @@ from click._termui_impl import ProgressBar
33
33
  from humanfriendly.tables import format_pretty_table
34
34
  from thefuzz import process
35
35
 
36
+ from tinybird.iterating.data_branch_modes import DataBranchMode
36
37
  from tinybird.tb.client import (
37
38
  AuthException,
38
39
  AuthNoTokenException,
@@ -105,7 +106,9 @@ def echo_safe_humanfriendly_tables_format_smart_table(data: Iterable[Any], colum
105
106
  try:
106
107
  click.echo(humanfriendly.tables.format_smart_table(data, column_names=column_names))
107
108
  except ValueError as exc:
108
- if str(exc) == "max() arg is an empty sequence":
109
+ # Python 3.11 wording: "max() arg is an empty sequence"
110
+ # Python 3.12 wording: "max() iterable argument is empty"
111
+ if str(exc) in ("max() arg is an empty sequence", "max() iterable argument is empty"):
109
112
  click.echo("------------")
110
113
  click.echo("Empty")
111
114
  click.echo("------------")
@@ -122,7 +125,9 @@ def echo_safe_humanfriendly_tables_format_pretty_table(data: Iterable[Any], colu
122
125
  try:
123
126
  click.echo(humanfriendly.tables.format_pretty_table(data, column_names=column_names))
124
127
  except ValueError as exc:
125
- if str(exc) == "max() arg is an empty sequence":
128
+ # Python 3.11 wording: "max() arg is an empty sequence"
129
+ # Python 3.12 wording: "max() iterable argument is empty"
130
+ if str(exc) in ("max() arg is an empty sequence", "max() iterable argument is empty"):
126
131
  click.echo("------------")
127
132
  click.echo("Empty")
128
133
  click.echo("------------")
@@ -139,7 +144,9 @@ def echo_safe_format_table(data: Iterable[Any], columns) -> None:
139
144
  try:
140
145
  click.echo(format_table(data, columns))
141
146
  except ValueError as exc:
142
- if str(exc) == "max() arg is an empty sequence":
147
+ # Python 3.11 wording: "max() arg is an empty sequence"
148
+ # Python 3.12 wording: "max() iterable argument is empty"
149
+ if str(exc) in ("max() arg is an empty sequence", "max() iterable argument is empty"):
143
150
  click.echo("------------")
144
151
  click.echo("Empty")
145
152
  click.echo("------------")
@@ -363,6 +370,7 @@ def _get_tb_client(
363
370
  branch: Optional[str] = None,
364
371
  create_branch_if_missing: bool = False,
365
372
  request_from: Optional[str] = None,
373
+ branch_data_mode: Optional[str] = None,
366
374
  ) -> Tuple[TinyB, bool]:
367
375
  """Returns a tuple of (client, branch_created)."""
368
376
  resolved_request_from = request_from
@@ -384,11 +392,13 @@ def _get_tb_client(
384
392
  workspaces = cloud_client.user_workspaces_and_branches(version="v1")
385
393
  workspace = next((w for w in workspaces.get("workspaces", []) if w.get("name") == branch), None)
386
394
  if not workspace and create_branch_if_missing:
395
+ _last_partition = branch_data_mode == DataBranchMode.LAST_PARTITION.value
396
+ mode_label = branch_data_mode or "no data"
397
+ click.echo(FeedbackManager.gray(message=f"Creating branch '{branch}' with data_mode={mode_label}..."))
387
398
  response = cloud_client.create_workspace_branch(
388
399
  branch_name=branch,
389
- last_partition=False,
400
+ last_partition=_last_partition,
390
401
  all=False,
391
- ignore_datasources=None,
392
402
  )
393
403
  job_data = response.get("job") if isinstance(response, dict) else None
394
404
  if isinstance(job_data, dict):
@@ -2458,7 +2468,6 @@ def create_workspace_branch(
2458
2468
  branch_name: Optional[str],
2459
2469
  last_partition: bool,
2460
2470
  all: bool,
2461
- ignore_datasources: Optional[List[str]],
2462
2471
  wait: Optional[bool],
2463
2472
  ) -> None:
2464
2473
  """
@@ -2484,7 +2493,6 @@ def create_workspace_branch(
2484
2493
  branch_name,
2485
2494
  last_partition,
2486
2495
  all,
2487
- ignore_datasources,
2488
2496
  )
2489
2497
  assert isinstance(response, dict)
2490
2498
 
@@ -8,6 +8,8 @@ from tinybird.tb.modules.datafile.format_datasource import format_datasource
8
8
  from tinybird.tb.modules.datafile.format_pipe import format_pipe
9
9
  from tinybird.tb.modules.feedback_manager import FeedbackManager
10
10
 
11
+ PULL_EXISTING_FILE_MAX_DEPTH = 5
12
+
11
13
 
12
14
  def folder_pull(
13
15
  client: TinyB,
@@ -18,7 +20,7 @@ def folder_pull(
18
20
  progress_bar: bool = False,
19
21
  fmt: bool = False,
20
22
  ) -> list[str]:
21
- def get_file_folder(extension: str, resource_type: Optional[str]):
23
+ def get_file_folder(extension: str, resource_type: Optional[str]) -> Optional[str]:
22
24
  if extension == "datasource":
23
25
  return "datasources"
24
26
  if extension == "connection":
@@ -35,6 +37,24 @@ def folder_pull(
35
37
  return "pipes"
36
38
  return None
37
39
 
40
+ def find_existing_resource_file(dest_folder: Path, name: str) -> Optional[Path]:
41
+ if not dest_folder.exists():
42
+ return None
43
+
44
+ candidates = []
45
+ for candidate in dest_folder.rglob(name):
46
+ if not candidate.is_file():
47
+ continue
48
+
49
+ relative_parent = candidate.parent.relative_to(dest_folder)
50
+ if len(relative_parent.parts) <= PULL_EXISTING_FILE_MAX_DEPTH:
51
+ candidates.append(candidate)
52
+
53
+ if not candidates:
54
+ return None
55
+
56
+ return sorted(candidates, key=lambda path: (len(path.parent.relative_to(dest_folder).parts), str(path)))[0]
57
+
38
58
  def write_files(
39
59
  resources: list[dict[str, Any]],
40
60
  extension: str,
@@ -62,14 +82,17 @@ def folder_pull(
62
82
  file_folder = get_file_folder(extension, k.get("type"))
63
83
  f = Path(dest_folder) / file_folder if file_folder is not None else Path(dest_folder)
64
84
 
65
- if not f.exists():
66
- f.mkdir(parents=True)
67
-
68
- f = f / name
85
+ existing_file = find_existing_resource_file(Path(dest_folder), name)
86
+ if existing_file is not None:
87
+ f = existing_file
88
+ else:
89
+ if not f.exists():
90
+ f.mkdir(parents=True)
91
+ f = f / name
69
92
 
70
93
  if verbose:
71
94
  click.echo(FeedbackManager.info_writing_resource(resource=f))
72
- if not f.exists() or force:
95
+ if not f.exists() or force or existing_file is not None:
73
96
  with open(f, "w") as fd:
74
97
  if resource_to_write:
75
98
  fd.write(resource_to_write)
@@ -44,7 +44,6 @@ def _create_preview_branch(cloud_client: TinyB, preview_branch_name: str) -> Non
44
44
  branch_name=preview_branch_name,
45
45
  last_partition=False,
46
46
  all=False,
47
- ignore_datasources=None,
48
47
  )
49
48
 
50
49
  job_data = response.get("job") if isinstance(response, dict) else None
@@ -26,7 +26,9 @@ def print_table_formatted(res: dict, name: str):
26
26
  click.echo(FeedbackManager.success(message=stats_message))
27
27
  click.echo(FeedbackManager.gray(message=rows_message))
28
28
  except ValueError as exc:
29
- if str(exc) == "max() arg is an empty sequence":
29
+ # Python 3.11 wording: "max() arg is an empty sequence"
30
+ # Python 3.12 wording: "max() iterable argument is empty"
31
+ if str(exc) in ("max() arg is an empty sequence", "max() iterable argument is empty"):
30
32
  click.echo("------------")
31
33
  else:
32
34
  raise
@@ -389,11 +389,21 @@ async def branch_current() -> None:
389
389
  )
390
390
  @coro
391
391
  async def create_branch(
392
- branch_name: Optional[str], last_partition: bool, all: bool, ignore_datasources: List[str], wait: bool
392
+ branch_name: Optional[str],
393
+ last_partition: bool,
394
+ all: bool,
395
+ ignore_datasources: Tuple[str, ...],
396
+ wait: bool,
393
397
  ) -> None:
394
398
  if last_partition and all:
395
399
  raise CLIException(FeedbackManager.error_exception(error="Use --last-partition or --all but not both"))
396
- await create_workspace_branch(branch_name, last_partition, all, list(ignore_datasources), wait)
400
+ if ignore_datasources:
401
+ click.echo(
402
+ FeedbackManager.warning_deprecated(
403
+ warning="--ignore-datasource is no longer supported for `tb branch create` and will be ignored."
404
+ )
405
+ )
406
+ await create_workspace_branch(branch_name, last_partition, all, wait)
397
407
 
398
408
 
399
409
  @branch.command(name="rm", short_help="Removes a Branch from the Workspace. It can't be recovered.")
@@ -468,14 +478,6 @@ async def delete_branch(branch_name_or_id: str, yes: bool) -> None:
468
478
  help="Attach all data from 'main' to the new Branch. Use only if you actually need all the data in the Branch",
469
479
  hidden=True,
470
480
  )
471
- @click.option(
472
- "-i",
473
- "--ignore-datasource",
474
- "ignore_datasources",
475
- type=str,
476
- multiple=True,
477
- help="Ignore specified data source partitions",
478
- )
479
481
  @click.option(
480
482
  "--wait",
481
483
  is_flag=True,
@@ -483,7 +485,7 @@ async def delete_branch(branch_name_or_id: str, yes: bool) -> None:
483
485
  help="Wait for data branch jobs to finish, showing a progress bar. Disabled by default.",
484
486
  )
485
487
  @coro
486
- async def data_branch(last_partition: bool, all: bool, ignore_datasources: List[str], wait: bool) -> None:
488
+ async def data_branch(last_partition: bool, all: bool, wait: bool) -> None:
487
489
  if last_partition and all:
488
490
  raise CLIException(FeedbackManager.error_exception(error="Use --last-partition or --all but not both"))
489
491
 
@@ -500,7 +502,7 @@ async def data_branch(last_partition: bool, all: bool, ignore_datasources: List[
500
502
  raise CLIException(FeedbackManager.error_not_allowed_in_main_branch())
501
503
 
502
504
  try:
503
- response = await client.branch_workspace_data(config["id"], last_partition, all, ignore_datasources)
505
+ response = await client.branch_workspace_data(config["id"], last_partition, all)
504
506
 
505
507
  is_job: bool = "job" in response
506
508
  is_summary: bool = "partitions" in response
@@ -112,7 +112,9 @@ def echo_safe_humanfriendly_tables_format_smart_table(data: Iterable[Any], colum
112
112
  try:
113
113
  click.echo(humanfriendly.tables.format_smart_table(data, column_names=column_names))
114
114
  except ValueError as exc:
115
- if str(exc) == "max() arg is an empty sequence":
115
+ # Python 3.11 wording: "max() arg is an empty sequence"
116
+ # Python 3.12 wording: "max() iterable argument is empty"
117
+ if str(exc) in ("max() arg is an empty sequence", "max() iterable argument is empty"):
116
118
  click.echo("------------")
117
119
  click.echo("Empty")
118
120
  click.echo("------------")
@@ -677,7 +679,6 @@ async def create_workspace_branch(
677
679
  branch_name: Optional[str],
678
680
  last_partition: bool,
679
681
  all: bool,
680
- ignore_datasources: Optional[List[str]],
681
682
  wait: Optional[bool],
682
683
  ) -> None:
683
684
  """
@@ -701,7 +702,6 @@ async def create_workspace_branch(
701
702
  branch_name,
702
703
  last_partition,
703
704
  all,
704
- ignore_datasources,
705
705
  )
706
706
  assert isinstance(response, dict)
707
707
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: tinybird
3
- Version: 4.6.1.dev0
3
+ Version: 4.6.3
4
4
  Summary: Tinybird Command Line Tool
5
5
  Home-page: https://www.tinybird.co/docs/forward/commands
6
6
  Author: Tinybird
@@ -27,7 +27,7 @@ Requires-Dist: requests<3,>=2.28.1
27
27
  Requires-Dist: shandy-sqlfmt==0.11.1
28
28
  Requires-Dist: shandy-sqlfmt[jinjafmt]==0.11.1
29
29
  Requires-Dist: toposort==1.10
30
- Requires-Dist: tornado~=6.0.0
30
+ Requires-Dist: tornado~=6.5.5
31
31
  Requires-Dist: urllib3<2,>=1.26.14
32
32
  Requires-Dist: watchdog==6.0.0
33
33
  Requires-Dist: wheel
@@ -52,6 +52,19 @@ The Tinybird command-line tool allows you to use all the Tinybird functionality
52
52
  Changelog
53
53
  ----------
54
54
 
55
+ 4.6.2
56
+ *******
57
+
58
+ - `Changed` Replaced `branch_data_on_create` with `branch_data_mode` in CLI project config handling. The legacy key now raises an explicit migration error.
59
+ - `Changed` `branch_data_mode` now only accepts `last_partition` as a user-facing value.
60
+ - `Fixed` `dev_mode=local` now warns about branch data mode only when `branch_data_mode` is explicitly set in `tinybird.config.json`.
61
+ - `Changed` `tb branch create` and `tb branch clear` now show a deprecation warning (instead of failing) when `--ignore-datasource` is provided, and continue by ignoring that flag.
62
+
63
+ 4.6.1
64
+ *******
65
+
66
+ - `Changed` `tb pull` to overwrite existing local files when pulling from a Tinybird workspace.
67
+
55
68
  4.6.0
56
69
  *******
57
70
 
@@ -28,6 +28,8 @@ tinybird/datafile/exceptions.py
28
28
  tinybird/datafile/parse_connection.py
29
29
  tinybird/datafile/parse_datasource.py
30
30
  tinybird/datafile/parse_pipe.py
31
+ tinybird/iterating/__init__.py
32
+ tinybird/iterating/data_branch_modes.py
31
33
  tinybird/tb/__cli__.py
32
34
  tinybird/tb/check_pypi.py
33
35
  tinybird/tb/cli.py
@@ -102,7 +104,6 @@ tinybird/tb/modules/datafile/pipe_checker.py
102
104
  tinybird/tb/modules/datafile/playground.py
103
105
  tinybird/tb/modules/datafile/pull.py
104
106
  tinybird/tb/modules/tinyunit/tinyunit.py
105
- tinybird/tb/modules/tinyunit/tinyunit_lib.py
106
107
  tinybird/tb_cli_modules/auth.py
107
108
  tinybird/tb_cli_modules/branch.py
108
109
  tinybird/tb_cli_modules/cicd.py
@@ -121,5 +122,4 @@ tinybird/tb_cli_modules/telemetry.py
121
122
  tinybird/tb_cli_modules/test.py
122
123
  tinybird/tb_cli_modules/workspace.py
123
124
  tinybird/tb_cli_modules/workspace_members.py
124
- tinybird/tb_cli_modules/tinyunit/tinyunit.py
125
- tinybird/tb_cli_modules/tinyunit/tinyunit_lib.py
125
+ tinybird/tb_cli_modules/tinyunit/tinyunit.py
@@ -18,7 +18,7 @@ requests<3,>=2.28.1
18
18
  shandy-sqlfmt==0.11.1
19
19
  shandy-sqlfmt[jinjafmt]==0.11.1
20
20
  toposort==1.10
21
- tornado~=6.0.0
21
+ tornado~=6.5.5
22
22
  urllib3<2,>=1.26.14
23
23
  watchdog==6.0.0
24
24
  wheel
@@ -1,65 +0,0 @@
1
- import json
2
- from collections import namedtuple
3
- from json import JSONEncoder
4
- from typing import Optional
5
-
6
-
7
- class MyJSONEncoder(JSONEncoder):
8
- # def default(self, in_obj):
9
- # loaded_data = [
10
- # DataUnitTest(
11
- # getattr(unitDataTest, 'name'),
12
- # getattr(unitDataTest, 'description'),
13
- # getattr(unitDataTest, 'enabled'),
14
- # getattr(unitDataTest, 'endpoint'),
15
- # getattr(unitDataTest, 'result'),
16
- # getattr(unitDataTest, 'time'),
17
- # getattr(unitDataTest, 'sql'))
18
- # for unitDataTest in in_obj]
19
- # return loaded_data
20
- def default(self, obj):
21
- return obj.to_json()
22
-
23
-
24
- class DataUnitTest:
25
- def __init__(
26
- self,
27
- name: str,
28
- description: str,
29
- enabled: bool,
30
- endpoint: Optional[str],
31
- result: Optional[str],
32
- time: int,
33
- sql: str,
34
- ):
35
- self.name = name
36
- self.description = description
37
- self.enabled = enabled
38
- self.endpoint = endpoint
39
- self.result = result
40
- self.time = time
41
- self.sql = sql
42
-
43
- def __iter__(self):
44
- yield from {
45
- "name": self.name,
46
- "description": self.description,
47
- "enabled": self.enabled,
48
- "endpoint": self.endpoint,
49
- "sql": self.sql,
50
- "result": self.result,
51
- "time": self.time,
52
- }.items()
53
-
54
- def __str__(self):
55
- return json.dumps(dict(self), ensure_ascii=False)
56
-
57
- def __repr__(self):
58
- return self.__str__()
59
-
60
- def to_json(self):
61
- return self.__str__()
62
-
63
-
64
- def customDataUnitTestDecoder(dataUnitTestDict):
65
- return namedtuple("X", dataUnitTestDict.keys())(*dataUnitTestDict.values())
@@ -1,65 +0,0 @@
1
- import json
2
- from collections import namedtuple
3
- from json import JSONEncoder
4
- from typing import Optional
5
-
6
-
7
- class MyJSONEncoder(JSONEncoder):
8
- # def default(self, in_obj):
9
- # loaded_data = [
10
- # DataUnitTest(
11
- # getattr(unitDataTest, 'name'),
12
- # getattr(unitDataTest, 'description'),
13
- # getattr(unitDataTest, 'enabled'),
14
- # getattr(unitDataTest, 'endpoint'),
15
- # getattr(unitDataTest, 'result'),
16
- # getattr(unitDataTest, 'time'),
17
- # getattr(unitDataTest, 'sql'))
18
- # for unitDataTest in in_obj]
19
- # return loaded_data
20
- def default(self, obj):
21
- return obj.to_json()
22
-
23
-
24
- class DataUnitTest:
25
- def __init__(
26
- self,
27
- name: str,
28
- description: str,
29
- enabled: bool,
30
- endpoint: Optional[str],
31
- result: Optional[str],
32
- time: int,
33
- sql: str,
34
- ):
35
- self.name = name
36
- self.description = description
37
- self.enabled = enabled
38
- self.endpoint = endpoint
39
- self.result = result
40
- self.time = time
41
- self.sql = sql
42
-
43
- def __iter__(self):
44
- yield from {
45
- "name": self.name,
46
- "description": self.description,
47
- "enabled": self.enabled,
48
- "endpoint": self.endpoint,
49
- "sql": self.sql,
50
- "result": self.result,
51
- "time": self.time,
52
- }.items()
53
-
54
- def __str__(self):
55
- return json.dumps(dict(self), ensure_ascii=False)
56
-
57
- def __repr__(self):
58
- return self.__str__()
59
-
60
- def to_json(self):
61
- return self.__str__()
62
-
63
-
64
- def customDataUnitTestDecoder(dataUnitTestDict):
65
- return namedtuple("X", dataUnitTestDict.keys())(*dataUnitTestDict.values())
File without changes
File without changes