tinybird-cli 5.13.3.dev1__tar.gz → 5.14.0.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 (48) hide show
  1. {tinybird_cli-5.13.3.dev1 → tinybird_cli-5.14.0.dev0}/PKG-INFO +13 -2
  2. {tinybird_cli-5.13.3.dev1 → tinybird_cli-5.14.0.dev0}/tinybird/__cli__.py +2 -2
  3. {tinybird_cli-5.13.3.dev1 → tinybird_cli-5.14.0.dev0}/tinybird/client.py +1 -1
  4. {tinybird_cli-5.13.3.dev1 → tinybird_cli-5.14.0.dev0}/tinybird/connectors.py +3 -3
  5. {tinybird_cli-5.13.3.dev1 → tinybird_cli-5.14.0.dev0}/tinybird/datafile.py +13 -11
  6. {tinybird_cli-5.13.3.dev1 → tinybird_cli-5.14.0.dev0}/tinybird/feedback_manager.py +1 -1
  7. {tinybird_cli-5.13.3.dev1 → tinybird_cli-5.14.0.dev0}/tinybird/sql.py +4 -4
  8. {tinybird_cli-5.13.3.dev1 → tinybird_cli-5.14.0.dev0}/tinybird/sql_template_fmt.py +1 -1
  9. {tinybird_cli-5.13.3.dev1 → tinybird_cli-5.14.0.dev0}/tinybird/sql_toolset.py +9 -2
  10. {tinybird_cli-5.13.3.dev1 → tinybird_cli-5.14.0.dev0}/tinybird/tb_cli_modules/cli.py +2 -2
  11. {tinybird_cli-5.13.3.dev1 → tinybird_cli-5.14.0.dev0}/tinybird/tb_cli_modules/common.py +20 -18
  12. {tinybird_cli-5.13.3.dev1 → tinybird_cli-5.14.0.dev0}/tinybird/tb_cli_modules/pipe.py +1 -1
  13. {tinybird_cli-5.13.3.dev1 → tinybird_cli-5.14.0.dev0}/tinybird/tb_cli_modules/tinyunit/tinyunit.py +1 -1
  14. {tinybird_cli-5.13.3.dev1 → tinybird_cli-5.14.0.dev0}/tinybird/tornado_template.py +2 -2
  15. {tinybird_cli-5.13.3.dev1 → tinybird_cli-5.14.0.dev0}/tinybird_cli.egg-info/PKG-INFO +13 -2
  16. {tinybird_cli-5.13.3.dev1 → tinybird_cli-5.14.0.dev0}/setup.cfg +0 -0
  17. {tinybird_cli-5.13.3.dev1 → tinybird_cli-5.14.0.dev0}/tinybird/ch_utils/constants.py +0 -0
  18. {tinybird_cli-5.13.3.dev1 → tinybird_cli-5.14.0.dev0}/tinybird/ch_utils/engine.py +0 -0
  19. {tinybird_cli-5.13.3.dev1 → tinybird_cli-5.14.0.dev0}/tinybird/check_pypi.py +0 -0
  20. {tinybird_cli-5.13.3.dev1 → tinybird_cli-5.14.0.dev0}/tinybird/config.py +0 -0
  21. {tinybird_cli-5.13.3.dev1 → tinybird_cli-5.14.0.dev0}/tinybird/context.py +0 -0
  22. {tinybird_cli-5.13.3.dev1 → tinybird_cli-5.14.0.dev0}/tinybird/datatypes.py +0 -0
  23. {tinybird_cli-5.13.3.dev1 → tinybird_cli-5.14.0.dev0}/tinybird/git_settings.py +0 -0
  24. {tinybird_cli-5.13.3.dev1 → tinybird_cli-5.14.0.dev0}/tinybird/sql_template.py +0 -0
  25. {tinybird_cli-5.13.3.dev1 → tinybird_cli-5.14.0.dev0}/tinybird/syncasync.py +0 -0
  26. {tinybird_cli-5.13.3.dev1 → tinybird_cli-5.14.0.dev0}/tinybird/tb_cli.py +0 -0
  27. {tinybird_cli-5.13.3.dev1 → tinybird_cli-5.14.0.dev0}/tinybird/tb_cli_modules/auth.py +0 -0
  28. {tinybird_cli-5.13.3.dev1 → tinybird_cli-5.14.0.dev0}/tinybird/tb_cli_modules/branch.py +0 -0
  29. {tinybird_cli-5.13.3.dev1 → tinybird_cli-5.14.0.dev0}/tinybird/tb_cli_modules/cicd.py +0 -0
  30. {tinybird_cli-5.13.3.dev1 → tinybird_cli-5.14.0.dev0}/tinybird/tb_cli_modules/config.py +0 -0
  31. {tinybird_cli-5.13.3.dev1 → tinybird_cli-5.14.0.dev0}/tinybird/tb_cli_modules/connection.py +0 -0
  32. {tinybird_cli-5.13.3.dev1 → tinybird_cli-5.14.0.dev0}/tinybird/tb_cli_modules/datasource.py +0 -0
  33. {tinybird_cli-5.13.3.dev1 → tinybird_cli-5.14.0.dev0}/tinybird/tb_cli_modules/exceptions.py +0 -0
  34. {tinybird_cli-5.13.3.dev1 → tinybird_cli-5.14.0.dev0}/tinybird/tb_cli_modules/fmt.py +0 -0
  35. {tinybird_cli-5.13.3.dev1 → tinybird_cli-5.14.0.dev0}/tinybird/tb_cli_modules/job.py +0 -0
  36. {tinybird_cli-5.13.3.dev1 → tinybird_cli-5.14.0.dev0}/tinybird/tb_cli_modules/regions.py +0 -0
  37. {tinybird_cli-5.13.3.dev1 → tinybird_cli-5.14.0.dev0}/tinybird/tb_cli_modules/tag.py +0 -0
  38. {tinybird_cli-5.13.3.dev1 → tinybird_cli-5.14.0.dev0}/tinybird/tb_cli_modules/telemetry.py +0 -0
  39. {tinybird_cli-5.13.3.dev1 → tinybird_cli-5.14.0.dev0}/tinybird/tb_cli_modules/test.py +0 -0
  40. {tinybird_cli-5.13.3.dev1 → tinybird_cli-5.14.0.dev0}/tinybird/tb_cli_modules/tinyunit/tinyunit_lib.py +0 -0
  41. {tinybird_cli-5.13.3.dev1 → tinybird_cli-5.14.0.dev0}/tinybird/tb_cli_modules/token.py +0 -0
  42. {tinybird_cli-5.13.3.dev1 → tinybird_cli-5.14.0.dev0}/tinybird/tb_cli_modules/workspace.py +0 -0
  43. {tinybird_cli-5.13.3.dev1 → tinybird_cli-5.14.0.dev0}/tinybird/tb_cli_modules/workspace_members.py +0 -0
  44. {tinybird_cli-5.13.3.dev1 → tinybird_cli-5.14.0.dev0}/tinybird_cli.egg-info/SOURCES.txt +0 -0
  45. {tinybird_cli-5.13.3.dev1 → tinybird_cli-5.14.0.dev0}/tinybird_cli.egg-info/dependency_links.txt +0 -0
  46. {tinybird_cli-5.13.3.dev1 → tinybird_cli-5.14.0.dev0}/tinybird_cli.egg-info/entry_points.txt +0 -0
  47. {tinybird_cli-5.13.3.dev1 → tinybird_cli-5.14.0.dev0}/tinybird_cli.egg-info/requires.txt +0 -0
  48. {tinybird_cli-5.13.3.dev1 → tinybird_cli-5.14.0.dev0}/tinybird_cli.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: tinybird_cli
3
- Version: 5.13.3.dev1
3
+ Version: 5.14.0.dev0
4
4
  Summary: Tinybird Command Line Tool
5
5
  Home-page: https://www.tinybird.co/docs/cli
6
6
  Author: Tinybird
@@ -18,14 +18,25 @@ The Tinybird command-line tool allows you to use all the Tinybird functionality
18
18
  Changelog
19
19
  ----------
20
20
 
21
- 5.13.3.dev1
21
+ 5.14.0.dev0
22
+ ***********
23
+
24
+ - `Added` support for `KAFKA_KEY_FORMAT` and `KAFKA_VALUE_FORMAT` parameters for kafka datasources.
25
+
26
+ 5.13.3
22
27
  ***********
23
28
 
24
29
  - `Fixed` updating the cron schedule of a BigQuery data source when having a query but no external_data_source setting.
30
+ - `Fixed` handle Kafka connector with multiple brokers properly.
25
31
 
26
32
  5.13.2
27
33
  ***********
28
34
 
35
+ - `Fixed` Correctly handle Kafka connector with multiple brokers
36
+
37
+ 5.13.1.dev2
38
+ ***********
39
+
29
40
  - `Added` comply with [PEP 625](https://peps.python.org/pep-0625/)
30
41
  - `Added` `eu-west-1` region to the list of available regions.
31
42
 
@@ -4,5 +4,5 @@ __description__ = 'Tinybird Command Line Tool'
4
4
  __url__ = 'https://www.tinybird.co/docs/cli'
5
5
  __author__ = 'Tinybird'
6
6
  __author_email__ = 'support@tinybird.co'
7
- __version__ = '5.13.3.dev1'
8
- __revision__ = '3be7e20'
7
+ __version__ = '5.14.0.dev0'
8
+ __revision__ = '643b049'
@@ -72,7 +72,7 @@ def parse_error_response(response: Response) -> str:
72
72
  if content.get("error", None):
73
73
  error = content["error"]
74
74
  if content.get("errors", None):
75
- error += f' -> errors: {content.get("errors")}'
75
+ error += f" -> errors: {content.get('errors')}"
76
76
  else:
77
77
  error = json.dumps(response, indent=4)
78
78
  return error
@@ -246,7 +246,7 @@ class BigQuery(Connector):
246
246
  uri='{self.gcs.gs_url()}{destination}*.csv',
247
247
  format='CSV',
248
248
  overwrite=true,
249
- header={ "true" if with_headers else "false" },
249
+ header={"true" if with_headers else "false"},
250
250
  field_delimiter=',') AS
251
251
  {sql}
252
252
  """
@@ -319,7 +319,7 @@ class Snowflake(Connector):
319
319
 
320
320
  def create_stage(self):
321
321
  sql = f"""
322
- create stage "{self.options['schema']}".{self.stage()}
322
+ create stage "{self.options["schema"]}".{self.stage()}
323
323
  url='{self.gcs.gcs_url()}'
324
324
  storage_integration = {self.storage_integration()};
325
325
  """
@@ -337,7 +337,7 @@ class Snowflake(Connector):
337
337
  from ({sql})
338
338
  overwrite = true
339
339
  file_format = (TYPE=CSV COMPRESSION=NONE ESCAPE_UNENCLOSED_FIELD=NONE FIELD_DELIMITER='|' FIELD_OPTIONALLY_ENCLOSED_BY='"' null_if=())
340
- header = {"true" if with_headers else "false" }
340
+ header = {"true" if with_headers else "false"}
341
341
  max_file_size = 2500000000;
342
342
  """
343
343
  self.execute(sql)
@@ -1218,6 +1218,8 @@ def parse(
1218
1218
  "kafka_store_raw_value": assign_var("kafka_store_raw_value"),
1219
1219
  "kafka_store_headers": assign_var("kafka_store_headers"),
1220
1220
  "kafka_store_binary_headers": assign_var("kafka_store_binary_headers"),
1221
+ "kafka_key_format": assign_var("kafka_key_format"),
1222
+ "kafka_value_format": assign_var("kafka_value_format"),
1221
1223
  "kafka_key_avro_deserialization": assign_var("kafka_key_avro_deserialization"),
1222
1224
  "kafka_ssl_ca_pem": assign_var("kafka_ssl_ca_pem"),
1223
1225
  "kafka_sasl_mechanism": assign_var("kafka_sasl_mechanism"),
@@ -2175,8 +2177,8 @@ class PipeCheckerRunner:
2175
2177
  AND extractURLParameter(assumeNotNull(url), 'debug') <> 'query'
2176
2178
  AND error = 0
2177
2179
  AND not mapContains(parameters, '__tb__semver')
2178
- {" AND " + " AND ".join([f"mapContains(pipe_request_params, '{match}')" for match in matches]) if matches and len(matches) > 0 else ''}
2179
- { extra_where_clause }
2180
+ {" AND " + " AND ".join([f"mapContains(pipe_request_params, '{match}')" for match in matches]) if matches and len(matches) > 0 else ""}
2181
+ {extra_where_clause}
2180
2182
  Limit 5000000 -- Enough to bring data while not processing all requests from highly used pipes
2181
2183
  )
2182
2184
  group by request_param_names, http_method
@@ -2202,7 +2204,7 @@ class PipeCheckerRunner:
2202
2204
  AND extractURLParameter(assumeNotNull(url), 'debug') <> 'query'
2203
2205
  AND error = 0
2204
2206
  AND not mapContains(parameters, '__tb__semver')
2205
- {" AND " + " AND ".join([f"mapContains(pipe_request_params, '{match}')" for match in matches]) if matches and len(matches) > 0 else ''}
2207
+ {" AND " + " AND ".join([f"mapContains(pipe_request_params, '{match}')" for match in matches]) if matches and len(matches) > 0 else ""}
2206
2208
  {extra_where_clause}
2207
2209
  LIMIT {limit}
2208
2210
  )
@@ -4425,7 +4427,7 @@ async def folder_push(
4425
4427
  name=(
4426
4428
  name
4427
4429
  if to_run[name]["version"] is None
4428
- else f'{name}__v{to_run[name]["version"]}'
4430
+ else f"{name}__v{to_run[name]['version']}"
4429
4431
  )
4430
4432
  )
4431
4433
  )
@@ -4442,7 +4444,7 @@ async def folder_push(
4442
4444
  if raise_on_exists:
4443
4445
  raise AlreadyExistsException(
4444
4446
  FeedbackManager.warning_name_already_exists(
4445
- name=name if to_run[name]["version"] is None else f'{name}__v{to_run[name]["version"]}'
4447
+ name=name if to_run[name]["version"] is None else f"{name}__v{to_run[name]['version']}"
4446
4448
  )
4447
4449
  )
4448
4450
  else:
@@ -4457,7 +4459,7 @@ async def folder_push(
4457
4459
  name=(
4458
4460
  name
4459
4461
  if to_run[name]["version"] is None
4460
- else f'{name}__v{to_run[name]["version"]}'
4462
+ else f"{name}__v{to_run[name]['version']}"
4461
4463
  )
4462
4464
  )
4463
4465
  )
@@ -5013,7 +5015,7 @@ async def format_engine(
5013
5015
  else:
5014
5016
  if node.get("engine", None):
5015
5017
  empty = '""'
5016
- file_parts.append(f'ENGINE {node["engine"]["type"]}' if node.get("engine", {}).get("type") else empty)
5018
+ file_parts.append(f"ENGINE {node['engine']['type']}" if node.get("engine", {}).get("type") else empty)
5017
5019
  file_parts.append(DATAFILE_NEW_LINE)
5018
5020
  for arg in sorted(node["engine"].get("args", [])):
5019
5021
  elem = ", ".join([x.strip() for x in arg[1].split(",")])
@@ -5030,7 +5032,7 @@ async def format_node_type(file_parts: List[str], node: Dict[str, Any]) -> List[
5030
5032
  if node_type == PipeNodeTypes.MATERIALIZED:
5031
5033
  file_parts.append(node_type_upper)
5032
5034
  file_parts.append(DATAFILE_NEW_LINE)
5033
- file_parts.append(f'DATASOURCE {node["datasource"]}')
5035
+ file_parts.append(f"DATASOURCE {node['datasource']}")
5034
5036
  file_parts.append(DATAFILE_NEW_LINE)
5035
5037
  await format_engine(file_parts, node)
5036
5038
 
@@ -5038,10 +5040,10 @@ async def format_node_type(file_parts: List[str], node: Dict[str, Any]) -> List[
5038
5040
  if node_type == PipeNodeTypes.COPY:
5039
5041
  file_parts.append(node_type_upper)
5040
5042
  file_parts.append(DATAFILE_NEW_LINE)
5041
- file_parts.append(f'TARGET_DATASOURCE {node["target_datasource"]}')
5043
+ file_parts.append(f"TARGET_DATASOURCE {node['target_datasource']}")
5042
5044
  if node.get("mode"):
5043
5045
  file_parts.append(DATAFILE_NEW_LINE)
5044
- file_parts.append(f'COPY_MODE {node.get("mode")}')
5046
+ file_parts.append(f"COPY_MODE {node.get('mode')}")
5045
5047
 
5046
5048
  if node.get(CopyParameters.COPY_SCHEDULE):
5047
5049
  is_ondemand = node[CopyParameters.COPY_SCHEDULE].lower() == ON_DEMAND
@@ -5095,7 +5097,7 @@ async def format_node(
5095
5097
  if item and not unroll_includes:
5096
5098
  return
5097
5099
 
5098
- file_parts.append(f'NODE {node["name"].strip()}')
5100
+ file_parts.append(f"NODE {node['name'].strip()}")
5099
5101
  file_parts.append(DATAFILE_NEW_LINE)
5100
5102
 
5101
5103
  from collections import namedtuple
@@ -951,7 +951,7 @@ Ready? """
951
951
  )
952
952
  success_datasource_alter = success_message("** The Data Source has been correctly updated.")
953
953
  success_datasource_kafka_connected = success_message(
954
- "** Data Source '{id}' created\n" "** Kafka streaming connection configured successfully!"
954
+ "** Data Source '{id}' created\n** Kafka streaming connection configured successfully!"
955
955
  )
956
956
  success_datasource_shared = success_message(
957
957
  "** The Data Source {datasource} has been correctly shared with {workspace}"
@@ -86,7 +86,7 @@ def get_format(sql: str) -> Optional[str]:
86
86
  """
87
87
  FORMAT_RE = r"\s+format\s+(\w+)\s*$"
88
88
  sql = sql.strip()
89
- format = re.findall(FORMAT_RE, sql, re.I)
89
+ format = re.findall(FORMAT_RE, sql, re.IGNORECASE)
90
90
  return format[0] if format else None
91
91
 
92
92
 
@@ -100,7 +100,7 @@ def get_format_group(sql: str) -> str:
100
100
  """
101
101
  FORMAT_RE = r"\s+format\s+(\w+)\s*$"
102
102
  sql = sql.strip()
103
- format = re.search(FORMAT_RE, sql, re.I)
103
+ format = re.search(FORMAT_RE, sql, re.IGNORECASE)
104
104
  return format.group() if format else ""
105
105
 
106
106
 
@@ -135,7 +135,7 @@ def remove_format(sql: str) -> str:
135
135
  """
136
136
  FORMAT_RE = r"\s+(format)\s+(\w+)\s*$"
137
137
  sql = sql.strip()
138
- return re.sub(FORMAT_RE, "", sql, flags=re.I)
138
+ return re.sub(FORMAT_RE, "", sql, flags=re.IGNORECASE)
139
139
 
140
140
 
141
141
  def col_name(name: str, backquotes: bool = True) -> str:
@@ -241,7 +241,7 @@ def format_parse_error(
241
241
  message += f" found at position {adjusted_position - len(keyword)}"
242
242
  else:
243
243
  message += (
244
- f" found {repr(table_structure[i]) if len(table_structure)>i else 'EOF'} at position {adjusted_position}"
244
+ f" found {repr(table_structure[i]) if len(table_structure) > i else 'EOF'} at position {adjusted_position}"
245
245
  )
246
246
  return message
247
247
 
@@ -100,7 +100,7 @@ def _format_jinja_node(self, node: Node, max_length: int) -> bool:
100
100
  parts = tag.code.split("\n")
101
101
  prefix = INDENT * (node.depth[0] + node.depth[1])
102
102
  if len(parts) > 1:
103
- tag.code = "\n".join([f'{prefix if i != 0 else ""}{part}' for i, part in enumerate(parts)])
103
+ tag.code = "\n".join([f"{prefix if i != 0 else ''}{part}" for i, part in enumerate(parts)])
104
104
 
105
105
  node.value = str(tag)
106
106
 
@@ -3,7 +3,7 @@ import logging
3
3
  from collections import defaultdict
4
4
  from datetime import datetime
5
5
  from functools import lru_cache
6
- from typing import Any, FrozenSet, List, Optional, Set, Tuple
6
+ from typing import FrozenSet, List, Optional, Set, Tuple
7
7
 
8
8
  from chtoolset import query as chquery
9
9
  from toposort import toposort
@@ -172,7 +172,7 @@ def tables_or_sql(replacement: dict, table_functions=False) -> set:
172
172
  return {replacement}
173
173
 
174
174
 
175
- def _separate_as_tuple_if_contains_database_and_table(definition: str) -> Any:
175
+ def _separate_as_tuple_if_contains_database_and_table(definition: str) -> str | Tuple[str, str]:
176
176
  if "." in definition:
177
177
  database_and_table_separated = definition.split(".")
178
178
  return database_and_table_separated[0], database_and_table_separated[1]
@@ -219,6 +219,7 @@ def replace_tables(
219
219
  output_one_line: bool = False,
220
220
  timestamp: Optional[datetime] = None,
221
221
  function_allow_list: Optional[FrozenSet[str]] = None,
222
+ original_replacements: Optional[dict] = None,
222
223
  ) -> str:
223
224
  """
224
225
  Given a query and a list of table replacements, returns the query after applying the table replacements.
@@ -239,6 +240,12 @@ def replace_tables(
239
240
  _replacements[rk] = r if isinstance(r, tuple) else (default_database, r)
240
241
  _replaced_with.add(r)
241
242
 
243
+ if original_replacements:
244
+ # Some replacements have been expanded by filters and turned to a query str, but we need to send the original
245
+ # ones to is_invalid_resource()
246
+ for r in original_replacements.values():
247
+ _replaced_with.add(r)
248
+
242
249
  deps: defaultdict = defaultdict(set)
243
250
  _tables = sql_get_used_tables(
244
251
  sql,
@@ -1053,7 +1053,7 @@ async def materialize(
1053
1053
  return node, node_name
1054
1054
 
1055
1055
  def _choose_target_datasource_name(pipe, node, node_name):
1056
- return target_datasource or node.get("datasource", None) or f'mv_{pipe["resource_name"]}_{node_name}'
1056
+ return target_datasource or node.get("datasource", None) or f"mv_{pipe['resource_name']}_{node_name}"
1057
1057
 
1058
1058
  def _save_local_backup_pipe(pipe):
1059
1059
  pipe_bak = f"{filename}_bak"
@@ -1231,7 +1231,7 @@ def __patch_click_output():
1231
1231
  if isinstance(substitution, str):
1232
1232
  msg = re.sub(pattern, substitution, str(msg))
1233
1233
  else:
1234
- msg = re.sub(pattern, lambda m: substitution(m.group(0)), str(msg)) # noqa
1234
+ msg = re.sub(pattern, lambda m: substitution(m.group(0)), str(msg)) # noqa: B023
1235
1235
  return msg
1236
1236
 
1237
1237
  def _obfuscate_echo(msg: Any, *args: Any, **kwargs: Any) -> None:
@@ -1252,26 +1252,28 @@ def is_url_valid(url):
1252
1252
  return False
1253
1253
 
1254
1254
 
1255
- def validate_kafka_bootstrap_servers(host_and_port):
1256
- if not isinstance(host_and_port, str):
1255
+ def validate_kafka_bootstrap_servers(bootstrap_servers):
1256
+ if not isinstance(bootstrap_servers, str):
1257
1257
  raise CLIException(FeedbackManager.error_kafka_bootstrap_server())
1258
- parts = host_and_port.split(":")
1259
- if len(parts) > 2:
1260
- raise CLIException(FeedbackManager.error_kafka_bootstrap_server())
1261
- host = parts[0]
1262
- port_str = parts[1] if len(parts) == 2 else "9092"
1263
- try:
1264
- port = int(port_str)
1265
- except Exception:
1266
- raise CLIException(FeedbackManager.error_kafka_bootstrap_server())
1267
- with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock:
1258
+
1259
+ for host_and_port in bootstrap_servers.split(","):
1260
+ parts = host_and_port.split(":")
1261
+ if len(parts) > 2:
1262
+ raise CLIException(FeedbackManager.error_kafka_bootstrap_server())
1263
+ host = parts[0]
1264
+ port_str = parts[1] if len(parts) == 2 else "9092"
1268
1265
  try:
1269
- sock.settimeout(3)
1270
- sock.connect((host, port))
1271
- except socket.timeout:
1272
- raise CLIException(FeedbackManager.error_kafka_bootstrap_server_conn_timeout())
1266
+ port = int(port_str)
1273
1267
  except Exception:
1274
- raise CLIException(FeedbackManager.error_kafka_bootstrap_server_conn())
1268
+ raise CLIException(FeedbackManager.error_kafka_bootstrap_server())
1269
+ with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock:
1270
+ try:
1271
+ sock.settimeout(3)
1272
+ sock.connect((host, port))
1273
+ except socket.timeout:
1274
+ raise CLIException(FeedbackManager.error_kafka_bootstrap_server_conn_timeout())
1275
+ except Exception:
1276
+ raise CLIException(FeedbackManager.error_kafka_bootstrap_server_conn())
1275
1277
 
1276
1278
 
1277
1279
  def validate_kafka_key(s):
@@ -1588,7 +1590,7 @@ async def try_update_config_with_remote(
1588
1590
  def ask_for_admin_token_interactively(ui_host: str, default_token: Optional[str]) -> str:
1589
1591
  return (
1590
1592
  click.prompt(
1591
- f"\nCopy the \"admin your@email\" token from {ui_host}/tokens and paste it here { 'OR press enter to use the token from .tinyb file' if default_token else ''}",
1593
+ f'\nCopy the "admin your@email" token from {ui_host}/tokens and paste it here {"OR press enter to use the token from .tinyb file" if default_token else ""}',
1592
1594
  hide_input=True,
1593
1595
  show_default=False,
1594
1596
  default=default_token,
@@ -117,7 +117,7 @@ async def pipe_stats(ctx: click.Context, pipes: Tuple[str, ...], format_: str):
117
117
  sumIf(error_count, date > now() - interval 7 day) errors,
118
118
  avgMergeIf(avg_duration_state, date > now() - interval 7 day) latency
119
119
  FROM tinybird.pipe_stats
120
- WHERE pipe_id in ({','.join(pipes_to_get_stats)})
120
+ WHERE pipe_id in ({",".join(pipes_to_get_stats)})
121
121
  GROUP BY pipe_id
122
122
  ORDER BY requests DESC
123
123
  FORMAT JSON
@@ -155,7 +155,7 @@ def parse_file(file: str) -> Iterable[TestCase]:
155
155
  )
156
156
  except Exception as e:
157
157
  raise CLIException(
158
- f"""Error: {FeedbackManager.error_exception(error=e)} reading file, check "{file}"->"{definition.get('name')}" """
158
+ f"""Error: {FeedbackManager.error_exception(error=e)} reading file, check "{file}"->"{definition.get("name")}" """
159
159
  )
160
160
 
161
161
 
@@ -370,7 +370,7 @@ class Template:
370
370
  for chunk in self.file.body.chunks:
371
371
  if isinstance(chunk, _ExtendsBlock):
372
372
  if not loader:
373
- raise ParseError("{% extends %} block found, but no " "template loader")
373
+ raise ParseError("{% extends %} block found, but no template loader")
374
374
  template = loader.load(chunk.name, self.name)
375
375
  ancestors.extend(template._get_ancestors(loader))
376
376
  return ancestors
@@ -633,7 +633,7 @@ class _Expression(_Node):
633
633
 
634
634
  def generate(self, writer):
635
635
  writer.write_line("_tt_tmp = %s" % self.expression, self.line)
636
- writer.write_line("if isinstance(_tt_tmp, _tt_string_types):" " _tt_tmp = _tt_utf8(_tt_tmp)", self.line)
636
+ writer.write_line("if isinstance(_tt_tmp, _tt_string_types): _tt_tmp = _tt_utf8(_tt_tmp)", self.line)
637
637
  writer.write_line("else: _tt_tmp = _tt_utf8(str(_tt_tmp))", self.line)
638
638
  if not self.raw and writer.current_template.autoescape is not None:
639
639
  # In python3 functions like xhtml_escape return unicode,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: tinybird-cli
3
- Version: 5.13.3.dev1
3
+ Version: 5.14.0.dev0
4
4
  Summary: Tinybird Command Line Tool
5
5
  Home-page: https://www.tinybird.co/docs/cli
6
6
  Author: Tinybird
@@ -18,14 +18,25 @@ The Tinybird command-line tool allows you to use all the Tinybird functionality
18
18
  Changelog
19
19
  ----------
20
20
 
21
- 5.13.3.dev1
21
+ 5.14.0.dev0
22
+ ***********
23
+
24
+ - `Added` support for `KAFKA_KEY_FORMAT` and `KAFKA_VALUE_FORMAT` parameters for kafka datasources.
25
+
26
+ 5.13.3
22
27
  ***********
23
28
 
24
29
  - `Fixed` updating the cron schedule of a BigQuery data source when having a query but no external_data_source setting.
30
+ - `Fixed` handle Kafka connector with multiple brokers properly.
25
31
 
26
32
  5.13.2
27
33
  ***********
28
34
 
35
+ - `Fixed` Correctly handle Kafka connector with multiple brokers
36
+
37
+ 5.13.1.dev2
38
+ ***********
39
+
29
40
  - `Added` comply with [PEP 625](https://peps.python.org/pep-0625/)
30
41
  - `Added` `eu-west-1` region to the list of available regions.
31
42