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,285 @@
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
+ logical_prompt,
20
+ make_console,
21
+ max_result_option,
22
+ output_format,
23
+ render,
24
+ take,
25
+ verify_login_or_prompt,
26
+ )
27
+ from tabsdatak.cli.td.execution_utils import (
28
+ echo_plan_status,
29
+ echo_transaction_status,
30
+ emit_worker_logs,
31
+ function_runs_table,
32
+ obtain_list_fn_run_filters,
33
+ pick_worker,
34
+ span_projects,
35
+ workers_table,
36
+ )
37
+ from tabsdatak.sdk.status_utils.function_run import (
38
+ FUNCTION_RUN_VALID_USER_PROVIDED_STATUSES,
39
+ )
40
+
41
+ if TYPE_CHECKING:
42
+ from tabsdatak.sdk.tabsdata_server import TabsdataServer
43
+
44
+
45
+ @cli_command
46
+ @click.group()
47
+ def fnrun():
48
+ """Function run management commands"""
49
+
50
+
51
+ @fnrun.command(name="list")
52
+ @click.option(
53
+ "--proj", type=str, help="Name of the project to filter function runs by."
54
+ )
55
+ @click.option(
56
+ "--status",
57
+ type=str,
58
+ multiple=True,
59
+ help=(
60
+ "Filter function runs by status. The possible statuses are "
61
+ f"'{FUNCTION_RUN_VALID_USER_PROVIDED_STATUSES}'. This field is "
62
+ "case-insensitive. "
63
+ ),
64
+ )
65
+ @click.option(
66
+ "--coll",
67
+ type=str,
68
+ help="Collection of the function to which the function runs belong.",
69
+ )
70
+ @click.option(
71
+ "--fn",
72
+ type=str,
73
+ help="Name of the function to which the function runs belong.",
74
+ )
75
+ @click.option("--plan", type=str, help="ID of the plan to filter function runs by.")
76
+ @click.option(
77
+ "--plan-name",
78
+ help=(
79
+ "A plan name wildcard to match. "
80
+ "For example, 'my_plan*' will match all plans "
81
+ "with names starting with 'my_plan'."
82
+ ),
83
+ )
84
+ @click.option(
85
+ "--trx", type=str, help="ID of the transaction to filter function runs by."
86
+ )
87
+ @format_option
88
+ @max_result_option
89
+ @click.pass_context
90
+ def list_runs(
91
+ ctx: click.Context,
92
+ proj: str,
93
+ status: List[str],
94
+ coll: str,
95
+ fn: str,
96
+ plan: str,
97
+ plan_name: str,
98
+ trx: str,
99
+ ):
100
+ """List function runs across every project you can view."""
101
+ verify_login_or_prompt(ctx)
102
+ try:
103
+ server: TabsdataServer = ctx.obj["tabsdataserver"]
104
+ span_projects(server)
105
+ request_filter = obtain_list_fn_run_filters(
106
+ status=status,
107
+ exe_name=plan_name,
108
+ execution_id=plan,
109
+ fn=fn,
110
+ collection=coll,
111
+ trx=trx,
112
+ project=proj,
113
+ )
114
+ if output_format(ctx) == "table":
115
+ if plan:
116
+ echo_plan_status(server, plan)
117
+ if trx:
118
+ echo_transaction_status(server, trx)
119
+ list_of_function_runs, truncated = take(
120
+ ctx, server.list_function_runs_generator(filter=request_filter)
121
+ )
122
+
123
+ if output_format(ctx) != "table":
124
+ render(
125
+ ctx,
126
+ data=[r._data for r in list_of_function_runs],
127
+ table=lambda: None,
128
+ )
129
+ echo_more_results(ctx, truncated)
130
+ return
131
+
132
+ click.echo()
133
+ console = make_console()
134
+ console.print(function_runs_table(list_of_function_runs))
135
+ click.echo(f"Number of function runs: {len(list_of_function_runs)}")
136
+ echo_more_results(ctx, truncated)
137
+ click.echo()
138
+ except Exception as e:
139
+ hint_common_solutions(ctx, e)
140
+ raise click.ClickException(f"Failed to list function runs: {e}")
141
+
142
+
143
+ @fnrun.command()
144
+ @click.option("--fnrun", required=True, help="ID of the function run to display.")
145
+ @format_option
146
+ @click.pass_context
147
+ def get(ctx: click.Context, fnrun: str):
148
+ """
149
+ Display a function run: its details plus its workers.
150
+ """
151
+ verify_login_or_prompt(ctx)
152
+ try:
153
+ server: TabsdataServer = ctx.obj["tabsdataserver"]
154
+ span_projects(server)
155
+ function_run = server.get_function_run(fnrun)
156
+ workers = server.list_workers(filter=f"logical_function_run_id:eq:{fnrun}")
157
+
158
+ if output_format(ctx) != "table":
159
+ render(
160
+ ctx,
161
+ data={
162
+ "function_run": function_run._data,
163
+ "workers": [w._data for w in workers],
164
+ },
165
+ table=lambda: None,
166
+ )
167
+ return
168
+
169
+ table = Table(title="Function run")
170
+ table.add_column("Project")
171
+ table.add_column("Collection")
172
+ table.add_column("Function")
173
+ table.add_column("Plan name")
174
+ table.add_column("Plan ID", no_wrap=True)
175
+ table.add_column("Transaction ID", no_wrap=True)
176
+ table.add_column("Started on")
177
+ table.add_column("Ended on")
178
+ table.add_column("Status", no_wrap=True)
179
+ table.add_row(
180
+ function_run.project or "-",
181
+ collection_label(function_run),
182
+ function_run.function.name,
183
+ function_run.execution.name,
184
+ function_run.execution.id,
185
+ function_run.transaction.id,
186
+ beautify_time(function_run.started_on_str),
187
+ beautify_time(function_run.ended_on_str),
188
+ function_run.status,
189
+ )
190
+
191
+ click.echo()
192
+ console = make_console()
193
+ console.print(table)
194
+ console.print(workers_table(workers))
195
+ click.echo()
196
+
197
+ except Exception as e:
198
+ hint_common_solutions(ctx, e)
199
+ raise click.ClickException(f"Failed to display function run: {e}")
200
+
201
+
202
+ @fnrun.command()
203
+ @click.option(
204
+ "--fnrun", required=True, help="ID of the function run whose logs to show."
205
+ )
206
+ @click.option(
207
+ "--file",
208
+ help=(
209
+ "Path of the file where the logs will be saved, or '-' to write them to "
210
+ "stdout. Any folders provided before the file must already exist."
211
+ ),
212
+ )
213
+ @format_option
214
+ @click.pass_context
215
+ def logs(ctx: click.Context, fnrun: str, file: str):
216
+ """
217
+ Show logs of one of the function run's workers; when several ran (retries),
218
+ pick one from the listing.
219
+ """
220
+ verify_login_or_prompt(ctx)
221
+ try:
222
+ server: TabsdataServer = ctx.obj["tabsdataserver"]
223
+ span_projects(server)
224
+ target = pick_worker(
225
+ ctx,
226
+ server,
227
+ [f"logical_function_run_id:eq:{fnrun}"],
228
+ f"Function run '{fnrun}'",
229
+ )
230
+ emit_worker_logs(ctx, server, target, file)
231
+ except click.exceptions.Exit:
232
+ raise
233
+ except Exception as e:
234
+ hint_common_solutions(ctx, e)
235
+ raise click.ClickException(f"Failed to show logs: {e}")
236
+
237
+
238
+ @fnrun.command()
239
+ @click.option("--fnrun", help="ID of the function run to replay.")
240
+ @click.pass_context
241
+ def replay(ctx: click.Context, fnrun: str):
242
+ """Re-execute a finished function run.
243
+
244
+ Mints a new concrete physical function run against the latest patch (or
245
+ head, if no patch exists). The pointer stays bound to the previous
246
+ attempt's outputs until the new attempt finishes successfully.
247
+ """
248
+ verify_login_or_prompt(ctx)
249
+ fnrun = fnrun or logical_prompt(ctx, "ID of the function run to replay")
250
+ click.echo(f"Replaying function run '{fnrun}'")
251
+ click.echo("-" * 10)
252
+ try:
253
+ server: TabsdataServer = ctx.obj["tabsdataserver"]
254
+ result = server.replay_logical_function_run(fnrun)
255
+ click.echo("Replay scheduled")
256
+ click.echo(f"Run id: {result.get('logical_function_run_id')}")
257
+ click.echo(f"Previous FR (bound): {result.get('previous_function_run_id')}")
258
+ click.echo(f"New FR (running): {result.get('new_function_run_id')}")
259
+ click.echo(f"Function version id: {result.get('function_version_id')}")
260
+ except Exception as e:
261
+ hint_common_solutions(ctx, e)
262
+ raise click.ClickException(f"Failed to replay function run: {e}")
263
+
264
+
265
+ @fnrun.command()
266
+ @click.option("--fnrun", required=True, help="ID of the function run to recover.")
267
+ @click.pass_context
268
+ def recover(ctx: click.Context, fnrun: str):
269
+ """
270
+ Recover the transaction a function run belongs to: the transaction's
271
+ failed or on-hold function runs are re-scheduled.
272
+ """
273
+ verify_login_or_prompt(ctx)
274
+ try:
275
+ server: TabsdataServer = ctx.obj["tabsdataserver"]
276
+ span_projects(server)
277
+ function_run = server.get_function_run(fnrun)
278
+ trx = function_run.transaction.id
279
+ click.echo(f"Recovering transaction '{trx}' of function run '{fnrun}'")
280
+ click.echo("-" * 10)
281
+ server.recover_transaction(trx)
282
+ click.echo("Transaction recovered successfully")
283
+ except Exception as e:
284
+ hint_common_solutions(ctx, e)
285
+ raise click.ClickException(f"Failed to recover function run: {e}")
@@ -0,0 +1,184 @@
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
+ beautify_list,
15
+ echo_more_results,
16
+ format_option,
17
+ hint_common_solutions,
18
+ logical_prompt,
19
+ make_console,
20
+ max_result_option,
21
+ proj_option,
22
+ render,
23
+ take,
24
+ verify_login_or_prompt,
25
+ )
26
+
27
+ if TYPE_CHECKING:
28
+ from tabsdatak.sdk.tabsdata_server import TabsdataServer
29
+
30
+
31
+ @cli_command
32
+ @click.group()
33
+ def group():
34
+ """Group management commands"""
35
+
36
+
37
+ @group.command()
38
+ @proj_option
39
+ @click.option("--name", "-n", help="Name of the group to create.")
40
+ @click.option("--description", help="Description of the group.")
41
+ @click.pass_context
42
+ def create(ctx: click.Context, name: str, description: str):
43
+ """Create a new group"""
44
+ verify_login_or_prompt(ctx)
45
+ name = name or logical_prompt(ctx, "Name of the group to create")
46
+ description = description or ""
47
+ click.echo("Creating a new group")
48
+ click.echo("-" * 10)
49
+ try:
50
+ server: TabsdataServer = ctx.obj["tabsdataserver"]
51
+ server.create_group(name, description)
52
+ click.echo("Group created successfully")
53
+ except Exception as e:
54
+ hint_common_solutions(ctx, e)
55
+ raise click.ClickException(f"Failed to create group: {e}")
56
+
57
+
58
+ @group.command()
59
+ @proj_option
60
+ @click.option("--name", "-n", help="Name of the group to delete.")
61
+ @click.option(
62
+ "--confirm",
63
+ help="Write 'delete' to confirm deletion. Will be prompted for it if not provided.",
64
+ )
65
+ @click.pass_context
66
+ def delete(ctx: click.Context, name: str, confirm: str):
67
+ """Delete a group by name. System groups cannot be deleted."""
68
+ verify_login_or_prompt(ctx)
69
+ name = name or logical_prompt(ctx, "Name of the group to delete")
70
+ click.echo(f"Deleting group: {name}")
71
+ click.echo("-" * 10)
72
+ confirm = confirm or logical_prompt(ctx, "Please type 'delete' to confirm deletion")
73
+ if confirm != "delete":
74
+ raise click.ClickException(
75
+ "Deletion not confirmed. The confirmation word is 'delete'."
76
+ )
77
+ try:
78
+ server: TabsdataServer = ctx.obj["tabsdataserver"]
79
+ server.delete_group(name)
80
+ click.echo("Group deleted successfully")
81
+ except Exception as e:
82
+ hint_common_solutions(ctx, e)
83
+ raise click.ClickException(f"Failed to delete group: {e}")
84
+
85
+
86
+ @group.command()
87
+ @proj_option
88
+ @format_option
89
+ @click.option("--name", "-n", help="Name of the group to display.")
90
+ @click.pass_context
91
+ def get(ctx: click.Context, name: str):
92
+ """Display a group by name"""
93
+ verify_login_or_prompt(ctx)
94
+ name = name or logical_prompt(ctx, "Name of the group to display")
95
+ try:
96
+ server: TabsdataServer = ctx.obj["tabsdataserver"]
97
+ grp = server.get_group(name)
98
+ collection_names = sorted(
99
+ c.name
100
+ for c in server.list_collections()
101
+ if (c._data or {}).get("group") == name
102
+ )
103
+
104
+ def _table():
105
+ table = Table(title="Group", show_header=False, show_lines=True)
106
+ table.add_column("Field", style="cyan", no_wrap=True)
107
+ table.add_column("Value")
108
+ table.add_row("Name:", grp.name)
109
+ table.add_row("Description:", grp.description or "")
110
+ table.add_row("System:", "Yes" if grp.system else "No")
111
+ table.add_row("Created by:", grp.created_by or "-")
112
+ table.add_row("Created on:", grp.created_on_str or "-")
113
+ table.add_row("Collections:", beautify_list(collection_names))
114
+ click.echo()
115
+ make_console(emoji=False, markup=False).print(table)
116
+ click.echo()
117
+
118
+ render(ctx, data=lambda: grp._data, table=_table)
119
+ except Exception as e:
120
+ hint_common_solutions(ctx, e)
121
+ raise click.ClickException(f"Failed to display group: {e}")
122
+
123
+
124
+ @group.command()
125
+ @proj_option
126
+ @format_option
127
+ @max_result_option
128
+ @click.pass_context
129
+ def list(ctx: click.Context):
130
+ """List all groups"""
131
+ verify_login_or_prompt(ctx)
132
+ try:
133
+ server: TabsdataServer = ctx.obj["tabsdataserver"]
134
+ list_of_groups, truncated = take(ctx, server.list_groups_generator())
135
+
136
+ def _table():
137
+ table = Table(title="Groups")
138
+ table.add_column("Name", style="cyan", no_wrap=True)
139
+ table.add_column("Description")
140
+ table.add_column("Created by")
141
+ table.add_column("Created on")
142
+ for grp in list_of_groups:
143
+ table.add_row(
144
+ grp.name,
145
+ grp.description,
146
+ grp.created_by,
147
+ grp.created_on_str,
148
+ )
149
+ click.echo()
150
+ make_console().print(table)
151
+ click.echo(f"Number of groups: {len(list_of_groups)}")
152
+ click.echo()
153
+
154
+ render(ctx, data=lambda: [g._data for g in list_of_groups], table=_table)
155
+ echo_more_results(ctx, truncated)
156
+ except Exception as e:
157
+ hint_common_solutions(ctx, e)
158
+ raise click.ClickException(f"Failed to list groups: {e}")
159
+
160
+
161
+ @group.command()
162
+ @proj_option
163
+ @click.option("--name", "-n", help="Name of the group to update.")
164
+ @click.option("--new-name", help="New name for the group.")
165
+ @click.option("--description", help="New description for the group.")
166
+ @click.pass_context
167
+ def update(
168
+ ctx: click.Context,
169
+ name: str,
170
+ new_name: str,
171
+ description: str,
172
+ ):
173
+ """Update a group by name. System groups cannot be updated."""
174
+ verify_login_or_prompt(ctx)
175
+ name = name or logical_prompt(ctx, "Name of the group to update")
176
+ click.echo(f"Updating group: {name}")
177
+ click.echo("-" * 10)
178
+ try:
179
+ server: TabsdataServer = ctx.obj["tabsdataserver"]
180
+ server.update_group(name, new_name=new_name, new_description=description)
181
+ click.echo("Group updated successfully")
182
+ except Exception as e:
183
+ hint_common_solutions(ctx, e)
184
+ raise click.ClickException(f"Failed to update group: {e}")