tinybird 0.0.1.dev16__py3-none-any.whl → 0.0.1.dev18__py3-none-any.whl

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.
@@ -36,7 +36,6 @@ from tinybird.tb.modules.common import (
36
36
  coro,
37
37
  create_tb_client,
38
38
  echo_safe_format_table,
39
- folder_init,
40
39
  get_current_main_workspace,
41
40
  getenv_bool,
42
41
  is_major_semver,
@@ -81,7 +80,6 @@ DEFAULT_PATTERNS: List[Tuple[str, Union[str, Callable[[str], str]]]] = [
81
80
  )
82
81
  @click.option("--token", help="Use auth token, defaults to TB_TOKEN envvar, then to the .tinyb file")
83
82
  @click.option("--host", help="Use custom host, defaults to TB_HOST envvar, then to https://api.tinybird.co")
84
- @click.option("--semver", help="Semver of a Release to run the command. Example: 1.0.0", hidden=True)
85
83
  @click.option("--gcp-project-id", help="The Google Cloud project ID", hidden=True)
86
84
  @click.option(
87
85
  "--gcs-bucket", help="The Google Cloud Storage bucket to write temp files when using the connectors", hidden=True
@@ -118,7 +116,6 @@ async def cli(
118
116
  debug: bool,
119
117
  token: str,
120
118
  host: str,
121
- semver: str,
122
119
  gcp_project_id: str,
123
120
  gcs_bucket: str,
124
121
  google_application_credentials: str,
@@ -140,7 +137,7 @@ async def cli(
140
137
  """
141
138
 
142
139
  # We need to unpatch for our tests not to break
143
- if show_tokens:
140
+ if show_tokens or local or ctx.invoked_subcommand == "build":
144
141
  __unpatch_click_output()
145
142
  else:
146
143
  __patch_click_output()
@@ -156,9 +153,7 @@ async def cli(
156
153
  config_temp.set_token(token)
157
154
  if host:
158
155
  config_temp.set_host(host)
159
- if semver:
160
- config_temp.set_semver(semver)
161
- if token or host or semver:
156
+ if token or host:
162
157
  await try_update_config_with_remote(config_temp, auto_persist=False, raise_on_errors=False)
163
158
 
164
159
  # Overwrite token and host with env vars manually, without resorting to click.
@@ -170,10 +165,8 @@ async def cli(
170
165
  token = os.environ.get("TB_TOKEN", "")
171
166
  if not host and "TB_HOST" in os.environ:
172
167
  host = os.environ.get("TB_HOST", "")
173
- if not semver and "TB_SEMVER" in os.environ:
174
- semver = os.environ.get("TB_SEMVER", "")
175
168
 
176
- config = await get_config(host, token, semver)
169
+ config = await get_config(host, token)
177
170
  client = _get_tb_client(config.get("token", None), config["host"])
178
171
 
179
172
  # If they have passed a token or host as paramter and it's different that record in .tinyb, refresh the workspace id
@@ -232,88 +225,13 @@ async def cli(
232
225
  logging.debug("debug enabled")
233
226
 
234
227
  ctx.ensure_object(dict)["client"] = (
235
- await get_tinybird_local_client()
236
- if local
237
- else _get_tb_client(config.get("token", None), config["host"], semver)
228
+ await get_tinybird_local_client() if local else _get_tb_client(config.get("token", None), config["host"])
238
229
  )
239
230
 
240
231
  for connector in SUPPORTED_CONNECTORS:
241
232
  load_connector_config(ctx, connector, debug, check_uninstalled=True)
242
233
 
243
234
 
244
- @cli.command()
245
- @click.option(
246
- "--generate-datasources",
247
- is_flag=True,
248
- default=False,
249
- help="Generate datasources based on CSV, NDJSON and Parquet files in this folder",
250
- )
251
- @click.option(
252
- "--folder",
253
- default=None,
254
- type=click.Path(exists=True, file_okay=False),
255
- help="Folder where datafiles will be placed",
256
- )
257
- @click.option("-f", "--force", is_flag=True, default=False, help="Overrides existing files")
258
- @click.option(
259
- "-ir",
260
- "--ignore-remote",
261
- is_flag=True,
262
- default=False,
263
- help="Ignores remote files not present in the local data project on git init",
264
- )
265
- @click.option(
266
- "--git",
267
- is_flag=True,
268
- default=False,
269
- help="Init workspace with git releases. Generates CI/CD files for your git provider",
270
- )
271
- @click.option(
272
- "--override-commit",
273
- default=None,
274
- help="Use this option to manually override the reference commit of your workspace. This is useful if a commit is not recognized in your git log, such as after a force push (git push -f).",
275
- )
276
- @click.option(
277
- "--cicd", is_flag=True, default=False, help="Generates only CI/CD files for your git provider", hidden=True
278
- )
279
- @click.pass_context
280
- @coro
281
- async def init(
282
- ctx: Context,
283
- generate_datasources: bool,
284
- folder: Optional[str],
285
- force: bool,
286
- ignore_remote: bool,
287
- git: bool,
288
- override_commit: Optional[str],
289
- cicd: Optional[bool],
290
- ) -> None:
291
- """Initialize folder layout."""
292
- client: TinyB = ctx.ensure_object(dict)["client"]
293
- config = CLIConfig.get_project_config()
294
- if config.get("token") is None:
295
- raise AuthNoTokenException
296
- folder = folder if folder else getcwd()
297
-
298
- workspaces: List[Dict[str, Any]] = (await client.user_workspaces_and_branches()).get("workspaces", [])
299
- current_ws: Dict[str, Any] = next(
300
- (workspace for workspace in workspaces if config and workspace.get("id", ".") == config.get("id", "..")), {}
301
- )
302
-
303
- if current_ws.get("is_branch"):
304
- raise CLIException(FeedbackManager.error_not_allowed_in_branch())
305
-
306
- await folder_init(client, folder, generate_datasources, generate_releases=True, force=force)
307
-
308
- error = False
309
- final_response = None
310
-
311
- if final_response:
312
- if error:
313
- raise CLIException(final_response)
314
- click.echo(final_response)
315
-
316
-
317
235
  @cli.command()
318
236
  @click.argument("filenames", type=click.Path(exists=True), nargs=-1, default=None)
319
237
  @click.option("--debug", is_flag=True, default=False, help="Print internal representation")
@@ -1347,9 +1265,6 @@ async def deploy(
1347
1265
  check_backfill_required = getenv_bool("TB_CHECK_BACKFILL_REQUIRED", True)
1348
1266
  try:
1349
1267
  tb_client = create_tb_client(ctx)
1350
- if dry_run:
1351
- config.set_semver(None)
1352
- tb_client.semver = None
1353
1268
  await folder_push(
1354
1269
  tb_client=tb_client,
1355
1270
  dry_run=dry_run,
@@ -17,7 +17,7 @@ from contextlib import closing
17
17
  from copy import deepcopy
18
18
  from enum import Enum
19
19
  from functools import wraps
20
- from os import chmod, environ, getcwd, getenv
20
+ from os import environ, getcwd, getenv
21
21
  from pathlib import Path
22
22
  from typing import TYPE_CHECKING, Any, Callable, Dict, Iterable, List, Optional, Set, Tuple, Union
23
23
  from urllib.parse import urlparse
@@ -61,9 +61,7 @@ if TYPE_CHECKING:
61
61
  from tinybird.connectors import Connector
62
62
 
63
63
  from tinybird.feedback_manager import FeedbackManager, warning_message
64
- from tinybird.git_settings import DEFAULT_TINYENV_FILE
65
64
  from tinybird.syncasync import async_to_sync, sync_to_async
66
- from tinybird.tb.modules.cicd import APPEND_FIXTURES_SH, DEFAULT_REQUIREMENTS_FILE, EXEC_TEST_SH
67
65
  from tinybird.tb.modules.config import CLIConfig
68
66
  from tinybird.tb.modules.exceptions import (
69
67
  CLIAuthException,
@@ -218,19 +216,6 @@ async def get_current_environment(client, config):
218
216
  return next((workspace for workspace in workspaces if workspace["id"] == config["id"]), None)
219
217
 
220
218
 
221
- async def get_current_workspace_branches(config: CLIConfig) -> List[Dict[str, Any]]:
222
- current_main_workspace: Optional[Dict[str, Any]] = await get_current_main_workspace(config)
223
- if not current_main_workspace:
224
- raise CLIException(FeedbackManager.error_unable_to_identify_main_workspace())
225
-
226
- client = config.get_client()
227
- user_branches: List[Dict[str, Any]] = (await client.user_workspace_branches()).get("workspaces", [])
228
- all_branches: List[Dict[str, Any]] = (await client.branches()).get("environments", [])
229
- branches = user_branches + [branch for branch in all_branches if branch not in user_branches]
230
-
231
- return [branch for branch in branches if branch.get("main") == current_main_workspace["id"]]
232
-
233
-
234
219
  class AliasedGroup(click.Group):
235
220
  def get_command(self, ctx, cmd_name):
236
221
  # Step one: built-in commands as normal
@@ -320,16 +305,15 @@ def getenv_bool(key: str, default: bool) -> bool:
320
305
  return v.lower() == "true" or v == "1"
321
306
 
322
307
 
323
- def _get_tb_client(token: str, host: str, semver: Optional[str] = None) -> TinyB:
308
+ def _get_tb_client(token: str, host: str) -> TinyB:
324
309
  disable_ssl: bool = getenv_bool("TB_DISABLE_SSL_CHECKS", False)
325
- return TinyB(token, host, version=VERSION, disable_ssl_checks=disable_ssl, send_telemetry=True, semver=semver)
310
+ return TinyB(token, host, version=VERSION, disable_ssl_checks=disable_ssl, send_telemetry=True)
326
311
 
327
312
 
328
313
  def create_tb_client(ctx: Context) -> TinyB:
329
314
  token = ctx.ensure_object(dict)["config"].get("token", "")
330
315
  host = ctx.ensure_object(dict)["config"].get("host", DEFAULT_API_HOST)
331
- semver = ctx.ensure_object(dict)["config"].get("semver", "")
332
- return _get_tb_client(token, host, semver=semver)
316
+ return _get_tb_client(token, host)
333
317
 
334
318
 
335
319
  async def _analyze(filename: str, client: TinyB, format: str, connector: Optional["Connector"] = None):
@@ -385,111 +369,6 @@ async def folder_init(
385
369
  for path in Path(folder).glob(f"*.{format}"):
386
370
  await _generate_datafile(str(path), client, format=format, force=force)
387
371
 
388
- if generate_releases:
389
- base = Path(".")
390
- f = base / (".tinyenv")
391
- if not f.exists() or force:
392
- async with aiofiles.open(".tinyenv", "w") as file:
393
- await file.write(DEFAULT_TINYENV_FILE)
394
- click.echo(FeedbackManager.info_file_created(file=".tinyenv"))
395
- else:
396
- click.echo(FeedbackManager.info_dottinyenv_already_exists())
397
-
398
- base = Path(".")
399
- f = base / ("requirements.txt")
400
- if not f.exists() or force:
401
- async with aiofiles.open("requirements.txt", "w") as file:
402
- await file.write(DEFAULT_REQUIREMENTS_FILE)
403
- click.echo(FeedbackManager.info_file_created(file="requirements.txt"))
404
-
405
- base = Path("scripts")
406
- if not base.exists():
407
- base = Path()
408
- f = base / ("exec_test.sh")
409
- if not f.exists() or force:
410
- async with aiofiles.open(f"{f}", "w") as t_file:
411
- await t_file.write(EXEC_TEST_SH)
412
- click.echo(FeedbackManager.info_file_created(file="scripts/exec_test.sh"))
413
- chmod(f, 0o755)
414
-
415
- f = base / ("append_fixtures.sh")
416
- if not f.exists() or force:
417
- async with aiofiles.open(f"{f}", "w") as t_file:
418
- await t_file.write(APPEND_FIXTURES_SH)
419
- click.echo(FeedbackManager.info_file_created(file="scripts/append_fixtures.sh"))
420
- chmod(f, 0o755)
421
-
422
- base = Path("tests")
423
- if not base.exists():
424
- base = Path()
425
- f = base / ("example.yml")
426
- if not base.exists() or force:
427
- async with aiofiles.open(f"{f}", "w") as t_file:
428
- await t_file.write(
429
- """
430
- ##############################################################################################################################
431
- ### Visit https://www.tinybird.co/docs/production/implementing-test-strategies.html#data-quality-tests ###
432
- ### for more details on Data Quality tests ###
433
- ##############################################################################################################################
434
-
435
- - example_no_negative_numbers:
436
- max_bytes_read: null
437
- max_time: null
438
- sql: |
439
- SELECT
440
- number
441
- FROM numbers(10)
442
- WHERE
443
- number < 0
444
-
445
- # - example_top_products_params_no_empty_top_10_on_2023:
446
- # max_bytes_read: null
447
- # max_time: null
448
- # sql: |
449
- # SELECT *
450
- # FROM top_products_params
451
- # WHERE empty(top_10)
452
- # pipe:
453
- # name: top_products_params
454
- # params:
455
- # start: '2023-01-01'
456
- # end: '2023-12-31'
457
-
458
- """
459
- )
460
-
461
- f = base / ("regression.yaml")
462
- if not base.exists() or force:
463
- async with aiofiles.open(f"{f}", "w") as t_file:
464
- await t_file.write(
465
- """
466
- ############################################################################################################################
467
- ### Visit https://www.tinybird.co/docs/production/implementing-test-strategies.html#regression-tests ###
468
- ### for more details on Regression tests ###
469
- ############################################################################################################################
470
-
471
- ###
472
- ### New pipes are covered by this rule, rules below this one supersede this setting
473
- ###
474
- - pipe: '.*'
475
- tests:
476
- - coverage:
477
-
478
-
479
-
480
- ###
481
- ### These are rules to customize regression testing by pipe using regular expressions
482
- ### For instance skip regression tests for the pipes matching `endpoint_name.*`
483
- ###
484
- - pipe: 'endpoint_name.*'
485
- tests:
486
- - coverage:
487
- config:
488
- skip: True
489
-
490
- """
491
- )
492
-
493
372
 
494
373
  async def configure_connector(connector):
495
374
  if connector not in SUPPORTED_CONNECTORS:
@@ -776,77 +655,6 @@ async def create_workspace_interactive(
776
655
  await create_workspace_non_interactive(ctx, workspace_name, starterkit, user_token, fork) # type: ignore
777
656
 
778
657
 
779
- async def create_workspace_branch(
780
- branch_name: Optional[str],
781
- last_partition: bool,
782
- all: bool,
783
- ignore_datasources: Optional[List[str]],
784
- wait: Optional[bool],
785
- ) -> None:
786
- """
787
- Creates a workspace branch
788
- """
789
- config = CLIConfig.get_project_config()
790
- _ = await try_update_config_with_remote(config)
791
-
792
- try:
793
- workspace = await get_current_workspace(config)
794
- if not workspace:
795
- raise CLIWorkspaceException(FeedbackManager.error_workspace())
796
-
797
- if not branch_name:
798
- click.echo(FeedbackManager.info_workspace_branch_create_greeting())
799
- default_name = f"{workspace['name']}_{uuid.uuid4().hex[0:4]}"
800
- branch_name = click.prompt("\Branch name", default=default_name, err=True, type=str)
801
- assert isinstance(branch_name, str)
802
-
803
- response = await config.get_client().create_workspace_branch(
804
- branch_name,
805
- last_partition,
806
- all,
807
- ignore_datasources,
808
- )
809
- assert isinstance(response, dict)
810
-
811
- is_job: bool = "job" in response
812
- is_summary: bool = "partitions" in response
813
-
814
- if not is_job and not is_summary:
815
- raise CLIException(str(response))
816
-
817
- if all and not is_job:
818
- raise CLIException(str(response))
819
-
820
- click.echo(
821
- FeedbackManager.success_workspace_branch_created(workspace_name=workspace["name"], branch_name=branch_name)
822
- )
823
-
824
- job_id: Optional[str] = None
825
-
826
- if is_job:
827
- job_id = response["job"]["job_id"]
828
- job_url = response["job"]["job_url"]
829
- click.echo(FeedbackManager.info_data_branch_job_url(url=job_url))
830
-
831
- if wait and is_job:
832
- assert isinstance(job_id, str)
833
-
834
- # Await the job to finish and get the result dict
835
- job_response = await wait_job(config.get_client(), job_id, job_url, "Branch creation")
836
- if job_response is None:
837
- raise CLIException(f"Empty job API response (job_id: {job_id}, job_url: {job_url})")
838
- else:
839
- response = job_response.get("result", {})
840
- is_summary = "partitions" in response
841
-
842
- await switch_workspace(config, branch_name, only_environments=True)
843
- if is_summary and (bool(last_partition) or bool(all)):
844
- await print_data_branch_summary(config.get_client(), None, response)
845
-
846
- except Exception as e:
847
- raise CLIException(FeedbackManager.error_exception(error=str(e)))
848
-
849
-
850
658
  async def print_data_branch_summary(client, job_id, response=None):
851
659
  response = await client.job(job_id) if job_id else response or {"partitions": []}
852
660
  columns = ["Data Source", "Partition", "Status", "Error"]
@@ -1327,13 +1135,10 @@ def _get_setting_value(connection, setting, sensitive_settings):
1327
1135
  return connection.get(setting, "")
1328
1136
 
1329
1137
 
1330
- async def switch_workspace(config: CLIConfig, workspace_name_or_id: str, only_environments: bool = False) -> None:
1138
+ async def switch_workspace(config: CLIConfig, workspace_name_or_id: str) -> None:
1331
1139
  try:
1332
- if only_environments:
1333
- workspaces = await get_current_workspace_branches(config)
1334
- else:
1335
- response = await config.get_client().user_workspaces()
1336
- workspaces = response["workspaces"]
1140
+ response = await config.get_client().user_workspaces()
1141
+ workspaces = response["workspaces"]
1337
1142
 
1338
1143
  workspace = next(
1339
1144
  (
@@ -1345,10 +1150,7 @@ async def switch_workspace(config: CLIConfig, workspace_name_or_id: str, only_en
1345
1150
  )
1346
1151
 
1347
1152
  if not workspace:
1348
- if only_environments:
1349
- raise CLIException(FeedbackManager.error_branch(branch=workspace_name_or_id))
1350
- else:
1351
- raise CLIException(FeedbackManager.error_workspace(workspace=workspace_name_or_id))
1153
+ raise CLIException(FeedbackManager.error_workspace(workspace=workspace_name_or_id))
1352
1154
 
1353
1155
  config.set_token(workspace["token"])
1354
1156
  config.set_token_for_host(workspace["token"], config.get_host())
@@ -72,7 +72,6 @@ class CLIConfig:
72
72
  "token": "TB_TOKEN",
73
73
  "user_token": "TB_USER_TOKEN",
74
74
  "host": "TB_HOST",
75
- "semver": "TB_SEMVER",
76
75
  }
77
76
 
78
77
  DEFAULTS: Dict[str, str] = {"host": DEFAULT_API_HOST if not FeatureFlags.is_localhost() else DEFAULT_LOCALHOST}
@@ -178,15 +177,6 @@ class CLIConfig:
178
177
  except KeyError:
179
178
  return None
180
179
 
181
- def set_semver(self, semver: Optional[str]) -> None:
182
- self["semver"] = semver
183
-
184
- def get_semver(self) -> Optional[str]:
185
- try:
186
- return self["semver"]
187
- except KeyError:
188
- return None
189
-
190
180
  def set_token_for_host(self, token: Optional[str], host: Optional[str]) -> None:
191
181
  """Sets the token for the specified host.
192
182