tabsdata-cli 2.0.0__cp312-abi3-win_amd64.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.
Files changed (39) hide show
  1. tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/__init__.py +5 -0
  2. tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/_registry.py +78 -0
  3. tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/assets/manifest/BANNER +5 -0
  4. tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/assets/manifest/LICENSE +3 -0
  5. tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/assets/manifest/README-PyPi.md +8 -0
  6. tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/assets/manifest/RELEASE-NOTES +1 -0
  7. tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/assets/manifest/THIRD-PARTY +3 -0
  8. tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/assets/manifest/VERSION +1 -0
  9. tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/cli_utils.py +993 -0
  10. tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/shell.py +390 -0
  11. tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/td/__init__.py +3 -0
  12. tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/td/auth_group.py +197 -0
  13. tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/td/cli.py +508 -0
  14. tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/td/collection_group.py +458 -0
  15. tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/td/config_utils.py +93 -0
  16. tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/td/connection_group.py +159 -0
  17. tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/td/examples_group.py +206 -0
  18. tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/td/execution_utils.py +429 -0
  19. tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/td/fn_group.py +1109 -0
  20. tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/td/fnrun_group.py +285 -0
  21. tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/td/group_group.py +184 -0
  22. tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/td/metrics_group.py +323 -0
  23. tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/td/permission_group.py +259 -0
  24. tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/td/plan_group.py +324 -0
  25. tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/td/project_group.py +170 -0
  26. tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/td/role_group.py +630 -0
  27. tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/td/settings_group.py +344 -0
  28. tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/td/table_group.py +600 -0
  29. tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/td/trigger_group.py +338 -0
  30. tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/td/trx_group.py +329 -0
  31. tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/td/user_group.py +200 -0
  32. tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/td/worker_group.py +185 -0
  33. tabsdata_cli-2.0.0.data/purelib/tabsdatak/cli/template.py +781 -0
  34. tabsdata_cli-2.0.0.data/purelib/tabsdatak/py.typed +0 -0
  35. tabsdata_cli-2.0.0.dist-info/METADATA +39 -0
  36. tabsdata_cli-2.0.0.dist-info/RECORD +39 -0
  37. tabsdata_cli-2.0.0.dist-info/WHEEL +5 -0
  38. tabsdata_cli-2.0.0.dist-info/licenses/src/main/python/tabsdatak/cli/assets/manifest/LICENSE +3 -0
  39. tabsdata_cli-2.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,324 @@
1
+ #
2
+ # Copyright 2026 Tabsdata Inc.
3
+ #
4
+
5
+ from __future__ import annotations
6
+
7
+ from typing import TYPE_CHECKING, List
8
+
9
+ import rich_click as click
10
+ from rich.table import Table
11
+
12
+ from tabsdatak.cli._registry import cli_command
13
+ from tabsdatak.cli.cli_utils import (
14
+ beautify_time,
15
+ collection_label,
16
+ echo_more_results,
17
+ format_option,
18
+ hint_common_solutions,
19
+ make_console,
20
+ max_result_option,
21
+ output_format,
22
+ render,
23
+ show_hint,
24
+ verify_login_or_prompt,
25
+ )
26
+ from tabsdatak.cli.td.execution_utils import (
27
+ confirm_cancel,
28
+ emit_worker_logs,
29
+ function_runs_table,
30
+ live_monitor,
31
+ obtain_execution_list,
32
+ obtain_list_exec_filters,
33
+ pick_worker,
34
+ plans_table,
35
+ span_projects,
36
+ workers_table,
37
+ )
38
+ from tabsdatak.sdk.status_utils.execution import (
39
+ EXECUTION_FINAL_STATUSES,
40
+ EXECUTION_SUCCESSFUL_FINAL_STATUSES,
41
+ EXECUTION_VALID_USER_PROVIDED_STATUSES,
42
+ )
43
+
44
+ if TYPE_CHECKING:
45
+ from tabsdatak.sdk.tabsdata_server import TabsdataServer
46
+
47
+ AT_OPTION_HELP = (
48
+ "If provided, only plans started before that time will be shown. Must be "
49
+ "either a valid timestamp in the form of a unix timestamp (milliseconds since "
50
+ "epoch without a dot, e.g. '1750074554472') or a valid date-time format. "
51
+ "The valid "
52
+ "formats are 'YYYY-MM-DD', 'YYYY-MM-DDTHH', 'YYYY-MM-DDTHH:MM', "
53
+ "'YYYY-MM-DDTHH:MM:SS', and 'YYYY-MM-DDTHH:MM:SS.sss'. A 'Z' character can be "
54
+ "added at the end to indicate UTC time (e.g., '2023-10-01T12Z' or "
55
+ "'2023-10-01T12:00:00.000Z') or it can be omitted to indicate local "
56
+ "time (e.g., '2023-10-01T12' or '2023-10-01T12:00:00.000')."
57
+ )
58
+
59
+
60
+ @cli_command
61
+ @click.group()
62
+ def plan():
63
+ """Plan (execution) management commands"""
64
+
65
+
66
+ @plan.command(name="list")
67
+ @click.option("--proj", type=str, help="Name of the project to filter plans by.")
68
+ @click.option(
69
+ "--status",
70
+ type=str,
71
+ multiple=True,
72
+ help=(
73
+ "Filter plans by status. The possible statuses are "
74
+ f"'{EXECUTION_VALID_USER_PROVIDED_STATUSES}'. This field is "
75
+ "case-insensitive. "
76
+ ),
77
+ )
78
+ @click.option("--fn", type=str, help="Name of the function to filter plans by.")
79
+ @click.option("--coll", type=str, help="Name of the collection to filter plans by.")
80
+ @click.option(
81
+ "--name",
82
+ "-n",
83
+ help=(
84
+ "A plan name wildcard to match for the list. "
85
+ "For example, 'my_plan*' will match all plans "
86
+ "with names starting with 'my_plan'."
87
+ ),
88
+ )
89
+ @click.option(
90
+ "--last",
91
+ is_flag=True,
92
+ help="If set, only the last plan of the list will be shown.",
93
+ )
94
+ @click.option("--at", help=AT_OPTION_HELP, type=str)
95
+ @click.option(
96
+ "--monitor",
97
+ is_flag=True,
98
+ help=(
99
+ "If set, the command will monitor the listed plans until they all reach "
100
+ "a final state."
101
+ ),
102
+ )
103
+ @format_option
104
+ @max_result_option
105
+ @click.pass_context
106
+ def list_plans(
107
+ ctx: click.Context,
108
+ proj: str,
109
+ status: List[str],
110
+ fn: str,
111
+ coll: str,
112
+ name: str,
113
+ last: bool,
114
+ at: str,
115
+ monitor: bool,
116
+ ):
117
+ """List plans across every project you can view."""
118
+ verify_login_or_prompt(ctx)
119
+ request_filter = obtain_list_exec_filters(
120
+ status=status,
121
+ exe_name=name,
122
+ fn=fn,
123
+ collection=coll,
124
+ at=at,
125
+ project=proj,
126
+ )
127
+ try:
128
+ server: TabsdataServer = ctx.obj["tabsdataserver"]
129
+ span_projects(server)
130
+ list_of_executions, truncated = obtain_execution_list(
131
+ ctx, server, request_filter, last
132
+ )
133
+
134
+ if output_format(ctx) != "table":
135
+ render(
136
+ ctx,
137
+ data=[e._data for e in list_of_executions],
138
+ table=lambda: None,
139
+ )
140
+ echo_more_results(ctx, truncated)
141
+ return
142
+
143
+ if monitor:
144
+ click.echo("Waiting for the plans to finish...")
145
+ list_of_executions = live_monitor(
146
+ list_of_executions,
147
+ lambda: obtain_execution_list(ctx, server, request_filter, last)[0],
148
+ plans_table,
149
+ lambda e: e.status in EXECUTION_FINAL_STATUSES,
150
+ )
151
+ click.echo("Plans finished.")
152
+ else:
153
+ click.echo()
154
+ console = make_console()
155
+ console.print(plans_table(list_of_executions))
156
+ echo_more_results(ctx, truncated)
157
+ click.echo()
158
+ if not all([
159
+ aux_exe.status in EXECUTION_SUCCESSFUL_FINAL_STATUSES
160
+ for aux_exe in list_of_executions
161
+ ]):
162
+ show_hint(
163
+ ctx,
164
+ "If you want to explore why a plan failed, run "
165
+ "'tdk plan logs --plan <PLAN-ID>' to pick a worker and read its "
166
+ "logs, or 'tdk worker list --plan <PLAN-ID>' to see the workers.",
167
+ )
168
+ except Exception as e:
169
+ hint_common_solutions(ctx, e)
170
+ raise click.ClickException(f"Failed to list plans: {e}")
171
+
172
+
173
+ @plan.command()
174
+ @click.option("--plan", required=True, help="ID of the plan to display.")
175
+ @format_option
176
+ @click.pass_context
177
+ def get(ctx: click.Context, plan: str):
178
+ """
179
+ Display a plan: its details plus its function runs and workers.
180
+ """
181
+ verify_login_or_prompt(ctx)
182
+ try:
183
+ server: TabsdataServer = ctx.obj["tabsdataserver"]
184
+ span_projects(server)
185
+ execution = server.get_execution(plan)
186
+ function_runs = server.list_function_runs(filter=f"execution_id:eq:{plan}")
187
+ workers = server.list_workers(filter=f"execution_id:eq:{plan}")
188
+
189
+ if output_format(ctx) != "table":
190
+ render(
191
+ ctx,
192
+ data={
193
+ "plan": execution._data,
194
+ "function_runs": [r._data for r in function_runs],
195
+ "workers": [w._data for w in workers],
196
+ },
197
+ table=lambda: None,
198
+ )
199
+ return
200
+
201
+ table = Table(title="Plan")
202
+ table.add_column("Name")
203
+ table.add_column("Project")
204
+ table.add_column("Collection")
205
+ table.add_column("Function")
206
+ table.add_column("Triggered on")
207
+ table.add_column("Started on")
208
+ table.add_column("Ended on")
209
+ table.add_column("Status", no_wrap=True)
210
+ table.add_row(
211
+ execution.name,
212
+ execution.project or "-",
213
+ collection_label(execution),
214
+ execution.function.name,
215
+ beautify_time(execution.triggered_on_str),
216
+ beautify_time(execution.started_on_str),
217
+ beautify_time(execution.ended_on_str),
218
+ execution.status,
219
+ )
220
+
221
+ click.echo()
222
+ console = make_console()
223
+ console.print(table)
224
+ console.print(function_runs_table(function_runs))
225
+ console.print(workers_table(workers))
226
+ click.echo()
227
+
228
+ except Exception as e:
229
+ hint_common_solutions(ctx, e)
230
+ raise click.ClickException(f"Failed to display plan: {e}")
231
+
232
+
233
+ @plan.command()
234
+ @click.option("--plan", required=True, help="ID of the plan to cancel.")
235
+ @click.option(
236
+ "--confirm",
237
+ help="Write 'cancel' to confirm cancellation. Will be prompted for it if "
238
+ "not provided.",
239
+ )
240
+ @click.pass_context
241
+ def cancel(ctx: click.Context, plan: str, confirm: str):
242
+ """
243
+ Cancel a plan: every non-committed transaction in it is canceled.
244
+ Canceled runs cannot be recovered.
245
+ """
246
+ verify_login_or_prompt(ctx)
247
+ click.echo(f"Canceling plan with ID '{plan}'")
248
+ click.echo("-" * 10)
249
+ confirm_cancel(ctx, confirm, f"plan '{plan}'")
250
+ try:
251
+ server: TabsdataServer = ctx.obj["tabsdataserver"]
252
+ server.cancel_execution(plan)
253
+ click.echo("Plan canceled successfully")
254
+ except Exception as e:
255
+ hint_common_solutions(ctx, e)
256
+ raise click.ClickException(f"Failed to cancel plan: {e}")
257
+
258
+
259
+ @plan.command()
260
+ @click.option("--plan", required=True, help="ID of the plan to recover.")
261
+ @click.pass_context
262
+ def recover(ctx: click.Context, plan: str):
263
+ """
264
+ Recover a plan: its failed or on-hold function runs are re-scheduled.
265
+ """
266
+ verify_login_or_prompt(ctx)
267
+ click.echo(f"Recovering plan with ID '{plan}'")
268
+ click.echo("-" * 10)
269
+ try:
270
+ server: TabsdataServer = ctx.obj["tabsdataserver"]
271
+ server.recover_execution(plan)
272
+ click.echo("Plan recovered successfully")
273
+ except Exception as e:
274
+ hint_common_solutions(ctx, e)
275
+ raise click.ClickException(f"Failed to recover plan: {e}")
276
+
277
+
278
+ @plan.command()
279
+ @click.option("--plan", required=True, help="ID of the plan to monitor.")
280
+ @click.pass_context
281
+ def monitor(ctx: click.Context, plan: str):
282
+ """
283
+ Monitor the execution of a plan.
284
+ """
285
+ verify_login_or_prompt(ctx)
286
+ try:
287
+ server: TabsdataServer = ctx.obj["tabsdataserver"]
288
+ span_projects(server)
289
+ execution = server.get_execution(plan)
290
+ from tabsdatak.cli.td.fn_group import _monitor_execution_or_transaction
291
+
292
+ _monitor_execution_or_transaction(ctx, execution=execution)
293
+ except Exception as e:
294
+ hint_common_solutions(ctx, e)
295
+ raise click.ClickException(f"Failed to monitor plan: {e}")
296
+
297
+
298
+ @plan.command()
299
+ @click.option("--plan", required=True, help="ID of the plan whose logs to show.")
300
+ @click.option(
301
+ "--file",
302
+ help=(
303
+ "Path of the file where the logs will be saved, or '-' to write them to "
304
+ "stdout. Any folders provided before the file must already exist."
305
+ ),
306
+ )
307
+ @format_option
308
+ @click.pass_context
309
+ def logs(ctx: click.Context, plan: str, file: str):
310
+ """
311
+ Show logs of one of the plan's workers; when several ran, pick one from
312
+ the listing.
313
+ """
314
+ verify_login_or_prompt(ctx)
315
+ try:
316
+ server: TabsdataServer = ctx.obj["tabsdataserver"]
317
+ span_projects(server)
318
+ target = pick_worker(ctx, server, [f"execution_id:eq:{plan}"], f"Plan '{plan}'")
319
+ emit_worker_logs(ctx, server, target, file)
320
+ except click.exceptions.Exit:
321
+ raise
322
+ except Exception as e:
323
+ hint_common_solutions(ctx, e)
324
+ raise click.ClickException(f"Failed to show logs: {e}")
@@ -0,0 +1,170 @@
1
+ #
2
+ # Copyright 2026 Tabsdata Inc.
3
+ #
4
+
5
+ from __future__ import annotations
6
+
7
+ from typing import TYPE_CHECKING
8
+
9
+ import rich_click as click
10
+ from rich.table import Table
11
+
12
+ from tabsdatak.cli._registry import cli_command
13
+ from tabsdatak.cli.cli_utils import (
14
+ echo_more_results,
15
+ format_option,
16
+ hint_common_solutions,
17
+ logical_prompt,
18
+ make_console,
19
+ max_result_option,
20
+ render,
21
+ take,
22
+ verify_login_or_prompt,
23
+ )
24
+
25
+ if TYPE_CHECKING:
26
+ from tabsdatak.sdk.tabsdata_server import TabsdataServer
27
+
28
+
29
+ @cli_command
30
+ @click.group()
31
+ def project():
32
+ """Project management commands"""
33
+
34
+
35
+ @project.command()
36
+ @click.option("--name", "-n", help="Name of the project to create.")
37
+ @click.option("--description", help="Description of the project.")
38
+ @click.pass_context
39
+ def create(ctx: click.Context, name: str, description: str):
40
+ """Create a new project"""
41
+ verify_login_or_prompt(ctx)
42
+ name = name or logical_prompt(ctx, "Name of the project to create")
43
+ description = description or ""
44
+ click.echo("Creating a new project")
45
+ click.echo("-" * 10)
46
+ try:
47
+ server: TabsdataServer = ctx.obj["tabsdataserver"]
48
+ server.create_project(name, description)
49
+ click.echo("Project created successfully")
50
+ except Exception as e:
51
+ hint_common_solutions(ctx, e)
52
+ raise click.ClickException(f"Failed to create project: {e}")
53
+
54
+
55
+ @project.command()
56
+ @click.option("--name", "-n", help="Name of the project to delete.")
57
+ @click.option(
58
+ "--confirm",
59
+ help="Write 'delete' to confirm deletion. Will be prompted for it if not provided.",
60
+ )
61
+ @click.pass_context
62
+ def delete(ctx: click.Context, name: str, confirm: str):
63
+ """Delete a project by name."""
64
+ verify_login_or_prompt(ctx)
65
+ name = name or logical_prompt(ctx, "Name of the project to delete")
66
+ click.echo(f"Deleting project: {name}")
67
+ click.echo("-" * 10)
68
+ confirm = confirm or logical_prompt(ctx, "Please type 'delete' to confirm deletion")
69
+ if confirm != "delete":
70
+ raise click.ClickException(
71
+ "Deletion not confirmed. The confirmation word is 'delete'."
72
+ )
73
+ try:
74
+ server: TabsdataServer = ctx.obj["tabsdataserver"]
75
+ server.delete_project(name)
76
+ click.echo("Project deleted successfully")
77
+ except Exception as e:
78
+ hint_common_solutions(ctx, e)
79
+ raise click.ClickException(f"Failed to delete project: {e}")
80
+
81
+
82
+ @project.command()
83
+ @format_option
84
+ @click.option("--name", "-n", help="Name of the project to display.")
85
+ @click.pass_context
86
+ def get(ctx: click.Context, name: str):
87
+ """Display a project by name"""
88
+ verify_login_or_prompt(ctx)
89
+ name = name or logical_prompt(ctx, "Name of the project to display")
90
+ try:
91
+ server: TabsdataServer = ctx.obj["tabsdataserver"]
92
+ prj = server.get_project(name)
93
+
94
+ def _table():
95
+ table = Table(title="Project", show_header=False, show_lines=True)
96
+ table.add_column("Field", style="cyan", no_wrap=True)
97
+ table.add_column("Value")
98
+ table.add_row("Name:", prj.name)
99
+ table.add_row("Description:", prj.description or "")
100
+ table.add_row("Created by:", prj.created_by or "-")
101
+ table.add_row("Created on:", prj.created_on_str or "-")
102
+ click.echo()
103
+ make_console(emoji=False, markup=False).print(table)
104
+ click.echo()
105
+
106
+ render(ctx, data=lambda: prj._data, table=_table)
107
+ except Exception as e:
108
+ hint_common_solutions(ctx, e)
109
+ raise click.ClickException(f"Failed to display project: {e}")
110
+
111
+
112
+ @project.command()
113
+ @format_option
114
+ @max_result_option
115
+ @click.pass_context
116
+ def list(ctx: click.Context):
117
+ """List all projects"""
118
+ verify_login_or_prompt(ctx)
119
+ try:
120
+ server: TabsdataServer = ctx.obj["tabsdataserver"]
121
+ list_of_projects, truncated = take(ctx, server.list_projects_generator())
122
+
123
+ def _table():
124
+ table = Table(title="Projects")
125
+ table.add_column("Name", style="cyan", no_wrap=True)
126
+ table.add_column("Description")
127
+ table.add_column("Created by")
128
+ table.add_column("Created on")
129
+ for prj in list_of_projects:
130
+ table.add_row(
131
+ prj.name,
132
+ prj.description,
133
+ prj.created_by,
134
+ prj.created_on_str,
135
+ )
136
+ click.echo()
137
+ make_console().print(table)
138
+ click.echo(f"Number of projects: {len(list_of_projects)}")
139
+ click.echo()
140
+
141
+ render(ctx, data=lambda: [p._data for p in list_of_projects], table=_table)
142
+ echo_more_results(ctx, truncated)
143
+ except Exception as e:
144
+ hint_common_solutions(ctx, e)
145
+ raise click.ClickException(f"Failed to list projects: {e}")
146
+
147
+
148
+ @project.command()
149
+ @click.option("--name", "-n", help="Name of the project to update.")
150
+ @click.option("--new-name", help="New name for the project.")
151
+ @click.option("--description", help="New description for the project.")
152
+ @click.pass_context
153
+ def update(
154
+ ctx: click.Context,
155
+ name: str,
156
+ new_name: str,
157
+ description: str,
158
+ ):
159
+ """Update a project by name"""
160
+ verify_login_or_prompt(ctx)
161
+ name = name or logical_prompt(ctx, "Name of the project to update")
162
+ click.echo(f"Updating project: {name}")
163
+ click.echo("-" * 10)
164
+ try:
165
+ server: TabsdataServer = ctx.obj["tabsdataserver"]
166
+ server.update_project(name, new_name=new_name, new_description=description)
167
+ click.echo("Project updated successfully")
168
+ except Exception as e:
169
+ hint_common_solutions(ctx, e)
170
+ raise click.ClickException(f"Failed to update project: {e}")