tinybird 4.6.21.dev0__tar.gz → 4.6.22.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.
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/PKG-INFO +6 -1
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/pyproject.toml +12 -1
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/client.py +4 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/datafile/common.py +9 -1
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/datatypes.py +34 -12
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/feedback_manager.py +5 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/sql_template.py +7 -2
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/__cli__.py +2 -2
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/client.py +4 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/copy.py +33 -1
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/datafile/build.py +1 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/datafile/format_pipe.py +4 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/datafile/playground.py +1 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/feedback_manager.py +5 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb_cli_modules/pipe.py +40 -21
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tornado_template.py +204 -0
- tinybird-4.6.22.dev0/tinybird/utils/__init__.py +0 -0
- tinybird-4.6.22.dev0/tinybird/utils/bools.py +19 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird.egg-info/PKG-INFO +6 -1
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird.egg-info/SOURCES.txt +3 -1
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/setup.cfg +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/__cli__.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/ch_utils/constants.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/ch_utils/engine.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/check_pypi.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/config.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/context.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/datafile/exceptions.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/datafile/parse_connection.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/datafile/parse_datasource.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/datafile/parse_pipe.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/git_settings.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/iterating/__init__.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/iterating/data_branch_modes.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/prompts.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/service_datasources.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/sql.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/sql_template_fmt.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/sql_toolset.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/syncasync.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/check_pypi.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/cli.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/config.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/branch.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/build.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/build_common.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/cicd.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/cli.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/common.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/config.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/connection.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/connection_dynamodb.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/connection_kafka.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/connection_s3.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/create.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/datafile/build_common.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/datafile/build_datasource.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/datafile/build_pipe.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/datafile/diff.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/datafile/fixture.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/datafile/format_common.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/datafile/format_connection.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/datafile/format_datasource.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/datafile/pipe_checker.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/datafile/pull.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/datasource.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/deployment.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/deployment_common.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/deprecations.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/endpoint.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/exceptions.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/fmt.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/info.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/infra.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/job.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/job_common.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/llm.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/llm_utils.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/local.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/local_common.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/local_logs.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/login.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/login_common.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/logout.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/logs.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/materialization.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/open.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/pipe.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/preview.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/project.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/project_commands.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/py_project.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/query_output.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/regions.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/secret.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/secret_common.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/sink.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/table.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/telemetry.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/test.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/test_common.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/tinyunit/tinyunit.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/token.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/ts_project.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/watch.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/workspace.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb/modules/workspace_members.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb_cli.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb_cli_modules/auth.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb_cli_modules/branch.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb_cli_modules/cicd.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb_cli_modules/cli.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb_cli_modules/common.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb_cli_modules/config.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb_cli_modules/connection.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb_cli_modules/datasource.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb_cli_modules/exceptions.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb_cli_modules/fmt.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb_cli_modules/job.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb_cli_modules/regions.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb_cli_modules/tag.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb_cli_modules/telemetry.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb_cli_modules/test.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb_cli_modules/tinyunit/tinyunit.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb_cli_modules/workspace.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird/tb_cli_modules/workspace_members.py +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird.egg-info/dependency_links.txt +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird.egg-info/entry_points.txt +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.dev0}/tinybird.egg-info/requires.txt +0 -0
- {tinybird-4.6.21.dev0 → tinybird-4.6.22.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.
|
|
3
|
+
Version: 4.6.22.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.20
|
|
56
|
+
********
|
|
57
|
+
|
|
58
|
+
- `Added` Copy pipes accept ``ON_DEMAND_COMPUTE true`` so scheduled runs use on-demand compute. Manual ``tb copy run`` still requires ``--on-demand-compute``, and asks for confirmation when the local datafile does not enable it (skip it with ``--yes``).
|
|
59
|
+
|
|
55
60
|
4.6.19
|
|
56
61
|
********
|
|
57
62
|
|
|
@@ -150,6 +150,8 @@ doc = [
|
|
|
150
150
|
]
|
|
151
151
|
lint = [
|
|
152
152
|
"botocore-stubs==1.36.23",
|
|
153
|
+
"flake8~=7.3",
|
|
154
|
+
"flake8-async~=26.8",
|
|
153
155
|
"mypy==2.3.1",
|
|
154
156
|
"ruff==0.16.8",
|
|
155
157
|
"types-Markdown",
|
|
@@ -184,6 +186,12 @@ test = [
|
|
|
184
186
|
"rangehttpserver==1.4.0",
|
|
185
187
|
"yappi==1.7.6",
|
|
186
188
|
]
|
|
189
|
+
# Synced automatically by plain `uv run`/`uv sync`, no `--group` needed.
|
|
190
|
+
dev = [
|
|
191
|
+
{ include-group = "test" },
|
|
192
|
+
{ include-group = "deploy" },
|
|
193
|
+
{ include-group = "lint" },
|
|
194
|
+
]
|
|
187
195
|
|
|
188
196
|
[project.scripts]
|
|
189
197
|
tinybird_server = "tinybird.app:run"
|
|
@@ -193,7 +201,10 @@ tb = "tinybird.tb.cli:cli"
|
|
|
193
201
|
colibri = "tinybird.colibri.cli:cli"
|
|
194
202
|
|
|
195
203
|
[tool.uv.workspace]
|
|
196
|
-
members = [
|
|
204
|
+
members = [
|
|
205
|
+
"chbackups",
|
|
206
|
+
"data_connector",
|
|
207
|
+
]
|
|
197
208
|
|
|
198
209
|
[tool.setuptools]
|
|
199
210
|
include-package-data = true
|
|
@@ -691,6 +691,7 @@ class TinyB:
|
|
|
691
691
|
target_datasource: Optional[str] = None,
|
|
692
692
|
schedule_cron: Optional[str] = None,
|
|
693
693
|
mode: Optional[str] = None,
|
|
694
|
+
on_demand_compute: Optional[bool] = None,
|
|
694
695
|
):
|
|
695
696
|
data = {"schedule_cron": schedule_cron}
|
|
696
697
|
|
|
@@ -700,6 +701,9 @@ class TinyB:
|
|
|
700
701
|
if mode:
|
|
701
702
|
data["mode"] = mode
|
|
702
703
|
|
|
704
|
+
if on_demand_compute is not None:
|
|
705
|
+
data["on_demand_compute"] = "true" if on_demand_compute else "false"
|
|
706
|
+
|
|
703
707
|
return await self._req(f"/v0/pipes/{pipe_name_or_id}/nodes/{node_id}/copy", method="PUT", data=data)
|
|
704
708
|
|
|
705
709
|
async def pipe_remove_copy(self, pipe_name_or_id: str, node_id: str):
|
|
@@ -28,6 +28,7 @@ from tinybird.ch_utils.engine import VALID_ENGINE_PARAMS, EngineParam
|
|
|
28
28
|
from tinybird.datafile.exceptions import IncludeFileNotFoundException, ParseException, ValidationException
|
|
29
29
|
from tinybird.tb.modules.exceptions import CLIPipeException
|
|
30
30
|
from tinybird.tb.modules.feedback_manager import FeedbackManager
|
|
31
|
+
from tinybird.utils.bools import parse_optional_bool
|
|
31
32
|
|
|
32
33
|
# Code from sql.py has been duplicated so I can change it without breaking absolutely everything in the app
|
|
33
34
|
# I'll try not to make logic changes, just error reporting changes
|
|
@@ -179,9 +180,10 @@ class CopyParameters(Parameters):
|
|
|
179
180
|
COPY_SCHEDULE = "copy_schedule"
|
|
180
181
|
COPY_MODE = "copy_mode"
|
|
181
182
|
COPY_MODE_ALIAS = "mode" # we need this because bot MODE and COPY_MODE go to `mode` variable inside the node
|
|
183
|
+
ON_DEMAND_COMPUTE = "on_demand_compute"
|
|
182
184
|
MANDATORY_ATTRIBUTES = PipeParameters.MANDATORY_ATTRIBUTES.union({TARGET_DATASOURCE})
|
|
183
185
|
ACCEPTED_ATTRIBUTES = PipeParameters.ACCEPTED_ATTRIBUTES.union(MANDATORY_ATTRIBUTES).union(
|
|
184
|
-
{COPY_SCHEDULE, COPY_MODE_ALIAS}
|
|
186
|
+
{COPY_SCHEDULE, COPY_MODE_ALIAS, ON_DEMAND_COMPUTE}
|
|
185
187
|
)
|
|
186
188
|
|
|
187
189
|
|
|
@@ -416,6 +418,11 @@ class Datafile:
|
|
|
416
418
|
)
|
|
417
419
|
if not croniter.is_valid(copy_schedule):
|
|
418
420
|
raise DatafileValidationError("COPY node schedule must be @on-demand or a valid cron expression.")
|
|
421
|
+
if CopyParameters.ON_DEMAND_COMPUTE in node:
|
|
422
|
+
try:
|
|
423
|
+
node[CopyParameters.ON_DEMAND_COMPUTE] = parse_optional_bool(node[CopyParameters.ON_DEMAND_COMPUTE])
|
|
424
|
+
except ValueError:
|
|
425
|
+
raise DatafileValidationError("ON_DEMAND_COMPUTE must be true or false")
|
|
419
426
|
for key in node.keys():
|
|
420
427
|
if key not in CopyParameters.valid_params():
|
|
421
428
|
raise DatafileValidationError(
|
|
@@ -2097,6 +2104,7 @@ def parse(
|
|
|
2097
2104
|
"copy_schedule": assign_node_var(CopyParameters.COPY_SCHEDULE),
|
|
2098
2105
|
"copy_mode": assign_node_var("mode"),
|
|
2099
2106
|
"mode": assign_node_var("mode"),
|
|
2107
|
+
"on_demand_compute": assign_node_var(CopyParameters.ON_DEMAND_COMPUTE),
|
|
2100
2108
|
"filter": assign_node_var("filter"),
|
|
2101
2109
|
"token": add_token,
|
|
2102
2110
|
"include": include,
|
|
@@ -120,52 +120,74 @@ def datetime_test(x: str) -> bool:
|
|
|
120
120
|
return any(p.match(x) for p in datetime_patterns)
|
|
121
121
|
|
|
122
122
|
|
|
123
|
+
def bounded_integer(x: str, pattern: re.Pattern[str]) -> Optional[int]:
|
|
124
|
+
if pattern.match(x) is None:
|
|
125
|
+
return None
|
|
126
|
+
digits = x.lstrip("+-").lstrip("0")
|
|
127
|
+
# 256-bit unsigned integers have at most 78 decimal digits.
|
|
128
|
+
if len(digits) > 78:
|
|
129
|
+
return None
|
|
130
|
+
return int(x) if len(x) <= 78 else int(digits or "0") * (-1 if x.startswith("-") else 1)
|
|
131
|
+
|
|
132
|
+
|
|
123
133
|
def int_8_test(x: str) -> bool:
|
|
124
|
-
|
|
134
|
+
value = bounded_integer(x, intx_re)
|
|
135
|
+
return value is not None and -int_8_max <= value < int_8_max
|
|
125
136
|
|
|
126
137
|
|
|
127
138
|
def int16_test(x: str) -> bool:
|
|
128
|
-
|
|
139
|
+
value = bounded_integer(x, intx_re)
|
|
140
|
+
return value is not None and -int16_max <= value < int16_max
|
|
129
141
|
|
|
130
142
|
|
|
131
143
|
def int32_test(x: str) -> bool:
|
|
132
|
-
|
|
144
|
+
value = bounded_integer(x, intx_re)
|
|
145
|
+
return value is not None and -int32_max <= value < int32_max
|
|
133
146
|
|
|
134
147
|
|
|
135
148
|
def int64_test(x: str) -> bool:
|
|
136
|
-
|
|
149
|
+
value = bounded_integer(x, intx_re)
|
|
150
|
+
return value is not None and -int64_max <= value < int64_max
|
|
137
151
|
|
|
138
152
|
|
|
139
153
|
def int128_test(x: str) -> bool:
|
|
140
|
-
|
|
154
|
+
value = bounded_integer(x, intx_re)
|
|
155
|
+
return value is not None and -int128_max <= value < int128_max
|
|
141
156
|
|
|
142
157
|
|
|
143
158
|
def int256_test(x: str) -> bool:
|
|
144
|
-
|
|
159
|
+
value = bounded_integer(x, intx_re)
|
|
160
|
+
return value is not None and -int256_max <= value < int256_max
|
|
145
161
|
|
|
146
162
|
|
|
147
163
|
def uint_8_test(x: str) -> bool:
|
|
148
|
-
|
|
164
|
+
value = bounded_integer(x, uintx_re)
|
|
165
|
+
return value is not None and 0 <= value < uint_8_max
|
|
149
166
|
|
|
150
167
|
|
|
151
168
|
def uint16_test(x: str) -> bool:
|
|
152
|
-
|
|
169
|
+
value = bounded_integer(x, uintx_re)
|
|
170
|
+
return value is not None and 0 <= value < uint16_max
|
|
153
171
|
|
|
154
172
|
|
|
155
173
|
def uint32_test(x: str) -> bool:
|
|
156
|
-
|
|
174
|
+
value = bounded_integer(x, uintx_re)
|
|
175
|
+
return value is not None and 0 <= value < uint32_max
|
|
157
176
|
|
|
158
177
|
|
|
159
178
|
def uint64_test(x: str) -> bool:
|
|
160
|
-
|
|
179
|
+
value = bounded_integer(x, uintx_re)
|
|
180
|
+
return value is not None and 0 <= value < uint64_max
|
|
161
181
|
|
|
162
182
|
|
|
163
183
|
def uint128_test(x: str) -> bool:
|
|
164
|
-
|
|
184
|
+
value = bounded_integer(x, intx_re)
|
|
185
|
+
return value is not None and 0 <= value < uint128_max
|
|
165
186
|
|
|
166
187
|
|
|
167
188
|
def uint256_test(x: str) -> bool:
|
|
168
|
-
|
|
189
|
+
value = bounded_integer(x, intx_re)
|
|
190
|
+
return value is not None and 0 <= value < uint256_max
|
|
169
191
|
|
|
170
192
|
|
|
171
193
|
def float_test(x: str) -> bool:
|
|
@@ -572,6 +572,11 @@ class FeedbackManager:
|
|
|
572
572
|
"You can read more about the pricing at https://www.tinybird.co/docs/classic/work-with-data/process-and-copy/materialized-views#compute-compute-separation-for-populates\n\n"
|
|
573
573
|
"Do you want to proceed with on-demand compute?"
|
|
574
574
|
)
|
|
575
|
+
warning_confirm_on_demand_compute_differs_from_datafile = warning_message(
|
|
576
|
+
"The local datafile of pipe '{pipe}' does not set ON_DEMAND_COMPUTE true, but --on-demand-compute was passed.\n"
|
|
577
|
+
"On-demand compute uses dedicated resources that are billed separately from your regular usage.\n\n"
|
|
578
|
+
"Do you want to proceed?"
|
|
579
|
+
)
|
|
575
580
|
|
|
576
581
|
warning_confirm_delete_token = prompt_message("Do you want to delete Token {token}?")
|
|
577
582
|
warning_confirm_refresh_token = prompt_message("Do you want to refresh Token {token}?")
|
|
@@ -18,7 +18,7 @@ from tinybird.context import (
|
|
|
18
18
|
)
|
|
19
19
|
|
|
20
20
|
from .datatypes import testers
|
|
21
|
-
from .tornado_template import VALID_CUSTOM_FUNCTION_NAMES, SecurityException, Template
|
|
21
|
+
from .tornado_template import SAFE_BUILTINS, VALID_CUSTOM_FUNCTION_NAMES, SecurityException, Template
|
|
22
22
|
|
|
23
23
|
VALID_ACTIVATE_FEATURES = frozenset(["analyzer", "parallel_replicas", "optimize_aggregation_in_order"])
|
|
24
24
|
|
|
@@ -1375,7 +1375,7 @@ _namespace = {
|
|
|
1375
1375
|
# they raise a pretty non understandable error but if someone
|
|
1376
1376
|
# is using them they know what they are trying to do
|
|
1377
1377
|
# read https://anee.me/escaping-python-jails-849c65cf306e on how to escape from python jails
|
|
1378
|
-
"
|
|
1378
|
+
"__builtins__": SAFE_BUILTINS,
|
|
1379
1379
|
"__import__": {},
|
|
1380
1380
|
"__debug__": {},
|
|
1381
1381
|
"__doc__": {},
|
|
@@ -1385,6 +1385,9 @@ _namespace = {
|
|
|
1385
1385
|
"close": None,
|
|
1386
1386
|
"print": None,
|
|
1387
1387
|
"input": None,
|
|
1388
|
+
# Keep customer parameters named `type`/`dir` usable without restoring the unsafe builtins.
|
|
1389
|
+
"type": None,
|
|
1390
|
+
"dir": None,
|
|
1388
1391
|
}
|
|
1389
1392
|
|
|
1390
1393
|
|
|
@@ -1478,6 +1481,8 @@ def generate(self, **kwargs) -> Tuple[str, TemplateExecutionResults]:
|
|
|
1478
1481
|
# the generated source code.
|
|
1479
1482
|
"__name__": self.name.replace(".", "_"),
|
|
1480
1483
|
"__loader__": ObjectDict(get_source=lambda name: self.code),
|
|
1484
|
+
# Set again after kwargs so request parameters can't override it
|
|
1485
|
+
"__builtins__": SAFE_BUILTINS,
|
|
1481
1486
|
"max_threads": set_max_threads,
|
|
1482
1487
|
"tb_secret": set_tb_secret,
|
|
1483
1488
|
"tb_var": set_tb_secret,
|
|
@@ -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.
|
|
8
|
-
__revision__ = '
|
|
7
|
+
__version__ = '4.6.22.dev0'
|
|
8
|
+
__revision__ = '3a75e0ef'
|
|
@@ -743,6 +743,7 @@ class TinyB:
|
|
|
743
743
|
target_datasource: Optional[str] = None,
|
|
744
744
|
schedule_cron: Optional[str] = None,
|
|
745
745
|
mode: Optional[str] = None,
|
|
746
|
+
on_demand_compute: Optional[bool] = None,
|
|
746
747
|
):
|
|
747
748
|
data = {"schedule_cron": schedule_cron}
|
|
748
749
|
|
|
@@ -752,6 +753,9 @@ class TinyB:
|
|
|
752
753
|
if mode:
|
|
753
754
|
data["mode"] = mode
|
|
754
755
|
|
|
756
|
+
if on_demand_compute is not None:
|
|
757
|
+
data["on_demand_compute"] = "true" if on_demand_compute else "false"
|
|
758
|
+
|
|
755
759
|
return self._req(f"/v0/pipes/{pipe_name_or_id}/nodes/{node_id}/copy", method="PUT", data=data)
|
|
756
760
|
|
|
757
761
|
def pipe_remove_copy(self, pipe_name_or_id: str, node_id: str):
|
|
@@ -10,13 +10,29 @@ from typing import Optional, Tuple
|
|
|
10
10
|
import click
|
|
11
11
|
from click import Context
|
|
12
12
|
|
|
13
|
-
from tinybird.datafile.common import get_name_version
|
|
13
|
+
from tinybird.datafile.common import CopyParameters, PipeNodeTypes, get_name_version
|
|
14
14
|
from tinybird.tb.client import AuthNoTokenException, TinyB
|
|
15
15
|
from tinybird.tb.modules.cli import cli
|
|
16
16
|
from tinybird.tb.modules.common import echo_safe_humanfriendly_tables_format_smart_table, wait_job
|
|
17
17
|
from tinybird.tb.modules.exceptions import CLIPipeException
|
|
18
18
|
from tinybird.tb.modules.feedback_manager import FeedbackManager
|
|
19
19
|
from tinybird.tb.modules.job_common import echo_job_url
|
|
20
|
+
from tinybird.tb.modules.project import Project
|
|
21
|
+
from tinybird.utils.bools import parse_optional_bool
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def local_copy_node_uses_on_demand_compute(project: Project, pipe_name: str) -> Optional[bool]:
|
|
25
|
+
"""Reads ON_DEMAND_COMPUTE from the local datafile. Returns None when there is no parseable copy pipe to compare"""
|
|
26
|
+
pipe_path = project.get_resource_path(pipe_name, "pipe")
|
|
27
|
+
if not pipe_path:
|
|
28
|
+
return None
|
|
29
|
+
datafile = project.get_pipe_datafile(str(project.path / pipe_path))
|
|
30
|
+
if datafile is None:
|
|
31
|
+
return None
|
|
32
|
+
copy_node = next((node for node in datafile.nodes if node.get("type", "").lower() == PipeNodeTypes.COPY), None)
|
|
33
|
+
if copy_node is None:
|
|
34
|
+
return None
|
|
35
|
+
return parse_optional_bool(copy_node.get(CopyParameters.ON_DEMAND_COMPUTE))
|
|
20
36
|
|
|
21
37
|
|
|
22
38
|
@cli.group()
|
|
@@ -89,6 +105,7 @@ def copy_ls(ctx: Context, match: str, format_: str):
|
|
|
89
105
|
default=False,
|
|
90
106
|
help="Use on-demand compute instances for the copy job.",
|
|
91
107
|
)
|
|
108
|
+
@click.option("--yes", is_flag=True, default=False, help="Do not ask for confirmation")
|
|
92
109
|
@click.pass_context
|
|
93
110
|
def copy_run(
|
|
94
111
|
ctx: click.Context,
|
|
@@ -97,9 +114,24 @@ def copy_run(
|
|
|
97
114
|
mode: str,
|
|
98
115
|
param: Optional[Tuple[str]],
|
|
99
116
|
on_demand_compute: bool,
|
|
117
|
+
yes: bool,
|
|
100
118
|
):
|
|
101
119
|
"""Run an on-demand copy pipe"""
|
|
102
120
|
|
|
121
|
+
project: Optional[Project] = ctx.ensure_object(dict).get("project")
|
|
122
|
+
if (
|
|
123
|
+
on_demand_compute
|
|
124
|
+
and not yes
|
|
125
|
+
and project
|
|
126
|
+
and local_copy_node_uses_on_demand_compute(project, pipe_name_or_id) is False
|
|
127
|
+
and not click.confirm(
|
|
128
|
+
FeedbackManager.warning_confirm_on_demand_compute_differs_from_datafile(pipe=pipe_name_or_id),
|
|
129
|
+
default=False,
|
|
130
|
+
)
|
|
131
|
+
):
|
|
132
|
+
click.echo(FeedbackManager.info_cancelled_by_user())
|
|
133
|
+
return
|
|
134
|
+
|
|
103
135
|
params = dict(key_value.split("=") for key_value in param) if param else {}
|
|
104
136
|
click.echo(FeedbackManager.highlight(message=f"\n» Running on-demand copy '{pipe_name_or_id}'"))
|
|
105
137
|
client: TinyB = ctx.ensure_object(dict)["client"]
|
|
@@ -881,6 +881,7 @@ def process_file(
|
|
|
881
881
|
"target_datasource": node.get("target_datasource", None),
|
|
882
882
|
"copy_schedule": node.get(CopyParameters.COPY_SCHEDULE, None),
|
|
883
883
|
"mode": node.get("mode", CopyModes.APPEND),
|
|
884
|
+
"on_demand_compute": node.get(CopyParameters.ON_DEMAND_COMPUTE, None),
|
|
884
885
|
}
|
|
885
886
|
|
|
886
887
|
is_export_node = ExportReplacements.is_export_node(node)
|
|
@@ -23,6 +23,7 @@ from tinybird.tb.modules.datafile.format_common import (
|
|
|
23
23
|
format_tags,
|
|
24
24
|
format_tokens,
|
|
25
25
|
)
|
|
26
|
+
from tinybird.utils.bools import parse_optional_bool
|
|
26
27
|
|
|
27
28
|
|
|
28
29
|
def format_node_sql(
|
|
@@ -81,6 +82,9 @@ def format_node_type(file_parts: List[str], node: Dict[str, Any]) -> List[str]:
|
|
|
81
82
|
file_parts.append(
|
|
82
83
|
f"{CopyParameters.COPY_SCHEDULE.upper()} {ON_DEMAND if is_ondemand else node[CopyParameters.COPY_SCHEDULE]}"
|
|
83
84
|
)
|
|
85
|
+
if parse_optional_bool(node.get(CopyParameters.ON_DEMAND_COMPUTE)):
|
|
86
|
+
file_parts.append(DATAFILE_NEW_LINE)
|
|
87
|
+
file_parts.append("ON_DEMAND_COMPUTE true")
|
|
84
88
|
file_parts.append(DATAFILE_NEW_LINE)
|
|
85
89
|
|
|
86
90
|
# Sink or Stream pipe
|
|
@@ -922,6 +922,7 @@ def process_file(
|
|
|
922
922
|
"target_datasource": node.get("target_datasource", None),
|
|
923
923
|
"copy_schedule": node.get(CopyParameters.COPY_SCHEDULE, None),
|
|
924
924
|
"mode": node.get("mode", CopyModes.APPEND),
|
|
925
|
+
"on_demand_compute": node.get(CopyParameters.ON_DEMAND_COMPUTE, None),
|
|
925
926
|
}
|
|
926
927
|
|
|
927
928
|
is_export_node = ExportReplacements.is_export_node(node)
|
|
@@ -749,6 +749,11 @@ STEP 3: ADD KEY TO SERVICE ACCOUNT
|
|
|
749
749
|
)
|
|
750
750
|
warning_confirm_delete_pipe = prompt_message('Do you want to remove the pipe "{pipe}"?')
|
|
751
751
|
warning_confirm_copy_pipe = prompt_message('Do you want to run a copy job from the pipe "{pipe}"?')
|
|
752
|
+
warning_confirm_on_demand_compute_differs_from_datafile = warning_message(
|
|
753
|
+
"The local datafile of pipe '{pipe}' does not set ON_DEMAND_COMPUTE true, but --on-demand-compute was passed.\n"
|
|
754
|
+
"On-demand compute uses dedicated resources that are billed separately from your regular usage.\n\n"
|
|
755
|
+
"Do you want to proceed?"
|
|
756
|
+
)
|
|
752
757
|
warning_confirm_sink_job = prompt_message('Do you want to run a sink job from the pipe "{pipe}"?')
|
|
753
758
|
warning_confirm_clear_workspace = prompt_message(
|
|
754
759
|
"Do you want to remove all pipes and Data Sources from this workspace?"
|
|
@@ -16,7 +16,15 @@ from click import Context
|
|
|
16
16
|
from tinybird import context
|
|
17
17
|
from tinybird.client import AuthNoTokenException, DoesNotExistException, TinyB
|
|
18
18
|
from tinybird.config import DEFAULT_API_HOST, FeatureFlags
|
|
19
|
-
from tinybird.datafile_common import
|
|
19
|
+
from tinybird.datafile_common import (
|
|
20
|
+
PipeNodeTypes,
|
|
21
|
+
PipeTypes,
|
|
22
|
+
folder_push,
|
|
23
|
+
get_name_version,
|
|
24
|
+
local_copy_node_uses_on_demand_compute,
|
|
25
|
+
process_file,
|
|
26
|
+
wait_job,
|
|
27
|
+
)
|
|
20
28
|
from tinybird.feedback_manager import FeedbackManager
|
|
21
29
|
from tinybird.tb_cli_modules.branch import warn_if_in_live
|
|
22
30
|
from tinybird.tb_cli_modules.cli import cli
|
|
@@ -749,30 +757,41 @@ async def pipe_copy_run(
|
|
|
749
757
|
|
|
750
758
|
params = dict(key_value.split("=") for key_value in param) if param else {}
|
|
751
759
|
|
|
752
|
-
if yes
|
|
753
|
-
|
|
754
|
-
client: TinyB = ctx.ensure_object(dict)["client"]
|
|
760
|
+
if not yes and not click.confirm(FeedbackManager.warning_confirm_copy_pipe(pipe=pipe_name_or_id)):
|
|
761
|
+
return
|
|
755
762
|
|
|
756
|
-
|
|
757
|
-
|
|
763
|
+
if (
|
|
764
|
+
on_demand_compute
|
|
765
|
+
and not yes
|
|
766
|
+
and local_copy_node_uses_on_demand_compute(".", pipe_name_or_id) is False
|
|
767
|
+
and not click.confirm(
|
|
768
|
+
FeedbackManager.warning_confirm_on_demand_compute_differs_from_datafile(pipe=pipe_name_or_id),
|
|
769
|
+
default=False,
|
|
770
|
+
)
|
|
771
|
+
):
|
|
772
|
+
return
|
|
758
773
|
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
target_datasource_id = response["tags"]["copy_target_datasource"]
|
|
762
|
-
target_datasource = await client.get_datasource(target_datasource_id)
|
|
763
|
-
target_datasource_name = target_datasource["name"]
|
|
764
|
-
click.echo(
|
|
765
|
-
FeedbackManager.success_copy_job_created(target_datasource=target_datasource_name, job_url=job_url)
|
|
766
|
-
)
|
|
774
|
+
click.echo(FeedbackManager.info_copy_job_running(pipe=pipe_name_or_id))
|
|
775
|
+
client: TinyB = ctx.ensure_object(dict)["client"]
|
|
767
776
|
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
click.echo(FeedbackManager.success_data_copied_to_ds(target_datasource=target_datasource_name))
|
|
777
|
+
try:
|
|
778
|
+
response = await client.pipe_run_copy(pipe_name_or_id, params, mode, on_demand_compute=on_demand_compute)
|
|
771
779
|
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
780
|
+
job_id = response["job"]["job_id"]
|
|
781
|
+
job_url = response["job"]["job_url"]
|
|
782
|
+
target_datasource_id = response["tags"]["copy_target_datasource"]
|
|
783
|
+
target_datasource = await client.get_datasource(target_datasource_id)
|
|
784
|
+
target_datasource_name = target_datasource["name"]
|
|
785
|
+
click.echo(FeedbackManager.success_copy_job_created(target_datasource=target_datasource_name, job_url=job_url))
|
|
786
|
+
|
|
787
|
+
if wait:
|
|
788
|
+
await wait_job(client, job_id, job_url, "** Copying data")
|
|
789
|
+
click.echo(FeedbackManager.success_data_copied_to_ds(target_datasource=target_datasource_name))
|
|
790
|
+
|
|
791
|
+
except AuthNoTokenException:
|
|
792
|
+
raise
|
|
793
|
+
except Exception as e:
|
|
794
|
+
raise CLIPipeException(FeedbackManager.error_creating_copy_job(error=e))
|
|
776
795
|
|
|
777
796
|
|
|
778
797
|
@pipe_copy.command(name="resume", short_help="Resume a paused copy pipe")
|