remotivelabs-cli 0.0.25__py3-none-any.whl → 0.0.26__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.
- cli/broker/brokers.py +2 -2
- cli/broker/export.py +5 -5
- cli/broker/files.py +5 -5
- cli/broker/lib/broker.py +59 -49
- cli/broker/license_flows.py +11 -9
- cli/broker/licenses.py +2 -2
- cli/broker/playback.py +14 -34
- cli/broker/record.py +3 -3
- cli/broker/scripting.py +4 -4
- cli/broker/signals.py +11 -11
- cli/cloud/__init__.py +0 -1
- cli/cloud/auth.py +40 -35
- cli/cloud/auth_tokens.py +39 -36
- cli/cloud/brokers.py +24 -33
- cli/cloud/cloud_cli.py +9 -6
- cli/cloud/configs.py +19 -11
- cli/cloud/filestorage.py +63 -51
- cli/cloud/projects.py +10 -7
- cli/cloud/recordings.py +127 -108
- cli/cloud/recordings_playback.py +52 -39
- cli/cloud/rest_helper.py +247 -196
- cli/cloud/resumable_upload.py +9 -8
- cli/cloud/sample_recordings.py +5 -5
- cli/cloud/service_account_tokens.py +18 -16
- cli/cloud/service_accounts.py +9 -9
- cli/connect/__init__.py +0 -1
- cli/connect/connect.py +7 -6
- cli/connect/protopie/protopie.py +32 -16
- cli/errors.py +6 -5
- cli/remotive.py +13 -9
- cli/requirements.txt +4 -1
- cli/settings.py +9 -9
- cli/tools/__init__.py +0 -1
- cli/tools/can/__init__.py +0 -1
- cli/tools/can/can.py +8 -8
- cli/tools/tools.py +2 -2
- {remotivelabs_cli-0.0.25.dist-info → remotivelabs_cli-0.0.26.dist-info}/METADATA +5 -3
- remotivelabs_cli-0.0.26.dist-info/RECORD +44 -0
- remotivelabs_cli-0.0.25.dist-info/RECORD +0 -44
- {remotivelabs_cli-0.0.25.dist-info → remotivelabs_cli-0.0.26.dist-info}/LICENSE +0 -0
- {remotivelabs_cli-0.0.25.dist-info → remotivelabs_cli-0.0.26.dist-info}/WHEEL +0 -0
- {remotivelabs_cli-0.0.25.dist-info → remotivelabs_cli-0.0.26.dist-info}/entry_points.txt +0 -0
cli/cloud/recordings_playback.py
CHANGED
@@ -3,20 +3,21 @@ from __future__ import annotations
|
|
3
3
|
import datetime
|
4
4
|
import json
|
5
5
|
import os
|
6
|
+
import sys
|
6
7
|
import tempfile
|
7
8
|
from pathlib import Path
|
8
|
-
from typing import List, Union
|
9
|
+
from typing import Any, List, Union
|
9
10
|
|
10
11
|
import grpc
|
11
12
|
import rich
|
12
13
|
import typer
|
13
|
-
from rich import print as
|
14
|
+
from rich import print as rich_print
|
14
15
|
from rich.progress import Progress, SpinnerColumn, TextColumn
|
15
16
|
|
16
17
|
from cli.errors import ErrorPrinter
|
17
18
|
|
18
19
|
from ..broker.lib.broker import Broker, SubscribableSignal
|
19
|
-
from . import
|
20
|
+
from .rest_helper import RestHelper as Rest
|
20
21
|
|
21
22
|
app = typer.Typer(
|
22
23
|
help="""
|
@@ -32,7 +33,7 @@ def play(
|
|
32
33
|
project: str = typer.Option(..., help="Project ID", envvar="REMOTIVE_CLOUD_PROJECT"),
|
33
34
|
show_progress: bool = typer.Option(False, help="Show progress after started playing"),
|
34
35
|
repeat: bool = typer.Option(False, help="Repeat recording - must keep command running in terminal"),
|
35
|
-
):
|
36
|
+
) -> None:
|
36
37
|
"""
|
37
38
|
Start playing a recording.
|
38
39
|
There is no problem invoking play multiple times since if it is already playing the command will be ignored.
|
@@ -47,7 +48,7 @@ def pause(
|
|
47
48
|
recording_session: str = typer.Argument(..., help="Recording session id", envvar="REMOTIVE_CLOUD_RECORDING_SESSION"),
|
48
49
|
broker: str = typer.Option(None, help="Broker to use"),
|
49
50
|
project: str = typer.Option(..., help="Project ID", envvar="REMOTIVE_CLOUD_PROJECT"),
|
50
|
-
):
|
51
|
+
) -> None:
|
51
52
|
"""
|
52
53
|
Pause a recording
|
53
54
|
"""
|
@@ -59,7 +60,7 @@ def progress(
|
|
59
60
|
recording_session: str = typer.Argument(..., help="Recording session id", envvar="REMOTIVE_CLOUD_RECORDING_SESSION"),
|
60
61
|
broker: str = typer.Option(None, help="Broker to use"),
|
61
62
|
project: str = typer.Option(..., help="Project ID", envvar="REMOTIVE_CLOUD_PROJECT"),
|
62
|
-
):
|
63
|
+
) -> None:
|
63
64
|
"""
|
64
65
|
Shows progress of the recording playing.
|
65
66
|
Use --repeat to have the recording replayed when it reaches the end.
|
@@ -73,7 +74,7 @@ def seek(
|
|
73
74
|
seconds: int = typer.Option(..., min=0, help="Target offset in seconds"),
|
74
75
|
broker: str = typer.Option(None, help="Broker to use"),
|
75
76
|
project: str = typer.Option(..., help="Project ID", envvar="REMOTIVE_CLOUD_PROJECT"),
|
76
|
-
):
|
77
|
+
) -> None:
|
77
78
|
"""
|
78
79
|
Seek seconds into a recording
|
79
80
|
"""
|
@@ -85,7 +86,7 @@ def stop(
|
|
85
86
|
recording_session: str = typer.Argument(..., help="Recording session id", envvar="REMOTIVE_CLOUD_RECORDING_SESSION"),
|
86
87
|
broker: str = typer.Option(None, help="Broker to use"),
|
87
88
|
project: str = typer.Option(..., help="Project ID", envvar="REMOTIVE_CLOUD_PROJECT"),
|
88
|
-
):
|
89
|
+
) -> None:
|
89
90
|
"""
|
90
91
|
Stop playing
|
91
92
|
"""
|
@@ -99,6 +100,7 @@ def read_scripted_code_file(file_path: Path) -> bytes:
|
|
99
100
|
return file.read()
|
100
101
|
|
101
102
|
|
103
|
+
# pylint: disable=R0913
|
102
104
|
@app.command()
|
103
105
|
def subscribe(
|
104
106
|
recording_session: str = typer.Argument(..., help="Recording session id", envvar="REMOTIVE_CLOUD_RECORDING_SESSION"),
|
@@ -116,7 +118,7 @@ def subscribe(
|
|
116
118
|
),
|
117
119
|
on_change_only: bool = typer.Option(default=False, help="Only get signal if value is changed"),
|
118
120
|
project: str = typer.Option(..., help="Project ID", envvar="REMOTIVE_CLOUD_PROJECT"),
|
119
|
-
):
|
121
|
+
) -> None:
|
120
122
|
"""
|
121
123
|
Allows you to subscribe to signals based on a mounted recording without knowing the broker URL.
|
122
124
|
This simplifies when playing recordings from the cloud.
|
@@ -126,61 +128,66 @@ def subscribe(
|
|
126
128
|
if script is None:
|
127
129
|
if len(signal) == 0:
|
128
130
|
ErrorPrinter.print_generic_error("You must use include at least one signal and one namespace or use script when subscribing")
|
129
|
-
exit(1)
|
131
|
+
sys.exit(1)
|
130
132
|
if script is not None:
|
131
133
|
if len(signal) > 0:
|
132
134
|
ErrorPrinter.print_generic_error("You must must not specify --signal when using --script")
|
133
|
-
exit(1)
|
135
|
+
sys.exit(1)
|
134
136
|
|
135
137
|
broker_client = _get_broker_info(project, recording_session, broker, "subscribe")
|
136
138
|
|
137
139
|
try:
|
138
140
|
if script is not None:
|
139
141
|
script_src = read_scripted_code_file(script)
|
140
|
-
broker_client.subscribe_on_script(script_src, lambda sig:
|
142
|
+
broker_client.subscribe_on_script(script_src, lambda sig: rich_print(json.dumps(list(sig))), on_change_only)
|
141
143
|
else:
|
142
144
|
|
143
145
|
def to_subscribable_signal(sig: str):
|
144
146
|
arr = sig.split(":")
|
145
147
|
if len(arr) != 2:
|
146
148
|
ErrorPrinter.print_hint(f"--signal must have format namespace:signal ({sig})")
|
147
|
-
exit(1)
|
149
|
+
sys.exit(1)
|
148
150
|
return SubscribableSignal(namespace=arr[0], name=arr[1])
|
149
151
|
|
150
152
|
signals_to_subscribe_to = list(map(to_subscribable_signal, signal))
|
151
|
-
broker_client.long_name_subscribe(signals_to_subscribe_to, lambda sig:
|
153
|
+
broker_client.long_name_subscribe(signals_to_subscribe_to, lambda sig: rich_print(json.dumps(list(sig))), on_change_only)
|
152
154
|
print("Subscribing to signals, press Ctrl+C to exit")
|
153
155
|
except grpc.RpcError as rpc_error:
|
154
156
|
ErrorPrinter.print_grpc_error(rpc_error)
|
155
157
|
|
156
|
-
except Exception as e:
|
157
|
-
ErrorPrinter.print_generic_error(e)
|
158
|
-
exit(1)
|
158
|
+
except Exception as e: # pylint: disable=W0718
|
159
|
+
ErrorPrinter.print_generic_error(str(e))
|
160
|
+
sys.exit(1)
|
159
161
|
|
160
162
|
|
161
|
-
def _do_change_playback_mode(
|
163
|
+
def _do_change_playback_mode( # noqa: C901
|
162
164
|
mode: str,
|
163
165
|
recording_session: str,
|
164
|
-
|
166
|
+
brokerstr: str,
|
165
167
|
project: str,
|
166
168
|
seconds: int | None = None,
|
167
169
|
progress_on_play: bool = False,
|
168
170
|
repeat: bool = False,
|
169
|
-
): # noqa: C901
|
170
|
-
|
171
|
+
) -> None: # noqa: C901
|
172
|
+
# pylint: disable=R0913,R0912
|
173
|
+
response = Rest.handle_get(f"/api/project/{project}/files/recording/{recording_session}", return_response=True)
|
174
|
+
if response is None:
|
175
|
+
return
|
171
176
|
r = json.loads(response.text)
|
172
|
-
recordings:
|
177
|
+
recordings: List[Any] = r["recordings"]
|
173
178
|
files = list(map(lambda rec: {"recording": rec["fileName"], "namespace": rec["metadata"]["namespace"]}, recordings))
|
174
179
|
|
175
|
-
broker_name =
|
176
|
-
response =
|
180
|
+
broker_name = brokerstr if brokerstr is not None else "personal"
|
181
|
+
response = Rest.handle_get(f"/api/project/{project}/brokers/{broker_name}", return_response=True, allow_status_codes=[404])
|
182
|
+
if response is None:
|
183
|
+
return
|
177
184
|
if response.status_code == 404:
|
178
185
|
broker_arg = ""
|
179
|
-
if
|
180
|
-
broker_arg = f" --broker {
|
186
|
+
if brokerstr is not None:
|
187
|
+
broker_arg = f" --broker {brokerstr} --ensure-broker-started"
|
181
188
|
ErrorPrinter.print_generic_error("You need to mount the recording before you play")
|
182
189
|
ErrorPrinter.print_hint(f"remotive cloud recordings mount {recording_session}{broker_arg} --project {project}")
|
183
|
-
exit(1)
|
190
|
+
sys.exit(1)
|
184
191
|
|
185
192
|
broker_info = json.loads(response.text)
|
186
193
|
broker = Broker(broker_info["url"], None)
|
@@ -194,23 +201,26 @@ def _do_change_playback_mode(
|
|
194
201
|
if progress_on_play or repeat:
|
195
202
|
_track_progress(broker, repeat, files)
|
196
203
|
elif mode == "seek":
|
197
|
-
|
204
|
+
if seconds is not None:
|
205
|
+
broker.seek(files, int(seconds * 1000000), True)
|
206
|
+
else:
|
207
|
+
broker.seek(files, 0, True)
|
198
208
|
elif mode == "stop":
|
199
209
|
broker.seek(files, 0, True)
|
200
210
|
elif mode == "status":
|
201
211
|
_track_progress(broker, repeat, files)
|
202
212
|
else:
|
203
|
-
raise
|
213
|
+
raise ValueError(f"Illegal command {mode}")
|
204
214
|
|
205
215
|
|
206
|
-
def _track_progress(broker: Broker, repeat: bool, files: List):
|
216
|
+
def _track_progress(broker: Broker, repeat: bool, files: List[Any]) -> None:
|
207
217
|
p = Progress(SpinnerColumn(), TextColumn("[progress.description]{task.description}"), transient=True)
|
208
218
|
t = p.add_task("label", total=1)
|
209
219
|
if repeat:
|
210
220
|
rich.print(":point_right: Keep this command running in terminal to keep the recording play with repeat")
|
211
221
|
with p:
|
212
222
|
|
213
|
-
def print_progress(offset: int, total: int, current_mode: str):
|
223
|
+
def print_progress(offset: int, total: int, current_mode: str) -> None:
|
214
224
|
p.update(
|
215
225
|
t,
|
216
226
|
description=f"{(datetime.timedelta(seconds=offset))} " f"/ {(datetime.timedelta(seconds=total))} ({current_mode})",
|
@@ -219,14 +229,14 @@ def _track_progress(broker: Broker, repeat: bool, files: List):
|
|
219
229
|
broker.listen_on_playback(repeat, files, print_progress)
|
220
230
|
|
221
231
|
|
222
|
-
def _verify_recording_on_broker(broker: Broker, recording_session: str, mode: str, project: str):
|
232
|
+
def _verify_recording_on_broker(broker: Broker, recording_session: str, mode: str, project: str) -> None:
|
223
233
|
try:
|
224
234
|
# Here we try to verify that we are operating on a recording that is mounted on the
|
225
235
|
# broker so we can verify this before we try playback and can also present some good
|
226
236
|
# error messages
|
227
237
|
tmp = os.path.join(tempfile.gettempdir(), os.urandom(24).hex())
|
228
238
|
broker.download(".cloud.context", tmp, True)
|
229
|
-
with open(tmp, "r") as f:
|
239
|
+
with open(tmp, "r", encoding="utf8") as f:
|
230
240
|
json_context = json.loads(f.read())
|
231
241
|
if json_context["recordingSessionId"] != recording_session:
|
232
242
|
ErrorPrinter.print_generic_error(
|
@@ -234,29 +244,32 @@ def _verify_recording_on_broker(broker: Broker, recording_session: str, mode: st
|
|
234
244
|
f"which not the same as you are trying to {mode}, use cmd below to mount this recording"
|
235
245
|
)
|
236
246
|
ErrorPrinter.print_hint(f"remotive cloud recordings mount {recording_session} --project {project}")
|
237
|
-
exit(1)
|
247
|
+
sys.exit(1)
|
238
248
|
except grpc.RpcError as rpc_error:
|
239
|
-
if rpc_error.code() == grpc.StatusCode.NOT_FOUND:
|
249
|
+
if rpc_error.code() == grpc.StatusCode.NOT_FOUND: # pylint: disable=no-member
|
240
250
|
ErrorPrinter.print_generic_error(f"You must use mount to prepare a recording before you can use {mode}")
|
241
251
|
ErrorPrinter.print_hint(f"remotive cloud recordings mount {recording_session} --project {project}")
|
242
252
|
else:
|
243
253
|
ErrorPrinter.print_grpc_error(rpc_error)
|
244
|
-
exit(1)
|
254
|
+
sys.exit(1)
|
245
255
|
|
246
256
|
|
247
257
|
def _get_broker_info(project: str, recording_session: str, broker: Union[str, None], mode: str) -> Broker:
|
248
258
|
# Verify it exists
|
249
|
-
|
259
|
+
Rest.handle_get(f"/api/project/{project}/files/recording/{recording_session}", return_response=True)
|
250
260
|
|
251
261
|
broker_name = broker if broker is not None else "personal"
|
252
|
-
response =
|
262
|
+
response = Rest.handle_get(f"/api/project/{project}/brokers/{broker_name}", return_response=True, allow_status_codes=[404])
|
263
|
+
if response is None:
|
264
|
+
ErrorPrinter.print_generic_error(f"No response from: /api/project/{project}/brokers/{broker_name}")
|
265
|
+
sys.exit(1)
|
253
266
|
if response.status_code == 404:
|
254
267
|
broker_arg = ""
|
255
268
|
if broker is not None:
|
256
269
|
broker_arg = f"--broker {broker} --ensure-broker-started"
|
257
270
|
ErrorPrinter.print_generic_error("You need to mount the recording before you play")
|
258
271
|
ErrorPrinter.print_hint(f"remotive cloud recordings mount {recording_session} {broker_arg} --project {project}")
|
259
|
-
exit(1)
|
272
|
+
sys.exit(1)
|
260
273
|
broker_info = json.loads(response.text)
|
261
274
|
broker_client = Broker(broker_info["url"], None)
|
262
275
|
_verify_recording_on_broker(broker_client, recording_session, mode, project)
|