tinybird 4.6.18.dev0__tar.gz → 4.6.19__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 (128) hide show
  1. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/PKG-INFO +21 -1
  2. tinybird-4.6.19/pyproject.toml +228 -0
  3. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/ch_utils/constants.py +1 -0
  4. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/ch_utils/engine.py +56 -44
  5. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/client.py +51 -26
  6. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/datafile/common.py +20 -25
  7. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/git_settings.py +1 -3
  8. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/service_datasources.py +13 -12
  9. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/sql.py +3 -10
  10. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/sql_template.py +53 -71
  11. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/sql_template_fmt.py +8 -13
  12. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/sql_toolset.py +62 -5
  13. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/syncasync.py +9 -11
  14. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/__cli__.py +2 -2
  15. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/client.py +54 -23
  16. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/build_common.py +4 -6
  17. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/cicd.py +1 -0
  18. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/cli.py +4 -2
  19. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/common.py +23 -89
  20. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/config.py +1 -2
  21. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/connection_kafka.py +1 -2
  22. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/connection_s3.py +13 -18
  23. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/create.py +3 -6
  24. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/datafile/build.py +2 -3
  25. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/datafile/build_pipe.py +1 -4
  26. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/datafile/diff.py +2 -1
  27. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/datafile/format_common.py +9 -10
  28. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/datafile/format_connection.py +1 -2
  29. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/datafile/format_datasource.py +1 -2
  30. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/datafile/format_pipe.py +1 -2
  31. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/datafile/pipe_checker.py +6 -7
  32. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/datafile/playground.py +3 -4
  33. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/datasource.py +7 -8
  34. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/deployment_common.py +3 -5
  35. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/feedback_manager.py +1 -0
  36. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/infra.py +1 -2
  37. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/job.py +22 -0
  38. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/logs.py +1 -2
  39. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/materialization.py +1 -1
  40. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/project.py +4 -4
  41. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/tinyunit/tinyunit.py +4 -4
  42. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/token.py +2 -5
  43. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/watch.py +1 -1
  44. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb_cli_modules/branch.py +1 -2
  45. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb_cli_modules/cicd.py +2 -0
  46. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb_cli_modules/cli.py +5 -9
  47. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb_cli_modules/common.py +27 -94
  48. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb_cli_modules/config.py +1 -2
  49. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb_cli_modules/datasource.py +1 -1
  50. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb_cli_modules/pipe.py +9 -13
  51. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb_cli_modules/tinyunit/tinyunit.py +4 -4
  52. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb_cli_modules/workspace.py +11 -13
  53. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tornado_template.py +12 -17
  54. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird.egg-info/PKG-INFO +21 -1
  55. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird.egg-info/SOURCES.txt +1 -0
  56. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/setup.cfg +0 -0
  57. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/__cli__.py +0 -0
  58. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/check_pypi.py +0 -0
  59. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/config.py +0 -0
  60. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/context.py +0 -0
  61. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/datafile/exceptions.py +0 -0
  62. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/datafile/parse_connection.py +0 -0
  63. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/datafile/parse_datasource.py +0 -0
  64. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/datafile/parse_pipe.py +0 -0
  65. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/datatypes.py +0 -0
  66. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/feedback_manager.py +0 -0
  67. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/iterating/__init__.py +0 -0
  68. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/iterating/data_branch_modes.py +0 -0
  69. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/prompts.py +0 -0
  70. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/check_pypi.py +0 -0
  71. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/cli.py +0 -0
  72. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/config.py +0 -0
  73. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/branch.py +0 -0
  74. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/build.py +0 -0
  75. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/connection.py +0 -0
  76. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/connection_dynamodb.py +0 -0
  77. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/copy.py +0 -0
  78. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/datafile/build_common.py +0 -0
  79. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/datafile/build_datasource.py +0 -0
  80. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/datafile/fixture.py +0 -0
  81. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/datafile/pull.py +0 -0
  82. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/deployment.py +0 -0
  83. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/deprecations.py +0 -0
  84. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/endpoint.py +0 -0
  85. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/exceptions.py +0 -0
  86. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/fmt.py +0 -0
  87. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/info.py +0 -0
  88. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/job_common.py +0 -0
  89. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/llm.py +0 -0
  90. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/llm_utils.py +0 -0
  91. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/local.py +0 -0
  92. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/local_common.py +0 -0
  93. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/local_logs.py +0 -0
  94. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/login.py +0 -0
  95. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/login_common.py +0 -0
  96. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/logout.py +0 -0
  97. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/open.py +0 -0
  98. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/pipe.py +0 -0
  99. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/preview.py +0 -0
  100. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/project_commands.py +0 -0
  101. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/py_project.py +0 -0
  102. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/query_output.py +0 -0
  103. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/regions.py +0 -0
  104. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/secret.py +0 -0
  105. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/secret_common.py +0 -0
  106. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/sink.py +0 -0
  107. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/table.py +0 -0
  108. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/telemetry.py +0 -0
  109. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/test.py +0 -0
  110. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/test_common.py +0 -0
  111. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/ts_project.py +0 -0
  112. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/workspace.py +0 -0
  113. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb/modules/workspace_members.py +0 -0
  114. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb_cli.py +0 -0
  115. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb_cli_modules/auth.py +0 -0
  116. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb_cli_modules/connection.py +0 -0
  117. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb_cli_modules/exceptions.py +0 -0
  118. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb_cli_modules/fmt.py +0 -0
  119. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb_cli_modules/job.py +0 -0
  120. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb_cli_modules/regions.py +0 -0
  121. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb_cli_modules/tag.py +0 -0
  122. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb_cli_modules/telemetry.py +0 -0
  123. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb_cli_modules/test.py +0 -0
  124. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird/tb_cli_modules/workspace_members.py +0 -0
  125. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird.egg-info/dependency_links.txt +0 -0
  126. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird.egg-info/entry_points.txt +0 -0
  127. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/tinybird.egg-info/requires.txt +0 -0
  128. {tinybird-4.6.18.dev0 → tinybird-4.6.19}/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.18.dev0
3
+ Version: 4.6.19
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,26 @@ The Tinybird command-line tool allows you to use all the Tinybird functionality
52
52
  Changelog
53
53
  ----------
54
54
 
55
+ 4.6.19
56
+ ********
57
+
58
+ - `Changed` ``tb datasource append`` and ``tb datasource replace`` submit remote CSV, NDJSON, and Parquet URLs through the v1 import endpoint by default.
59
+
60
+ 4.6.18
61
+ ********
62
+
63
+ - `Fixed` CLI commands (for example ``tb migrate-to-forward``) no longer abort on startup when the best-effort refresh of the cached workspace id/name fails for a reason other than authentication.
64
+
65
+ 4.6.17
66
+ ********
67
+
68
+ - `Added` ``tb job retry <job_id>`` to retry eligible import and S3/GCS sync jobs in ``error`` or ``cancelled`` status.
69
+
70
+ 4.6.18
71
+ ********
72
+
73
+ - `Added` ``Markdown`` is now a valid ``FORMAT`` for SQL queries (for example ``tb sql "SELECT 1 FORMAT Markdown"``), returned as a Markdown table.
74
+
55
75
  4.6.16
56
76
  ********
57
77
 
@@ -0,0 +1,228 @@
1
+ [build-system]
2
+ requires = [
3
+ "setuptools>=61.0",
4
+ "wheel==0.37.1",
5
+ "cffi==1.17.1",
6
+ ]
7
+ build-backend = "setuptools.build_meta"
8
+
9
+ [project]
10
+ name = "tinybird"
11
+ version = "1.0"
12
+ description = "tinybird"
13
+ readme = { text = "tinybird analytics", content-type = "text/markdown" }
14
+ requires-python = ">=3.13,<3.15"
15
+ authors = [
16
+ { name = "tinybird.co" },
17
+ ]
18
+ dependencies = [
19
+ "GitPython==3.1.44",
20
+ "Markdown==3.5.1",
21
+ "aioboto3==14.0.0",
22
+ "aiofiles==24.1.0",
23
+ "aiohttp==3.11.2",
24
+ "anthropic==0.55.0",
25
+ "aws-msk-iam-sasl-signer-python==1.0.2",
26
+ "cachetools==5.5.0",
27
+ "cffi==1.17.1",
28
+ "chardet==4.0.0",
29
+ "ciso8601==2.2.0",
30
+ "click==8.1.8",
31
+ "clickhouse-cityhash==1.0.2.5",
32
+ "clickhouse-driver==0.2.9",
33
+ "confluent-kafka[avro,json,schemaregistry]==2.12.2",
34
+ "contextvars==2.4",
35
+ "cramjam==2.11.0",
36
+ "croniter==6.2.2",
37
+ "cryptography==3.4.8",
38
+ "datasketch==1.6.5",
39
+ "dnspython==2.8.0",
40
+ "docker==7.1.0",
41
+ "fastavro==1.12.2",
42
+ "fastmcp==2.12.2",
43
+ "google-cloud-aiplatform==1.148.1",
44
+ "google-cloud-compute==1.48.0",
45
+ "google-cloud-scheduler==2.20.0",
46
+ "google-cloud-secret-manager==2.29.0",
47
+ "google-cloud-storage==3.4.1",
48
+ "google_api_core==2.25.2",
49
+ "googleapis_common_protos==1.66.0",
50
+ "grpcio_status==1.67.1",
51
+ "humanfriendly==10.0",
52
+ "isal==1.8.0",
53
+ "jinja2",
54
+ "jsondiff==2.2.1",
55
+ "jsonschema==4.23.0",
56
+ "litellm==1.80.11",
57
+ "logfire-api==4.2.0",
58
+ "lru-dict==1.3.0",
59
+ "lz4==4.4.5",
60
+ "memray==1.20.0",
61
+ "msgspec==0.21.1",
62
+ "multidict==6.6.4",
63
+ "networkx==3.4.2",
64
+ "numpy==1.26.4",
65
+ "objgraph==3.6.2",
66
+ "opentelemetry-api==1.38.0",
67
+ "opentelemetry-exporter-otlp-proto-http==1.38.0",
68
+ "opentelemetry-sdk==1.38.0",
69
+ "orb-billing==2.17.0",
70
+ "orjson==3.11.5",
71
+ "packaging==23.2",
72
+ "passlib==1.7.1",
73
+ "posthog==7.28.0",
74
+ "pre-commit==4.6.2",
75
+ "prometheus_client==0.26.0",
76
+ "prompt_toolkit==3.0.48",
77
+ "propcache==0.3.2",
78
+ "proto_plus==1.25.0",
79
+ "protobuf==5.29.5",
80
+ "psutil==7.2.2",
81
+ "psycopg2-binary==2.9.12",
82
+ "py-spy==0.4.2",
83
+ "pyarrow==23.0.1",
84
+ "pycurl==7.45.7",
85
+ "pydantic-ai-slim[anthropic]==0.5.0",
86
+ "pydantic-ai-slim[google]==0.5.0",
87
+ "pydantic-ai-slim[retries]==0.5.0",
88
+ "pydantic==2.12.5",
89
+ "pyjwt[crypto]==2.9.0",
90
+ "pympler==1.1",
91
+ "pynacl==1.5.0",
92
+ "pyperclip==1.9.0",
93
+ "python-dateutil==2.9.0.post0",
94
+ "python-dotenv==1.1.0",
95
+ "python-ulid==2.7.0",
96
+ "pyyaml==6.0.1",
97
+ "redis[hiredis]==5.3.1",
98
+ "requests==2.32.4",
99
+ "sentry-sdk==2.69.1",
100
+ "setproctitle==1.3.3",
101
+ "shandy-sqlfmt==0.11.1",
102
+ "shandy-sqlfmt[jinjafmt]==0.11.1",
103
+ "sqlparse==0.4.3",
104
+ "starlette==1.0.1",
105
+ "streaming-form-data==1.20.0",
106
+ "stripe==3.1.0",
107
+ "tabulate==0.8.10",
108
+ "thefuzz==0.22.1",
109
+ "tinybird-python-sdk[ai]==0.3.7",
110
+ "tinybird-toolset==2.6.2",
111
+ "toposort==1.10",
112
+ "tornado==6.5.5",
113
+ "urllib3==1.26.20",
114
+ "uvicorn[standard]==0.49.0",
115
+ "virtualenv==20.21.1",
116
+ "watchdog==6.0.0",
117
+ "wheel==0.37.1",
118
+ "zlib-ng==1.0.0",
119
+ "zstandard==0.25.0",
120
+ ]
121
+
122
+ [dependency-groups]
123
+ deploy = [
124
+ "GitPython==3.1.44",
125
+ "ansible==8.5.0",
126
+ "ara==1.6.1",
127
+ "boto3==1.36.21",
128
+ "fabric==3.2.2",
129
+ "google-auth==2.47.0",
130
+ "jsonnet",
131
+ "prettytable==3.8.0",
132
+ "requests==2.32.4",
133
+ "requests[security]",
134
+ "xmltodict",
135
+ ]
136
+ devtools = [
137
+ "flameprof",
138
+ "matplotlib",
139
+ "memory_profiler",
140
+ "rope",
141
+ ]
142
+ doc = [
143
+ "Pallets-Sphinx-Themes==2.3.0",
144
+ "pyOpenSSL<24.0.0",
145
+ "sphinx-autobuild==2024.2.4",
146
+ "sphinx-sitemap",
147
+ "sphinx==7.3.7",
148
+ "sphinxcontrib-httpdomain",
149
+ "sphinxcontrib-jquery==4.1",
150
+ ]
151
+ lint = [
152
+ "botocore-stubs==1.36.23",
153
+ "mypy==2.3.1",
154
+ "ruff==0.16.8",
155
+ "types-Markdown",
156
+ "types-PyYAML",
157
+ "types-aiofiles==24.1.0.20241221",
158
+ "types-boto3-lite==1.36.21",
159
+ "types-cachetools",
160
+ "types-chardet",
161
+ "types-paramiko",
162
+ "types-pycurl",
163
+ "types-python-dateutil",
164
+ "types-pytz",
165
+ "types-requests",
166
+ "types-tabulate",
167
+ "types_croniter",
168
+ ]
169
+ test = [
170
+ "coverage==7.10.6",
171
+ "freezegun==1.5.5",
172
+ "google-auth==2.47.0",
173
+ "gsutil==4.58",
174
+ "hypothesis==6.156.1",
175
+ "oauth2client==3.0.0",
176
+ "py==1.11.0",
177
+ "pytest-asyncio==0.23.8",
178
+ "pytest-cov==4.1.0",
179
+ "pytest-env==1.1.3",
180
+ "pytest-flakefinder==1.1.0",
181
+ "pytest-timeout==2.3.1",
182
+ "pytest-xdist==3.6.1",
183
+ "pytest==8.1.2",
184
+ "rangehttpserver==1.4.0",
185
+ "yappi==1.7.6",
186
+ ]
187
+
188
+ [project.scripts]
189
+ tinybird_server = "tinybird.app:run"
190
+ tinybird_tool = "tinybird.tinybird_tool.main:cli"
191
+ tb_old = "tinybird.tb_cli:cli"
192
+ tb = "tinybird.tb.cli:cli"
193
+ colibri = "tinybird.colibri.cli:cli"
194
+
195
+ [tool.uv.workspace]
196
+ members = ["data_connector"]
197
+
198
+ [tool.setuptools]
199
+ include-package-data = true
200
+
201
+ [tool.setuptools.packages.find]
202
+ namespaces = false
203
+ include = ["tinybird", "tinybird.*", "tinybird_shared", "tinybird_shared.*", "scripts"]
204
+ exclude = ["tests*"]
205
+
206
+ [tool.setuptools.package-data]
207
+ tinybird = [
208
+ "templates/**/*",
209
+ "static/**/*",
210
+ "default_pipes/**/*",
211
+ "sql/**/*",
212
+ "monitors/**/*.sql",
213
+ "scripts/**/*",
214
+ "data_projects/**/*",
215
+ "data_projects/**/.*",
216
+ "raw_events/**/*",
217
+ "query_booster/**/*",
218
+ "deployments/**/*",
219
+ ]
220
+ tinybird_shared = [
221
+ "redis_client/**/*",
222
+ "retry/**/*",
223
+ "metrics/**/*",
224
+ "gatherer_settings/**/*",
225
+ "clickhouse/**/*",
226
+ "gatherer_backpressure/**/*",
227
+ "data_connector_circuit_breaker/**/*",
228
+ ]
@@ -263,4 +263,5 @@ VALID_QUERY_FORMATS = (
263
263
  "JSONCompact",
264
264
  "JSONStringsEachRowWithProgress",
265
265
  "ODBCDriver2",
266
+ "Markdown",
266
267
  )
@@ -111,8 +111,7 @@ class TableDetails:
111
111
 
112
112
  @property
113
113
  def version(self):
114
- _version = self.details.get("version", None)
115
- return _version
114
+ return self.details.get("version", None)
116
115
 
117
116
  def is_replicated(self) -> bool:
118
117
  engine: Optional[str] = self.details.get("engine", None)
@@ -183,6 +182,7 @@ class TableDetails:
183
182
  settings = self.details.get("settings", None)
184
183
  if settings and settings.strip().lower() != "index_granularity = 8192":
185
184
  return settings
185
+ return None
186
186
 
187
187
  @property
188
188
  def ttl(self):
@@ -190,23 +190,19 @@ class TableDetails:
190
190
 
191
191
  @property
192
192
  def ver(self):
193
- _ver = self.details.get("ver", None)
194
- return _ver
193
+ return self.details.get("ver", None)
195
194
 
196
195
  @property
197
196
  def is_deleted(self):
198
- _is_deleted = self.details.get("is_deleted", None)
199
- return _is_deleted
197
+ return self.details.get("is_deleted", None)
200
198
 
201
199
  @property
202
200
  def columns(self):
203
- _columns = self.details.get("columns", None)
204
- return _columns
201
+ return self.details.get("columns", None)
205
202
 
206
203
  @property
207
204
  def sign(self):
208
- _sign = self.details.get("sign", None)
209
- return _sign
205
+ return self.details.get("sign", None)
210
206
 
211
207
  @property
212
208
  def statistics(self) -> Dict[str, Any]:
@@ -444,35 +440,64 @@ def get_engine_config(engine: str):
444
440
  )
445
441
 
446
442
 
447
- def engine_params(columns, params: List[EngineParam], args: Dict):
448
- params_values = []
443
+ def validate_engine_config(
444
+ engine: str, args: dict, schema: Optional[str] = None, columns: Optional[List[Dict[str, Any]]] = None
445
+ ) -> None:
446
+ if schema is not None and columns is not None:
447
+ raise ValueError("You can not use 'schema' and 'columns' at the same time")
448
+ _, (params, options) = get_engine_config(engine)
449
+ schema_columns = parse_table_structure(schema) if schema is not None else (columns or [])
450
+ engine_settings = {key.replace("engine_", ""): value for key, value in args.items()}
451
+
452
+ for arg in engine_settings:
453
+ if not hasattr(TableDetails, arg):
454
+ raise ValueError(f"engine_{arg} is not a valid option")
455
+
449
456
  for p in params:
450
- if p.required and p.name not in args:
457
+ if p.required and p.name not in engine_settings:
451
458
  raise ValueError(f"Missing required parameter '{p.name}'")
452
- param_value = args.get(p.name, None) or p.default_value
459
+ param_value = engine_settings.get(p.name) or p.default_value
453
460
  if not param_value:
454
461
  continue
455
462
  if p.is_valid:
456
463
  check_is_valid(
457
- valid_check=p.is_valid, check_type="parameter", columns=columns, tb_param=p.tb_param, value=param_value
464
+ valid_check=p.is_valid,
465
+ check_type="parameter",
466
+ columns=schema_columns,
467
+ tb_param=p.tb_param,
468
+ value=param_value,
458
469
  )
470
+
471
+ for o in options:
472
+ if o.required and o.name not in engine_settings:
473
+ raise ValueError(f"Missing required option '{o.name}'")
474
+ option_value = engine_settings.get(o.name) or o.default_value
475
+ if o.is_valid:
476
+ check_is_valid(
477
+ valid_check=o.is_valid,
478
+ check_type="option",
479
+ columns=schema_columns,
480
+ tb_param=o.tb_param,
481
+ value=option_value,
482
+ )
483
+
484
+
485
+ def engine_params(params: List[EngineParam], args: Dict) -> List[str]:
486
+ params_values = []
487
+ for p in params:
488
+ param_value = args.get(p.name, None) or p.default_value
489
+ if not param_value:
490
+ continue
459
491
  params_values.append(param_value)
460
492
  return params_values
461
493
 
462
494
 
463
- def engine_options(columns, options: List[EngineOption], args: Dict):
495
+ def engine_options(options: List[EngineOption], args: Dict) -> List[str]:
464
496
  options_values = []
465
497
  engine_settings = ""
466
498
 
467
499
  for o in options:
468
- if o.required and o.name not in args:
469
- raise ValueError(f"Missing required option '{o.name}'")
470
500
  option_value = args.get(o.name) or o.default_value
471
- if o.is_valid:
472
- check_is_valid(
473
- valid_check=o.is_valid, check_type="option", columns=columns, tb_param=o.tb_param, value=option_value
474
- )
475
-
476
501
  if option_value:
477
502
  if o.sql.lower() == "settings":
478
503
  engine_settings = f"{o.sql} {option_value}"
@@ -494,7 +519,7 @@ def check_is_valid(
494
519
  columns: List[Dict[str, Any]],
495
520
  tb_param: str,
496
521
  value: str,
497
- ):
522
+ ) -> None:
498
523
  """
499
524
  >>> check_is_valid(sorting_key_is_valid, 'option', ['column-name'], 'sorting_key', 'date')
500
525
 
@@ -511,15 +536,13 @@ def check_is_valid(
511
536
  raise ValueError(f"Invalid value '{value}' for {check_type} '{tb_param}', reason: {e}")
512
537
 
513
538
 
514
- def build_engine(
515
- engine: str, columns: Optional[List], params: List[EngineParam], options: List[EngineOption], args: Dict
516
- ):
517
- return f"{engine}({', '.join(engine_params(columns, params, args))}) {' '.join(engine_options(columns, options, args))}".strip()
539
+ def build_engine(engine: str, params: List[EngineParam], options: List[EngineOption], args: Dict) -> str:
540
+ return f"{engine}({', '.join(engine_params(params, args))}) {' '.join(engine_options(options, args))}".strip()
518
541
 
519
542
 
520
543
  def engine_full_from_dict(
521
544
  engine: str, args: dict, schema: Optional[str] = None, columns: Optional[List[Dict[str, Any]]] = None
522
- ):
545
+ ) -> str:
523
546
  """
524
547
  >>> schema = ''
525
548
  >>> engine_full_from_dict('wadus', {}, schema=schema)
@@ -610,20 +633,10 @@ def engine_full_from_dict(
610
633
  'ReplacingMergeTree(updated_at) PARTITION BY (tuple()) ORDER BY (project_id) SETTINGS index_granularity = 32'
611
634
  """
612
635
 
613
- if schema is not None and columns is not None:
614
- raise ValueError("You can not use 'schema' and 'columns' at the same time")
615
- engine_config = get_engine_config(engine)
616
- name, (params, options) = engine_config
617
- if columns is None and schema is not None:
618
- columns = parse_table_structure(schema)
619
-
636
+ validate_engine_config(engine, args, schema=schema, columns=columns)
637
+ name, (params, options) = get_engine_config(engine)
620
638
  engine_settings = {key.replace("engine_", ""): value for key, value in args.items()}
621
-
622
- for arg in engine_settings:
623
- if not hasattr(TableDetails, arg):
624
- raise ValueError(f"engine_{arg} is not a valid option")
625
-
626
- return build_engine(name, columns, params, options, engine_settings)
639
+ return build_engine(name, params, options, engine_settings)
627
640
 
628
641
 
629
642
  def engine_params_from_engine_full(engine_full: str) -> Dict[str, Any]:
@@ -699,8 +712,7 @@ def ttl_from_engine(engine: str) -> Optional[str]:
699
712
  return None
700
713
  settings_array = engine.split(" SETTINGS ")
701
714
  settings = " SETTINGS " + settings_array[1] if len(settings_array) > 1 else None
702
- ttl = ttl_array[1][: -(len(settings))] if settings else ttl_array[1]
703
- return ttl
715
+ return ttl_array[1][: -(len(settings))] if settings else ttl_array[1]
704
716
 
705
717
 
706
718
  def ttl_condition_from_engine_full(engine_full: Optional[str]) -> Optional[str]:
@@ -266,14 +266,13 @@ class TinyB:
266
266
 
267
267
  async def alter_tokens(self, name: str, scopes: List[str]):
268
268
  if not scopes:
269
- return
269
+ return None
270
270
  scopes_url: str = "&".join([f"scope={scope}" for scope in scopes])
271
271
  url = f"/v0/tokens/{name}"
272
272
  if len(url + "?" + scopes_url) > TinyB.MAX_GET_LENGTH:
273
273
  return await self._req(url, method="PUT", data=scopes_url)
274
- else:
275
- url = url + "?" + scopes_url
276
- return await self._req(url, method="PUT", data="")
274
+ url = url + "?" + scopes_url
275
+ return await self._req(url, method="PUT", data="")
277
276
 
278
277
  async def datasources(self, branch: Optional[str] = None, used_by: bool = False) -> List[Dict[str, Any]]:
279
278
  params = {}
@@ -294,16 +293,13 @@ class TinyB:
294
293
  return await self._req(f"/v0/variables/{name}")
295
294
 
296
295
  async def create_secret(self, name: str, value: str):
297
- response = await self._req("/v0/variables", method="POST", data={"name": name, "value": value})
298
- return response
296
+ return await self._req("/v0/variables", method="POST", data={"name": name, "value": value})
299
297
 
300
298
  async def update_secret(self, name: str, value: str):
301
- response = await self._req(f"/v0/variables/{name}", method="PUT", data={"value": value})
302
- return response
299
+ return await self._req(f"/v0/variables/{name}", method="PUT", data={"value": value})
303
300
 
304
301
  async def delete_secret(self, name: str):
305
- response = await self._req(f"/v0/variables/{name}", method="DELETE")
306
- return response
302
+ return await self._req(f"/v0/variables/{name}", method="DELETE")
307
303
 
308
304
  async def get_connections(self, service: Optional[str] = None):
309
305
  params = {}
@@ -412,6 +408,16 @@ class TinyB:
412
408
  format: str = "csv",
413
409
  replace_options: Optional[Set[str]] = None,
414
410
  ) -> Dict[str, Any]:
411
+ if mode in ("append", "replace"):
412
+ return await self.datasource_append_url(
413
+ table_name,
414
+ url,
415
+ mode=mode,
416
+ sql_condition=sql_condition,
417
+ format=format,
418
+ replace_options=replace_options,
419
+ )
420
+
415
421
  params = {"name": table_name, "url": url, "mode": mode, "debug": "blocks_block_log", "format": format}
416
422
 
417
423
  if sql_condition:
@@ -428,6 +434,33 @@ class TinyB:
428
434
 
429
435
  return await self.wait_for_job(res["id"], status_callback, backoff_multiplier=1.5, maximum_backoff_seconds=20)
430
436
 
437
+ async def datasource_append_url(
438
+ self,
439
+ datasource_name: str,
440
+ url: str,
441
+ mode: str = "append",
442
+ sql_condition: Optional[str] = None,
443
+ format: str = "csv",
444
+ replace_options: Optional[Set[str]] = None,
445
+ ) -> Dict[str, Any]:
446
+ params = {"format": format, "url": url}
447
+ if sql_condition:
448
+ params["replace_condition"] = sql_condition
449
+ if replace_options:
450
+ for option in replace_options:
451
+ params[option] = "true"
452
+
453
+ res = await self._req(
454
+ f"/v1/datasources/{quote(datasource_name, safe='')}/{mode}?{urlencode(params, safe='')}",
455
+ method="POST",
456
+ data=b"",
457
+ )
458
+ if not isinstance(res, dict):
459
+ raise RuntimeError("We couldn't confirm that your import started. Please try again.")
460
+ if "error" in res:
461
+ raise Exception(res["error"])
462
+ return res
463
+
431
464
  async def datasource_delete(self, datasource_name: str, force: bool = False, dry_run: bool = False):
432
465
  params = {"force": "true" if force else "false", "dry_run": "true" if dry_run else "false"}
433
466
  return await self._req(f"/v0/datasources/{datasource_name}?{urlencode(params)}", method="DELETE")
@@ -577,8 +610,7 @@ class TinyB:
577
610
  node_name = node["params"]["name"] if node.get("params", None) else node["name"]
578
611
  if datasource_name:
579
612
  params["datasource"] = datasource_name
580
- response = await self._req(f"/v0/pipes/{pipe_name}/nodes/{node_name}/analysis?{urlencode(params)}")
581
- return response
613
+ return await self._req(f"/v0/pipes/{pipe_name}/nodes/{node_name}/analysis?{urlencode(params)}")
582
614
 
583
615
  async def populate_node(
584
616
  self,
@@ -600,10 +632,7 @@ class TinyB:
600
632
  params.update({"populate_condition": populate_condition})
601
633
  if on_demand_compute:
602
634
  params.update({"on_demand_compute": "true"})
603
- response = await self._req(
604
- f"/v0/pipes/{pipe_name}/nodes/{node_name}/population?{urlencode(params)}", method="POST"
605
- )
606
- return response
635
+ return await self._req(f"/v0/pipes/{pipe_name}/nodes/{node_name}/population?{urlencode(params)}", method="POST")
607
636
 
608
637
  async def pipes(self, branch=None, dependencies: bool = False, node_attrs=None, attrs=None) -> List[Dict[str, Any]]:
609
638
  params = {
@@ -635,9 +664,8 @@ class TinyB:
635
664
  query_string = urlencode(params)
636
665
  if len(url + "?" + query_string) > TinyB.MAX_GET_LENGTH:
637
666
  return await self._req(f"/v0/pipes/{pipe_name_or_uid}.{format}", method="POST", data=params)
638
- else:
639
- url = url + "?" + query_string
640
- return await self._req(url)
667
+ url = url + "?" + query_string
668
+ return await self._req(url)
641
669
 
642
670
  async def pipe_create(self, pipe_name: str, sql: str):
643
671
  return await self._req(
@@ -726,8 +754,7 @@ class TinyB:
726
754
 
727
755
  if len(sql) > TinyB.MAX_GET_LENGTH:
728
756
  return await self._req(f"/v0/sql?{urlencode(params)}", data=sql, method="POST")
729
- else:
730
- return await self._req(f"/v0/sql?q={quote(sql, safe='')}&{urlencode(params)}")
757
+ return await self._req(f"/v0/sql?q={quote(sql, safe='')}&{urlencode(params)}")
731
758
 
732
759
  async def jobs(self, status=None):
733
760
  jobs = (await self._req("/v0/jobs"))["jobs"]
@@ -922,7 +949,7 @@ class TinyB:
922
949
 
923
950
  async def add_workspaces_to_organization(self, organization_id: str, workspace_ids: List[str]):
924
951
  if not workspace_ids:
925
- return
952
+ return None
926
953
  return await self._req(
927
954
  f"/v0/organizations/{organization_id}/workspaces",
928
955
  method="PUT",
@@ -1105,8 +1132,7 @@ class TinyB:
1105
1132
  from tinybird.sql_toolset import format_sql
1106
1133
 
1107
1134
  return format_sql(sql)
1108
- else:
1109
- return await self._sql_get_format_remote(sql, with_clickhouse_format)
1135
+ return await self._sql_get_format_remote(sql, with_clickhouse_format)
1110
1136
  except ModuleNotFoundError:
1111
1137
  return await self._sql_get_format_remote(sql, with_clickhouse_format)
1112
1138
 
@@ -1168,8 +1194,7 @@ class TinyB:
1168
1194
  return next((connector for connector in result["connectors"] if connector_equals(connector, kwargs)), None)
1169
1195
 
1170
1196
  async def regions(self):
1171
- regions = await self._req("/v0/regions")
1172
- return regions
1197
+ return await self._req("/v0/regions")
1173
1198
 
1174
1199
  async def datasource_query_copy(self, datasource_name: str, sql_query: str):
1175
1200
  params = {"copy_to": datasource_name}