tinybird 0.0.1.dev0__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.
Potentially problematic release.
This version of tinybird might be problematic. Click here for more details.
- tinybird/__cli__.py +8 -0
- tinybird/ch_utils/constants.py +244 -0
- tinybird/ch_utils/engine.py +855 -0
- tinybird/check_pypi.py +25 -0
- tinybird/client.py +1281 -0
- tinybird/config.py +117 -0
- tinybird/connectors.py +428 -0
- tinybird/context.py +23 -0
- tinybird/datafile.py +5589 -0
- tinybird/datatypes.py +434 -0
- tinybird/feedback_manager.py +1022 -0
- tinybird/git_settings.py +145 -0
- tinybird/sql.py +865 -0
- tinybird/sql_template.py +2343 -0
- tinybird/sql_template_fmt.py +281 -0
- tinybird/sql_toolset.py +350 -0
- tinybird/syncasync.py +682 -0
- tinybird/tb_cli.py +25 -0
- tinybird/tb_cli_modules/auth.py +252 -0
- tinybird/tb_cli_modules/branch.py +1043 -0
- tinybird/tb_cli_modules/cicd.py +434 -0
- tinybird/tb_cli_modules/cli.py +1571 -0
- tinybird/tb_cli_modules/common.py +2082 -0
- tinybird/tb_cli_modules/config.py +344 -0
- tinybird/tb_cli_modules/connection.py +803 -0
- tinybird/tb_cli_modules/datasource.py +900 -0
- tinybird/tb_cli_modules/exceptions.py +91 -0
- tinybird/tb_cli_modules/fmt.py +91 -0
- tinybird/tb_cli_modules/job.py +85 -0
- tinybird/tb_cli_modules/pipe.py +858 -0
- tinybird/tb_cli_modules/regions.py +9 -0
- tinybird/tb_cli_modules/tag.py +100 -0
- tinybird/tb_cli_modules/telemetry.py +310 -0
- tinybird/tb_cli_modules/test.py +107 -0
- tinybird/tb_cli_modules/tinyunit/tinyunit.py +340 -0
- tinybird/tb_cli_modules/tinyunit/tinyunit_lib.py +71 -0
- tinybird/tb_cli_modules/token.py +349 -0
- tinybird/tb_cli_modules/workspace.py +269 -0
- tinybird/tb_cli_modules/workspace_members.py +212 -0
- tinybird/tornado_template.py +1194 -0
- tinybird-0.0.1.dev0.dist-info/METADATA +2815 -0
- tinybird-0.0.1.dev0.dist-info/RECORD +45 -0
- tinybird-0.0.1.dev0.dist-info/WHEEL +5 -0
- tinybird-0.0.1.dev0.dist-info/entry_points.txt +2 -0
- tinybird-0.0.1.dev0.dist-info/top_level.txt +4 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
from typing import Any, Dict, Optional
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
|
|
5
|
+
from tinybird.tb_cli_modules.telemetry import add_telemetry_event
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class CLIException(click.exceptions.ClickException):
|
|
9
|
+
"""Default exception for all exceptions raised in the CLI.
|
|
10
|
+
|
|
11
|
+
Allows to specift a custom telemetry event to be sent before
|
|
12
|
+
raising the exception (if not specified, it sends a generic
|
|
13
|
+
`error` beacon with the error message)
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(self, message: str, telemetry_event: Optional[str] = None, **kw_telemetry_event_data: Any) -> None:
|
|
17
|
+
telemetry_event = telemetry_event or "error"
|
|
18
|
+
data: Dict[str, Any] = {"error": message}
|
|
19
|
+
data.update(kw_telemetry_event_data)
|
|
20
|
+
add_telemetry_event(telemetry_event, **data)
|
|
21
|
+
super().__init__(message)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class CLIAuthException(CLIException):
|
|
25
|
+
"""Exceptions generated by the auth commands"""
|
|
26
|
+
|
|
27
|
+
def __init__(self, message: str, **kw_telemetry_event_data: Any) -> None:
|
|
28
|
+
super().__init__(message, "auth_error", **kw_telemetry_event_data)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class CLIReleaseException(CLIException):
|
|
32
|
+
"""Exceptions generated by the release commands"""
|
|
33
|
+
|
|
34
|
+
def __init__(self, message: str, **kw_telemetry_event_data: Any) -> None:
|
|
35
|
+
super().__init__(message, "release_error", **kw_telemetry_event_data)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class CLIBranchException(CLIException):
|
|
39
|
+
"""Exceptions generated by the branch commands"""
|
|
40
|
+
|
|
41
|
+
def __init__(self, message: str, **kw_telemetry_event_data: Any) -> None:
|
|
42
|
+
super().__init__(message, "branch_error", **kw_telemetry_event_data)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class CLIGitReleaseException(CLIException):
|
|
46
|
+
"""Exceptions generated by the git release related commands"""
|
|
47
|
+
|
|
48
|
+
def __init__(self, message: str, **kw_telemetry_event_data: Any) -> None:
|
|
49
|
+
super().__init__(message, "git_release_error", **kw_telemetry_event_data)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class CLIConnectionException(CLIException):
|
|
53
|
+
"""Exceptions generated by the connection commands"""
|
|
54
|
+
|
|
55
|
+
def __init__(self, message: str, **kw_telemetry_event_data: Any) -> None:
|
|
56
|
+
super().__init__(message, "connection_error", **kw_telemetry_event_data)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class CLIDatasourceException(CLIException):
|
|
60
|
+
"""Exceptions generated by the datasource commands"""
|
|
61
|
+
|
|
62
|
+
def __init__(self, message: str, **kw_telemetry_event_data: Any) -> None:
|
|
63
|
+
super().__init__(message, "datasource_error", **kw_telemetry_event_data)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class CLIPipeException(CLIException):
|
|
67
|
+
"""Exceptions generated by the pipe commands"""
|
|
68
|
+
|
|
69
|
+
def __init__(self, message: str, **kw_telemetry_event_data: Any) -> None:
|
|
70
|
+
super().__init__(message, "pipe_error", **kw_telemetry_event_data)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class CLIWorkspaceMembersException(CLIException):
|
|
74
|
+
"""Exceptions generated by the workspace members commands"""
|
|
75
|
+
|
|
76
|
+
def __init__(self, message: str, **kw_telemetry_event_data: Any) -> None:
|
|
77
|
+
super().__init__(message, "workspace_members_error", **kw_telemetry_event_data)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class CLIWorkspaceException(CLIException):
|
|
81
|
+
"""Exceptions generated by the workspace commands"""
|
|
82
|
+
|
|
83
|
+
def __init__(self, message: str, **kw_telemetry_event_data: Any) -> None:
|
|
84
|
+
super().__init__(message, "workspace_error", **kw_telemetry_event_data)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class CLITokenException(CLIException):
|
|
88
|
+
"""Exceptions generated by the token commands"""
|
|
89
|
+
|
|
90
|
+
def __init__(self, message: str, **kw_telemetry_event_data: Any) -> None:
|
|
91
|
+
super().__init__(message, "token_error", **kw_telemetry_event_data)
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import difflib
|
|
2
|
+
import sys
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import List, Optional
|
|
5
|
+
|
|
6
|
+
import aiofiles
|
|
7
|
+
import click
|
|
8
|
+
from click import Context
|
|
9
|
+
|
|
10
|
+
from tinybird.datafile import color_diff, format_datasource, format_pipe, is_file_a_datasource, peek
|
|
11
|
+
from tinybird.feedback_manager import FeedbackManager
|
|
12
|
+
from tinybird.tb_cli_modules.cli import cli
|
|
13
|
+
from tinybird.tb_cli_modules.common import coro
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@cli.command()
|
|
17
|
+
@click.argument("filenames", type=click.Path(exists=True), nargs=-1, required=True)
|
|
18
|
+
@click.option(
|
|
19
|
+
"--line-length",
|
|
20
|
+
is_flag=False,
|
|
21
|
+
default=100,
|
|
22
|
+
help="A number indicating the maximum characters per line in the node SQL, lines will be splitted based on the SQL syntax and the number of characters passed as a parameter",
|
|
23
|
+
)
|
|
24
|
+
@click.option("--dry-run", is_flag=True, default=False, help="Don't ask to override the local file")
|
|
25
|
+
@click.option("--yes", is_flag=True, default=False, help="Do not ask for confirmation to overwrite the local file")
|
|
26
|
+
@click.option(
|
|
27
|
+
"--diff",
|
|
28
|
+
is_flag=True,
|
|
29
|
+
default=False,
|
|
30
|
+
help="Formats local file, prints the diff and exits 1 if different, 0 if equal",
|
|
31
|
+
)
|
|
32
|
+
@click.pass_context
|
|
33
|
+
@coro
|
|
34
|
+
async def fmt(
|
|
35
|
+
ctx: Context, filenames: List[str], line_length: int, dry_run: bool, yes: bool, diff: bool
|
|
36
|
+
) -> Optional[str]:
|
|
37
|
+
"""
|
|
38
|
+
Formats a .datasource, .pipe or .incl file
|
|
39
|
+
|
|
40
|
+
This command removes comments starting with # from the file, use DESCRIPTION instead.
|
|
41
|
+
|
|
42
|
+
The format command tries to parse the datafile so syntax errors might rise.
|
|
43
|
+
|
|
44
|
+
.incl files must contain a NODE definition
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
result = ""
|
|
48
|
+
failed = []
|
|
49
|
+
for filename in filenames:
|
|
50
|
+
if not diff:
|
|
51
|
+
click.echo(filename)
|
|
52
|
+
extensions = Path(filename).suffixes
|
|
53
|
+
if is_file_a_datasource(filename):
|
|
54
|
+
result = await format_datasource(filename, skip_eval=True)
|
|
55
|
+
elif (".pipe" in extensions) or (".incl" in extensions):
|
|
56
|
+
result = await format_pipe(filename, line_length, skip_eval=True)
|
|
57
|
+
else:
|
|
58
|
+
click.echo("Unsupported file type. Supported files types are: .pipe, .incl and .datasource")
|
|
59
|
+
return None
|
|
60
|
+
|
|
61
|
+
if diff:
|
|
62
|
+
result = result.rstrip("\n")
|
|
63
|
+
lines_fmt = [f"{line}\n" for line in result.split("\n")]
|
|
64
|
+
async with aiofiles.open(filename, "r") as file:
|
|
65
|
+
lines_file = await file.readlines()
|
|
66
|
+
diff_result = difflib.unified_diff(
|
|
67
|
+
lines_file, lines_fmt, fromfile=f"{Path(filename).name} local", tofile="fmt datafile"
|
|
68
|
+
)
|
|
69
|
+
diff_result = color_diff(diff_result)
|
|
70
|
+
not_empty, diff_lines = peek(diff_result)
|
|
71
|
+
if not_empty:
|
|
72
|
+
sys.stdout.writelines(diff_lines)
|
|
73
|
+
failed.append(filename)
|
|
74
|
+
click.echo("")
|
|
75
|
+
else:
|
|
76
|
+
click.echo(result)
|
|
77
|
+
if dry_run:
|
|
78
|
+
return None
|
|
79
|
+
|
|
80
|
+
if yes or click.confirm(FeedbackManager.prompt_override_local_file(name=filename)):
|
|
81
|
+
async with aiofiles.open(f"{filename}", "w") as file:
|
|
82
|
+
await file.write(result)
|
|
83
|
+
|
|
84
|
+
click.echo(FeedbackManager.success_generated_local_file(file=filename))
|
|
85
|
+
|
|
86
|
+
if len(failed):
|
|
87
|
+
click.echo(FeedbackManager.error_failed_to_format_files(number=len(failed)))
|
|
88
|
+
for f in failed:
|
|
89
|
+
click.echo(f"tb fmt {f} --yes")
|
|
90
|
+
sys.exit(1)
|
|
91
|
+
return result
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# This is a command file for our CLI. Please keep it clean.
|
|
2
|
+
#
|
|
3
|
+
# - If it makes sense and only when strictly necessary, you can create utility functions in this file.
|
|
4
|
+
# - But please, **do not** interleave utility functions and command definitions.
|
|
5
|
+
|
|
6
|
+
import click
|
|
7
|
+
from click import Context
|
|
8
|
+
|
|
9
|
+
from tinybird.client import DoesNotExistException, TinyB
|
|
10
|
+
from tinybird.feedback_manager import FeedbackManager
|
|
11
|
+
from tinybird.tb_cli_modules.cli import cli
|
|
12
|
+
from tinybird.tb_cli_modules.common import coro, echo_safe_humanfriendly_tables_format_smart_table
|
|
13
|
+
from tinybird.tb_cli_modules.exceptions import CLIException
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@cli.group()
|
|
17
|
+
@click.pass_context
|
|
18
|
+
def job(ctx: Context) -> None:
|
|
19
|
+
"""Jobs commands."""
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@job.command(name="ls")
|
|
23
|
+
@click.option(
|
|
24
|
+
"-s",
|
|
25
|
+
"--status",
|
|
26
|
+
help="Show only jobs with this status",
|
|
27
|
+
type=click.Choice(["waiting", "working", "done", "error"], case_sensitive=False),
|
|
28
|
+
multiple=True,
|
|
29
|
+
default=None,
|
|
30
|
+
)
|
|
31
|
+
@click.pass_context
|
|
32
|
+
@coro
|
|
33
|
+
async def jobs_ls(ctx: Context, status: str) -> None:
|
|
34
|
+
"""List jobs, up to 100 in the last 48h"""
|
|
35
|
+
client: TinyB = ctx.ensure_object(dict)["client"]
|
|
36
|
+
jobs = await client.jobs(status=status)
|
|
37
|
+
columns = ["id", "kind", "status", "created at", "updated at", "job url"]
|
|
38
|
+
click.echo(FeedbackManager.info_jobs())
|
|
39
|
+
table = []
|
|
40
|
+
for j in jobs:
|
|
41
|
+
table.append([j[c.replace(" ", "_")] for c in columns])
|
|
42
|
+
echo_safe_humanfriendly_tables_format_smart_table(table, column_names=columns)
|
|
43
|
+
click.echo("\n")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@job.command(name="details")
|
|
47
|
+
@click.argument("job_id")
|
|
48
|
+
@click.pass_context
|
|
49
|
+
@coro
|
|
50
|
+
async def job_details(ctx: Context, job_id: str) -> None:
|
|
51
|
+
"""Get details for any job created in the last 48h"""
|
|
52
|
+
client: TinyB = ctx.ensure_object(dict)["client"]
|
|
53
|
+
job = await client.job(job_id)
|
|
54
|
+
columns = []
|
|
55
|
+
click.echo(FeedbackManager.info_job(job=job_id))
|
|
56
|
+
table = []
|
|
57
|
+
columns = job.keys()
|
|
58
|
+
table = [job.values()]
|
|
59
|
+
echo_safe_humanfriendly_tables_format_smart_table(table, column_names=columns)
|
|
60
|
+
click.echo("\n")
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@job.command(name="cancel")
|
|
64
|
+
@click.argument("job_id")
|
|
65
|
+
@click.pass_context
|
|
66
|
+
@coro
|
|
67
|
+
async def job_cancel(ctx: Context, job_id: str) -> None:
|
|
68
|
+
"""Try to cancel a job"""
|
|
69
|
+
client = ctx.ensure_object(dict)["client"]
|
|
70
|
+
|
|
71
|
+
try:
|
|
72
|
+
result = await client.job_cancel(job_id)
|
|
73
|
+
except DoesNotExistException:
|
|
74
|
+
raise CLIException(FeedbackManager.error_job_does_not_exist(job_id=job_id))
|
|
75
|
+
except Exception as e:
|
|
76
|
+
raise CLIException(FeedbackManager.error_exception(error=e))
|
|
77
|
+
else:
|
|
78
|
+
current_job_status = result["status"]
|
|
79
|
+
if current_job_status == "cancelling":
|
|
80
|
+
click.echo(FeedbackManager.success_job_cancellation_cancelling(job_id=job_id))
|
|
81
|
+
elif current_job_status == "cancelled":
|
|
82
|
+
click.echo(FeedbackManager.success_job_cancellation_cancelled(job_id=job_id))
|
|
83
|
+
else:
|
|
84
|
+
raise CLIException(FeedbackManager.error_job_cancelled_but_status_unknown(job_id=job_id))
|
|
85
|
+
click.echo("\n")
|