tinybird 4.6.23.dev0__tar.gz → 4.6.24.dev0__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 (130) hide show
  1. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/PKG-INFO +6 -1
  2. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/pyproject.toml +28 -25
  3. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/client.py +0 -8
  4. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/sql_template.py +55 -24
  5. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/sql_template_fmt.py +2 -2
  6. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/syncasync.py +0 -43
  7. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/__cli__.py +2 -2
  8. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/client.py +0 -8
  9. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/common.py +3 -3
  10. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/create.py +2 -4
  11. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/datafile/pull.py +73 -3
  12. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/datasource.py +1 -1
  13. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/exceptions.py +0 -21
  14. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb_cli_modules/common.py +3 -3
  15. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb_cli_modules/datasource.py +1 -1
  16. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tornado_template.py +38 -93
  17. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird.egg-info/PKG-INFO +6 -1
  18. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/setup.cfg +0 -0
  19. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/__cli__.py +0 -0
  20. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/ch_utils/constants.py +0 -0
  21. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/ch_utils/engine.py +0 -0
  22. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/check_pypi.py +0 -0
  23. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/config.py +0 -0
  24. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/context.py +0 -0
  25. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/datafile/common.py +0 -0
  26. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/datafile/exceptions.py +0 -0
  27. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/datafile/parse_connection.py +0 -0
  28. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/datafile/parse_datasource.py +0 -0
  29. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/datafile/parse_pipe.py +0 -0
  30. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/datatypes.py +0 -0
  31. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/feedback_manager.py +0 -0
  32. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/git_settings.py +0 -0
  33. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/iterating/__init__.py +0 -0
  34. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/iterating/data_branch_modes.py +0 -0
  35. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/prompts.py +0 -0
  36. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/service_datasources.py +0 -0
  37. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/sql.py +0 -0
  38. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/sql_toolset.py +0 -0
  39. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/check_pypi.py +0 -0
  40. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/cli.py +0 -0
  41. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/config.py +0 -0
  42. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/branch.py +0 -0
  43. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/build.py +0 -0
  44. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/build_common.py +0 -0
  45. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/cicd.py +0 -0
  46. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/cli.py +0 -0
  47. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/config.py +0 -0
  48. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/connection.py +0 -0
  49. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/connection_dynamodb.py +0 -0
  50. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/connection_kafka.py +0 -0
  51. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/connection_s3.py +0 -0
  52. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/copy.py +0 -0
  53. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/datafile/build.py +0 -0
  54. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/datafile/build_common.py +0 -0
  55. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/datafile/build_datasource.py +0 -0
  56. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/datafile/build_pipe.py +0 -0
  57. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/datafile/diff.py +0 -0
  58. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/datafile/fixture.py +0 -0
  59. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/datafile/format_common.py +0 -0
  60. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/datafile/format_connection.py +0 -0
  61. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/datafile/format_datasource.py +0 -0
  62. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/datafile/format_pipe.py +0 -0
  63. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/datafile/pipe_checker.py +0 -0
  64. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/datafile/playground.py +0 -0
  65. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/deployment.py +0 -0
  66. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/deployment_common.py +0 -0
  67. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/deprecations.py +0 -0
  68. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/endpoint.py +0 -0
  69. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/feedback_manager.py +0 -0
  70. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/fmt.py +0 -0
  71. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/info.py +0 -0
  72. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/infra.py +0 -0
  73. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/job.py +0 -0
  74. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/job_common.py +0 -0
  75. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/llm.py +0 -0
  76. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/llm_utils.py +0 -0
  77. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/local.py +0 -0
  78. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/local_common.py +0 -0
  79. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/local_logs.py +0 -0
  80. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/login.py +0 -0
  81. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/login_common.py +0 -0
  82. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/logout.py +0 -0
  83. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/logs.py +0 -0
  84. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/materialization.py +0 -0
  85. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/open.py +0 -0
  86. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/pipe.py +0 -0
  87. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/preview.py +0 -0
  88. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/project.py +0 -0
  89. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/project_commands.py +0 -0
  90. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/py_project.py +0 -0
  91. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/query_output.py +0 -0
  92. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/regions.py +0 -0
  93. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/secret.py +0 -0
  94. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/secret_common.py +0 -0
  95. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/sink.py +0 -0
  96. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/table.py +0 -0
  97. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/telemetry.py +0 -0
  98. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/test.py +0 -0
  99. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/test_common.py +0 -0
  100. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/tinyunit/tinyunit.py +0 -0
  101. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/token.py +0 -0
  102. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/ts_project.py +0 -0
  103. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/watch.py +0 -0
  104. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/workspace.py +0 -0
  105. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb/modules/workspace_members.py +0 -0
  106. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb_cli.py +0 -0
  107. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb_cli_modules/auth.py +0 -0
  108. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb_cli_modules/branch.py +0 -0
  109. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb_cli_modules/cicd.py +0 -0
  110. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb_cli_modules/cli.py +0 -0
  111. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb_cli_modules/config.py +0 -0
  112. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb_cli_modules/connection.py +0 -0
  113. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb_cli_modules/exceptions.py +0 -0
  114. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb_cli_modules/fmt.py +0 -0
  115. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb_cli_modules/job.py +0 -0
  116. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb_cli_modules/pipe.py +0 -0
  117. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb_cli_modules/regions.py +0 -0
  118. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb_cli_modules/tag.py +0 -0
  119. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb_cli_modules/telemetry.py +0 -0
  120. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb_cli_modules/test.py +0 -0
  121. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb_cli_modules/tinyunit/tinyunit.py +0 -0
  122. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb_cli_modules/workspace.py +0 -0
  123. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/tb_cli_modules/workspace_members.py +0 -0
  124. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/utils/__init__.py +0 -0
  125. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird/utils/bools.py +0 -0
  126. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird.egg-info/SOURCES.txt +0 -0
  127. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird.egg-info/dependency_links.txt +0 -0
  128. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird.egg-info/entry_points.txt +0 -0
  129. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/tinybird.egg-info/requires.txt +0 -0
  130. {tinybird-4.6.23.dev0 → tinybird-4.6.24.dev0}/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.23.dev0
3
+ Version: 4.6.24.dev0
4
4
  Summary: Tinybird Command Line Tool
5
5
  Home-page: https://www.tinybird.co/docs/forward/commands
6
6
  Author: Tinybird
@@ -52,6 +52,11 @@ The Tinybird command-line tool allows you to use all the Tinybird functionality
52
52
  Changelog
53
53
  ----------
54
54
 
55
+ 4.6.22
56
+ ********
57
+
58
+ - `Added` ``tb pull`` demotes sink and copy pipes that exceed the workspace's sink/copy pipe plan limit to plain pipes instead of failing, stripping their ``TYPE``/``EXPORT_*``/``TARGET_DATASOURCE``/``COPY_SCHEDULE`` settings and warning that the plan must be upgraded to restore them.
59
+
55
60
  4.6.21
56
61
  ********
57
62
 
@@ -18,35 +18,35 @@ authors = [
18
18
  dependencies = [
19
19
  "GitPython==3.1.44",
20
20
  "Markdown==3.5.1",
21
- "aioboto3==14.0.0",
21
+ "aioboto3~=15.0",
22
22
  "aiofiles==24.1.0",
23
- "aiohttp==3.11.2",
24
- "anthropic==0.55.0",
23
+ "aiohttp~=3.11",
24
+ "anthropic~=1.9",
25
25
  "async-lru==2.3.0",
26
26
  "aws-msk-iam-sasl-signer-python==1.0.2",
27
27
  "cachetools==5.5.0",
28
28
  "cffi==1.17.1",
29
29
  "chardet==4.0.0",
30
30
  "ciso8601==2.2.0",
31
- "click==8.1.8",
31
+ "click~=8.5",
32
32
  "clickhouse-cityhash==1.0.2.5",
33
33
  "clickhouse-driver==0.2.9",
34
34
  "confluent-kafka[avro,json,schemaregistry]==2.12.2",
35
35
  "contextvars==2.4",
36
36
  "cramjam==2.11.0",
37
37
  "croniter==6.2.2",
38
- "cryptography==3.4.8",
38
+ "cryptography~=38.0",
39
39
  "datasketch==1.6.5",
40
40
  "dnspython==2.8.0",
41
41
  "docker==7.1.0",
42
42
  "fastavro==1.12.2",
43
43
  "fastmcp==2.12.2",
44
- "google-cloud-aiplatform==1.148.1",
44
+ "google-cloud-aiplatform~=1.148",
45
45
  "google-cloud-compute==1.48.0",
46
46
  "google-cloud-scheduler==2.20.0",
47
47
  "google-cloud-secret-manager==2.29.0",
48
- "google-cloud-storage==3.4.1",
49
- "google_api_core==2.25.2",
48
+ "google-cloud-storage~=3.4",
49
+ "google_api_core~=2.25",
50
50
  "googleapis_common_protos==1.66.0",
51
51
  "grpcio_status==1.67.1",
52
52
  "humanfriendly==10.0",
@@ -69,7 +69,7 @@ dependencies = [
69
69
  "opentelemetry-sdk==1.38.0",
70
70
  "orb-billing==2.17.0",
71
71
  "orjson==3.11.5",
72
- "packaging==23.2",
72
+ "packaging~=26.3",
73
73
  "passlib==1.7.1",
74
74
  "posthog==7.28.0",
75
75
  "pre-commit==4.6.2",
@@ -83,10 +83,10 @@ dependencies = [
83
83
  "py-spy==0.4.2",
84
84
  "pyarrow==23.0.1",
85
85
  "pycurl==7.45.7",
86
- "pydantic-ai-slim[anthropic]==0.5.0",
87
- "pydantic-ai-slim[google]==0.5.0",
88
- "pydantic-ai-slim[retries]==0.5.0",
89
- "pydantic==2.12.5",
86
+ "pydantic-ai-slim[anthropic]~=2.0",
87
+ "pydantic-ai-slim[google]~=2.0",
88
+ "pydantic-ai-slim[retries]~=2.0",
89
+ "pydantic~=2.12",
90
90
  "pyjwt[crypto]==2.9.0",
91
91
  "pympler==1.1",
92
92
  "pynacl==1.5.0",
@@ -94,10 +94,10 @@ dependencies = [
94
94
  "python-dateutil==2.9.0.post0",
95
95
  "python-dotenv==1.1.0",
96
96
  "python-ulid==2.7.0",
97
- "pyyaml==6.0.1",
97
+ "pyyaml~=6.0",
98
98
  "redis[hiredis]==5.3.1",
99
99
  "requests==2.32.4",
100
- "sentry-sdk==2.69.1",
100
+ "sentry-sdk==2.69.2",
101
101
  "setproctitle==1.3.3",
102
102
  "shandy-sqlfmt==0.11.1",
103
103
  "shandy-sqlfmt[jinjafmt]==0.11.1",
@@ -108,7 +108,7 @@ dependencies = [
108
108
  "tabulate==0.8.10",
109
109
  "thefuzz==0.22.1",
110
110
  "tinybird-python-sdk[ai]==0.3.7",
111
- "tinybird-toolset==2.6.2",
111
+ "tinybird-toolset==2.6.6",
112
112
  "toposort==1.10",
113
113
  "tornado==6.5.5",
114
114
  "urllib3==1.26.20",
@@ -125,9 +125,9 @@ deploy = [
125
125
  "GitPython==3.1.44",
126
126
  "ansible==8.5.0",
127
127
  "ara==1.6.1",
128
- "boto3==1.36.21",
128
+ "boto3~=1.40",
129
129
  "fabric==3.2.2",
130
- "google-auth==2.47.0",
130
+ "google-auth~=2.47",
131
131
  "jsonnet",
132
132
  "prettytable==3.8.0",
133
133
  "requests==2.32.4",
@@ -142,7 +142,7 @@ devtools = [
142
142
  ]
143
143
  doc = [
144
144
  "Pallets-Sphinx-Themes==2.3.0",
145
- "pyOpenSSL<24.0.0",
145
+ "pyOpenSSL~=23.2",
146
146
  "sphinx-autobuild==2024.2.4",
147
147
  "sphinx-sitemap",
148
148
  "sphinx==7.3.7",
@@ -150,15 +150,15 @@ doc = [
150
150
  "sphinxcontrib-jquery==4.1",
151
151
  ]
152
152
  lint = [
153
- "botocore-stubs==1.36.23",
153
+ "botocore-stubs~=1.40",
154
154
  "flake8~=7.3",
155
155
  "flake8-async~=26.8",
156
156
  "mypy==2.3.1",
157
- "ruff==0.16.8",
157
+ "ruff==0.16.9",
158
158
  "types-Markdown",
159
159
  "types-PyYAML",
160
160
  "types-aiofiles==24.1.0.20241221",
161
- "types-boto3-lite==1.36.21",
161
+ "types-boto3-lite~=1.40",
162
162
  "types-cachetools",
163
163
  "types-chardet",
164
164
  "types-paramiko",
@@ -172,18 +172,19 @@ lint = [
172
172
  test = [
173
173
  "coverage==7.10.6",
174
174
  "freezegun==1.5.5",
175
- "google-auth==2.47.0",
175
+ "google-auth~=2.47",
176
176
  "gsutil==4.58",
177
+ "httpx2~=2.13",
177
178
  "hypothesis==6.156.1",
178
179
  "oauth2client==3.0.0",
179
180
  "py==1.11.0",
180
- "pytest-asyncio==0.23.8",
181
+ "pytest-asyncio~=1.4",
181
182
  "pytest-cov==4.1.0",
182
183
  "pytest-env==1.1.3",
183
184
  "pytest-flakefinder==1.1.0",
184
185
  "pytest-timeout==2.3.1",
185
186
  "pytest-xdist==3.6.1",
186
- "pytest==8.1.2",
187
+ "pytest~=8.1",
187
188
  "rangehttpserver==1.4.0",
188
189
  "yappi==1.7.6",
189
190
  ]
@@ -205,6 +206,8 @@ colibri = "tinybird.colibri.cli:cli"
205
206
  members = [
206
207
  "chbackups",
207
208
  "data_connector",
209
+ "gatherer",
210
+ "tinybird_orchestrator",
208
211
  ]
209
212
 
210
213
  [tool.setuptools]
@@ -47,14 +47,6 @@ class TimeoutException(Exception):
47
47
  pass
48
48
 
49
49
 
50
- class ReachRetryLimit(Exception):
51
- pass
52
-
53
-
54
- class ConnectorNothingToLoad(Exception):
55
- pass
56
-
57
-
58
50
  class JobException(Exception):
59
51
  pass
60
52
 
@@ -1,5 +1,4 @@
1
1
  import ast
2
- import builtins
3
2
  import linecache
4
3
  import logging
5
4
  import re
@@ -1336,8 +1335,10 @@ sqlescape_for_string_expression = sqlescape_generator(
1336
1335
  )
1337
1336
 
1338
1337
 
1339
- def escape_single_quote_str(s):
1340
- return "'" + s.replace("'", "''") + "'"
1338
+ def escape_single_quote_str(s: str) -> str:
1339
+ # ClickHouse string literals treat backslash as an escape char, so it must be doubled too:
1340
+ # otherwise a trailing backslash escapes the closing quote and the following input becomes SQL
1341
+ return "'" + s.replace("\\", "\\\\").replace("'", "''") + "'"
1341
1342
 
1342
1343
 
1343
1344
  def expression_wrapper(x, name, escape_arrays: bool = False):
@@ -1388,13 +1389,14 @@ _namespace = {
1388
1389
  }
1389
1390
 
1390
1391
 
1391
- reserved_vars = {"_tt_tmp", "_tt_append", "isinstance", "str", "error", "custom_error", *list(vars(builtins))}
1392
+ # Built from the builtins templates can actually resolve: names missing from SAFE_BUILTINS (`type`, `dir`,
1393
+ # `property`, `object`, ...) can only be template parameters, so they must be discovered as such to get a
1394
+ # value (None or a test-mode placeholder) instead of raising NameError.
1395
+ reserved_vars = {"_tt_tmp", "_tt_append", "isinstance", "str", "error", "custom_error", *SAFE_BUILTINS}
1392
1396
  for p in DEFAULT_PARAM_NAMES: # we handle these in an specific manner
1393
1397
  reserved_vars.discard(p) # `format` is part of builtins
1394
- # Allow 'id', 'type' and 'dir' as template parameter names despite being builtins.
1398
+ # Allow 'id' as a template parameter name despite being a builtin - https://gitlab.com/tinybird/analytics/-/issues/19119
1395
1399
  reserved_vars.discard("id")
1396
- reserved_vars.discard("type")
1397
- reserved_vars.discard("dir")
1398
1400
  error_vars = ["error", "custom_error"]
1399
1401
 
1400
1402
 
@@ -1470,8 +1472,8 @@ def generate(self, **kwargs) -> Tuple[str, TemplateExecutionResults]:
1470
1472
  return Expression(f"-- disable {feature}\n")
1471
1473
 
1472
1474
  namespace.update(_namespace)
1473
- # Default these here, not in _namespace, so they stay visible to variable extraction.
1474
- namespace.update({"id": None, "type": None, "dir": None})
1475
+ # Default it here, not in _namespace, so it stays visible to variable extraction.
1476
+ namespace.update({"id": None})
1475
1477
  namespace.update(kwargs)
1476
1478
  namespace.update(
1477
1479
  {
@@ -1820,7 +1822,7 @@ def get_var_names(t: Template):
1820
1822
  >>> [v['name'] for v in get_var_names(Template("{% for name in secret_names %}{{tb_secret(name)}}{% end %}"))]
1821
1823
  ['secret_names', 'name', 'tb_secret', 'name']
1822
1824
 
1823
- Type casting with date function in elif chain (type is in reserved_vars):
1825
+ Type casting with date function in elif chain:
1824
1826
  >>> t = Template("{% if kind == 'int' %}{{Int32(val, 0)}}{% elif kind == 'date' %}{{DateTime(val)}}{% elif kind == 'diff' %}{{date_diff_in_days(val, now)}}{% end %}")
1825
1827
  >>> [v['name'] for v in get_var_names(t)]
1826
1828
  ['kind', 'Int32', 'val', 'kind', 'DateTime', 'val', 'kind', 'date_diff_in_days', 'val', 'now']
@@ -2372,7 +2374,24 @@ def wrap_vars(t, escape_arrays: bool = False):
2372
2374
  return var
2373
2375
 
2374
2376
 
2375
- def get_used_tables_in_template(sql):
2377
+ def _constant_table_name(node: ast.expr) -> Optional[str]:
2378
+ """Value of a constant table() argument: a literal or a concatenation of string literals."""
2379
+ if isinstance(node, ast.Constant) and isinstance(node.value, (int, float)):
2380
+ return str(node.value)
2381
+ return _string_literal(node)
2382
+
2383
+
2384
+ def _string_literal(node: ast.expr) -> Optional[str]:
2385
+ if isinstance(node, ast.Constant) and isinstance(node.value, str):
2386
+ return node.value
2387
+ if isinstance(node, ast.BinOp) and isinstance(node.op, ast.Add):
2388
+ left, right = _string_literal(node.left), _string_literal(node.right)
2389
+ if left is not None and right is not None:
2390
+ return left + right
2391
+ return None
2392
+
2393
+
2394
+ def get_used_tables_in_template(sql: str) -> List[str]:
2376
2395
  """
2377
2396
  >>> get_used_tables_in_template("select * from {{table('test')}}")
2378
2397
  ['test']
@@ -2386,30 +2405,42 @@ def get_used_tables_in_template(sql):
2386
2405
  []
2387
2406
  >>> get_used_tables_in_template("select * from {{table('my.test')}}, {{table('another.one')}}")
2388
2407
  ['my.test', 'another.one']
2408
+
2409
+ Only constant table names can be known without rendering, so anything else is skipped
2410
+ >>> get_used_tables_in_template("select * from {{table(tbl)}}, {{TABLE('b')}}, {{table('my.' + 'c')}}")
2411
+ ['b', 'my.c']
2389
2412
  """
2413
+ return list(_get_used_tables_in_template_cached(sql))
2414
+
2415
+
2416
+ # Building the Template dominates the cost and the result only depends on the SQL, which deployments and
2417
+ # /v0/sql pipelines scan repeatedly
2418
+ @lru_cache(maxsize=2**10)
2419
+ def _get_used_tables_in_template_cached(sql: str) -> Tuple[str, ...]:
2390
2420
  try:
2391
2421
  t = Template(sql)
2392
2422
 
2393
2423
  def _n(chunks, tables):
2394
2424
  for x in chunks:
2395
2425
  if type(x).__name__ == "_Expression":
2396
- c = compile(x.expression, "<string>", "exec", dont_inherit=True)
2397
- v = [x.lower() for x in c.co_names if x not in _namespace and x not in reserved_vars]
2398
- if "table" in v:
2399
-
2400
- def _t(*args, **kwargs):
2401
- return str(args[0])
2402
-
2403
- n = {"table": _t, "TABLE": _t}
2404
- e = "_tt_tmp = %s" % x.expression
2405
- exec_in(e, n)
2406
- tables += [n["_tt_tmp"]]
2426
+ # Read the names from the AST instead of executing the expression: this runs outside
2427
+ # generate(), without the restricted builtins, on every pipe, query and deployment.
2428
+ for node in ast.walk(ast.parse(x.expression)):
2429
+ if (
2430
+ isinstance(node, ast.Call)
2431
+ and isinstance(node.func, ast.Name)
2432
+ and node.func.id in ("table", "TABLE")
2433
+ and node.args
2434
+ ):
2435
+ name = _constant_table_name(node.args[0])
2436
+ if name is not None:
2437
+ tables.append(name)
2407
2438
  elif type(x).__name__ == "_ControlBlock":
2408
2439
  _n(x.body.chunks, tables)
2409
2440
 
2410
- tables = []
2441
+ tables: List[str] = []
2411
2442
  _n(t.file.body.chunks, tables)
2412
- return tables
2443
+ return tuple(tables)
2413
2444
  except SecurityException as e:
2414
2445
  raise SQLTemplateException(e)
2415
2446
 
@@ -214,8 +214,8 @@ class TinybirdDialect(ClickHouse):
214
214
  ],
215
215
  }
216
216
 
217
- for section in override_rules:
218
- for rule in override_rules[section]:
217
+ for section, rules in override_rules.items():
218
+ for rule in rules:
219
219
  for rr in self.RULES[section]:
220
220
  if rr.name == rule.name:
221
221
  self.RULES[section].remove(rr)
@@ -260,49 +260,6 @@ def _iscoroutinefunction_or_partial(func: Any) -> bool:
260
260
  return asyncio.iscoroutinefunction(func)
261
261
 
262
262
 
263
- class ThreadSensitiveContext:
264
- """Async context manager to manage context for thread sensitive mode
265
- This context manager controls which thread pool executor is used when in
266
- thread sensitive mode. By default, a single thread pool executor is shared
267
- within a process.
268
- In Python 3.7+, the ThreadSensitiveContext() context manager may be used to
269
- specify a thread pool per context.
270
- In Python 3.6, usage of this context manager has no effect.
271
- This context manager is re-entrant, so only the outer-most call to
272
- ThreadSensitiveContext will set the context.
273
- """
274
-
275
- def __init__(self):
276
- self.token = None
277
-
278
- if contextvars:
279
-
280
- async def __aenter__(self):
281
- try:
282
- SyncToAsync.thread_sensitive_context.get()
283
- except LookupError:
284
- self.token = SyncToAsync.thread_sensitive_context.set(self)
285
-
286
- return self
287
-
288
- async def __aexit__(self, exc, value, tb):
289
- if not self.token:
290
- return
291
-
292
- executor = SyncToAsync.context_to_thread_executor.pop(self, None)
293
- if executor:
294
- executor.shutdown()
295
- SyncToAsync.thread_sensitive_context.reset(self.token)
296
-
297
- else:
298
-
299
- async def __aenter__(self):
300
- return self
301
-
302
- async def __aexit__(self, exc, value, tb):
303
- pass
304
-
305
-
306
263
  class AsyncToSync:
307
264
  """
308
265
  Utility class which turns an awaitable that only works on the thread with
@@ -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.23.dev0'
8
- __revision__ = '2456d09e'
7
+ __version__ = '4.6.24.dev0'
8
+ __revision__ = '5269fe22'
@@ -58,14 +58,6 @@ class TimeoutException(Exception):
58
58
  pass
59
59
 
60
60
 
61
- class ReachRetryLimit(Exception):
62
- pass
63
-
64
-
65
- class ConnectorNothingToLoad(Exception):
66
- pass
67
-
68
-
69
61
  class JobException(Exception):
70
62
  pass
71
63
 
@@ -1277,11 +1277,11 @@ class ConnectionReplacements:
1277
1277
  """Maps prompt parameters to API parameters."""
1278
1278
 
1279
1279
  api_params = {}
1280
- for key in params.keys():
1280
+ for key, value in params.items():
1281
1281
  try:
1282
- api_params[ConnectionReplacements._PARAMS_REPLACEMENTS[service][key]] = params[key]
1282
+ api_params[ConnectionReplacements._PARAMS_REPLACEMENTS[service][key]] = value
1283
1283
  except KeyError:
1284
- api_params[key] = params[key]
1284
+ api_params[key] = value
1285
1285
 
1286
1286
  api_params["service"] = service
1287
1287
  return api_params
@@ -215,14 +215,12 @@ def create_project_structure(folder: str):
215
215
  "connections →": "Connect to and ingest data from popular sources: Kafka, S3 or GCS.",
216
216
  }
217
217
 
218
- for x in PROJECT_PATHS_DESCRIPTIONS.keys():
218
+ for x, description in PROJECT_PATHS_DESCRIPTIONS.items():
219
219
  try:
220
220
  path = x.split("→")[0].strip()
221
221
  f = folder_path / path
222
222
  f.mkdir()
223
- click.echo(
224
- FeedbackManager.info(message=f"./{x} ") + FeedbackManager.gray(message=PROJECT_PATHS_DESCRIPTIONS[x])
225
- )
223
+ click.echo(FeedbackManager.info(message=f"./{x} ") + FeedbackManager.gray(message=description))
226
224
  except FileExistsError:
227
225
  pass
228
226
 
@@ -1,8 +1,11 @@
1
+ from datetime import datetime
1
2
  from pathlib import Path
2
3
  from typing import Any, Optional
3
4
 
4
5
  import click
5
6
 
7
+ from tinybird.datafile.common import PipeNodeTypes
8
+ from tinybird.datafile.parse_pipe import parse_pipe
6
9
  from tinybird.tb.client import AuthNoTokenException, TinyB
7
10
  from tinybird.tb.modules.datafile.format_datasource import format_datasource
8
11
  from tinybird.tb.modules.datafile.format_pipe import format_pipe
@@ -10,6 +13,52 @@ from tinybird.tb.modules.feedback_manager import FeedbackManager
10
13
 
11
14
  PULL_EXISTING_FILE_MAX_DEPTH = 5
12
15
 
16
+ DEMOTABLE_NODE_TYPES = {
17
+ PipeNodeTypes.DATA_SINK: ("sink_pipes", "Sink"),
18
+ PipeNodeTypes.COPY: ("copy_pipes", "Copy"),
19
+ }
20
+
21
+
22
+ def _parse_created_at(value: Any) -> datetime:
23
+ try:
24
+ return datetime.fromisoformat(str(value))
25
+ except ValueError:
26
+ return datetime.min
27
+
28
+
29
+ def pipes_over_node_type_limit(pipes: list[dict[str, Any]], node_type: str, limit: Optional[int]) -> set[str]:
30
+ if limit is None:
31
+ return set()
32
+
33
+ matching_pipes = [p for p in pipes if p.get("type") == node_type]
34
+ if len(matching_pipes) <= limit:
35
+ return set()
36
+
37
+ matching_pipes.sort(key=lambda p: _parse_created_at(p.get("created_at")))
38
+ return {p["name"] for p in matching_pipes[limit:]}
39
+
40
+
41
+ def pipes_over_plan_limits(pipes: list[dict[str, Any]], limits: dict[str, Any]) -> dict[str, str]:
42
+ demoted: dict[str, str] = {}
43
+ for node_type, (limits_key, _label) in DEMOTABLE_NODE_TYPES.items():
44
+ limit = limits.get(limits_key, {}).get("max")
45
+ for name in pipes_over_node_type_limit(pipes, node_type, limit):
46
+ demoted[name] = node_type
47
+ return demoted
48
+
49
+
50
+ def demote_pipe_content(name: str, content: str, node_type: str) -> str:
51
+ datafile = parse_pipe(name, content=content, replace_includes=False).datafile
52
+ demoted = False
53
+ for node in datafile.nodes:
54
+ if (node.get("type") or "").lower() == node_type:
55
+ node.pop("type", None)
56
+ demoted = True
57
+
58
+ if not demoted:
59
+ return content
60
+ return format_pipe(name, datafile=datafile)
61
+
13
62
 
14
63
  def folder_pull(
15
64
  client: TinyB,
@@ -61,6 +110,7 @@ def folder_pull(
61
110
  get_resource_function: str,
62
111
  progress_bar: bool = False,
63
112
  fmt: bool = False,
113
+ demoted_pipe_types: Optional[dict[str, str]] = None,
64
114
  ) -> list[Path]:
65
115
  def write_resource(k: dict[str, Any]) -> Optional[Path]:
66
116
  name = f"{k['name']}.{extension}"
@@ -73,8 +123,19 @@ def folder_pull(
73
123
  else:
74
124
  resource = getattr(client, get_resource_function)(k["name"])
75
125
  resource_to_write = resource
76
-
77
- if fmt:
126
+ resource_type = k.get("type")
127
+
128
+ demoted_node_type = demoted_pipe_types.get(k["name"]) if demoted_pipe_types else None
129
+ if demoted_node_type:
130
+ resource_to_write = demote_pipe_content(name, resource, demoted_node_type)
131
+ resource_type = None
132
+ label = DEMOTABLE_NODE_TYPES[demoted_node_type][1]
133
+ warnings.append(
134
+ f"{label} pipe '{k['name']}' exceeds the {demoted_node_type} pipe limit of your current "
135
+ f"plan and was pulled as a plain pipe without its {demoted_node_type} configuration. "
136
+ "Upgrade your plan and reconfigure it to restore it."
137
+ )
138
+ elif fmt:
78
139
  if extension == "datasource":
79
140
  resource_to_write = format_datasource(name, content=resource)
80
141
  elif extension == "pipe":
@@ -85,7 +146,7 @@ def folder_pull(
85
146
  dest_folder = Path(folder) / "vendor" / k["name"].split(".", 1)[0]
86
147
  name = f"{k['name'].split('.', 1)[1]}.{extension}"
87
148
 
88
- file_folder = get_file_folder(extension, k.get("type"))
149
+ file_folder = get_file_folder(extension, resource_type)
89
150
  f = Path(dest_folder) / file_folder if file_folder is not None else Path(dest_folder)
90
151
 
91
152
  existing_file = find_existing_resource_file(Path(dest_folder), name)
@@ -128,6 +189,14 @@ def folder_pull(
128
189
  pipes = client.pipes()
129
190
  connections = client.connections()
130
191
 
192
+ demoted_pipe_types: dict[str, str] = {}
193
+ if not only_vendored:
194
+ try:
195
+ limits = client.workspace_info().get("limits", {})
196
+ except Exception:
197
+ limits = {}
198
+ demoted_pipe_types = pipes_over_plan_limits(pipes, limits)
199
+
131
200
  if only_vendored:
132
201
  written_files = write_files(
133
202
  resources=[ds for ds in datasources if "shared_from" in ds],
@@ -154,6 +223,7 @@ def folder_pull(
154
223
  get_resource_function="pipe_file",
155
224
  progress_bar=progress_bar,
156
225
  fmt=fmt,
226
+ demoted_pipe_types=demoted_pipe_types,
157
227
  )
158
228
  )
159
229
  written_files.extend(
@@ -677,7 +677,7 @@ def datasource_start(ctx: Context, datasource_name: str) -> None:
677
677
 
678
678
  @datasource.command(name="delete")
679
679
  @click.argument("datasource_name")
680
- @click.option("--sql-condition", default=None, help="SQL WHERE condition to remove rows", hidden=True, required=True)
680
+ @click.option("--sql-condition", help="SQL WHERE condition to remove rows", hidden=True, required=True)
681
681
  @click.option("--yes", is_flag=True, default=False, help="Do not ask for confirmation")
682
682
  @click.option(
683
683
  "--wait/--no-wait",
@@ -92,20 +92,6 @@ class CLITokenException(CLIException):
92
92
  super().__init__(message, "token_error", **kw_telemetry_event_data)
93
93
 
94
94
 
95
- class CLIBuildException(CLIException):
96
- """Exceptions generated by the build commands"""
97
-
98
- def __init__(self, message: str, **kw_telemetry_event_data: Any) -> None:
99
- super().__init__(message, "build_error", **kw_telemetry_event_data)
100
-
101
-
102
- class CLIDeploymentException(CLIException):
103
- """Exceptions generated by the deployment commands"""
104
-
105
- def __init__(self, message: str, **kw_telemetry_event_data: Any) -> None:
106
- super().__init__(message, "deployment_error", **kw_telemetry_event_data)
107
-
108
-
109
95
  class CLITestException(CLIException):
110
96
  """Exceptions generated by the test commands"""
111
97
 
@@ -127,13 +113,6 @@ class CLIMockException(CLIException):
127
113
  super().__init__(message, "mock_error", **kw_telemetry_event_data)
128
114
 
129
115
 
130
- class CLIAgentException(CLIException):
131
- """Exceptions generated by the agent commands"""
132
-
133
- def __init__(self, message: str, **kw_telemetry_event_data: Any) -> None:
134
- super().__init__(message, "agent_error", **kw_telemetry_event_data)
135
-
136
-
137
116
  class CLILoginException(CLIException):
138
117
  """Exceptions generated by the login commands"""
139
118
 
@@ -1304,11 +1304,11 @@ class ConnectionReplacements:
1304
1304
  """Maps prompt parameters to API parameters."""
1305
1305
 
1306
1306
  api_params = {}
1307
- for key in params.keys():
1307
+ for key, value in params.items():
1308
1308
  try:
1309
- api_params[ConnectionReplacements._PARAMS_REPLACEMENTS[service][key]] = params[key]
1309
+ api_params[ConnectionReplacements._PARAMS_REPLACEMENTS[service][key]] = value
1310
1310
  except KeyError:
1311
- api_params[key] = params[key]
1311
+ api_params[key] = value
1312
1312
 
1313
1313
  api_params["service"] = service
1314
1314
  return api_params
@@ -385,7 +385,7 @@ async def datasource_truncate(ctx, datasource_name, yes, cascade):
385
385
 
386
386
  @datasource.command(name="delete")
387
387
  @click.argument("datasource_name")
388
- @click.option("--sql-condition", default=None, help="SQL WHERE condition to remove rows", hidden=True, required=True)
388
+ @click.option("--sql-condition", help="SQL WHERE condition to remove rows", hidden=True, required=True)
389
389
  @click.option("--yes", is_flag=True, default=False, help="Do not ask for confirmation")
390
390
  @click.option(
391
391
  "--wait/--no-wait",