remotivelabs-cli 0.0.25__py3-none-any.whl → 0.0.27__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.
Files changed (43) hide show
  1. cli/broker/brokers.py +2 -2
  2. cli/broker/export.py +5 -5
  3. cli/broker/files.py +5 -5
  4. cli/broker/lib/broker.py +82 -51
  5. cli/broker/license_flows.py +11 -9
  6. cli/broker/licenses.py +2 -2
  7. cli/broker/playback.py +14 -34
  8. cli/broker/record.py +3 -3
  9. cli/broker/scripting.py +4 -4
  10. cli/broker/signals.py +20 -12
  11. cli/cloud/__init__.py +0 -1
  12. cli/cloud/auth.py +40 -35
  13. cli/cloud/auth_tokens.py +73 -37
  14. cli/cloud/brokers.py +24 -33
  15. cli/cloud/cloud_cli.py +7 -18
  16. cli/cloud/configs.py +28 -11
  17. cli/cloud/filestorage.py +63 -51
  18. cli/cloud/organisations.py +30 -0
  19. cli/cloud/projects.py +11 -8
  20. cli/cloud/recordings.py +148 -117
  21. cli/cloud/recordings_playback.py +52 -39
  22. cli/cloud/rest_helper.py +247 -196
  23. cli/cloud/resumable_upload.py +9 -8
  24. cli/cloud/sample_recordings.py +5 -5
  25. cli/cloud/service_account_tokens.py +18 -16
  26. cli/cloud/service_accounts.py +9 -9
  27. cli/connect/__init__.py +0 -1
  28. cli/connect/connect.py +7 -6
  29. cli/connect/protopie/protopie.py +32 -16
  30. cli/errors.py +6 -5
  31. cli/remotive.py +13 -9
  32. cli/requirements.txt +4 -1
  33. cli/settings.py +9 -9
  34. cli/tools/__init__.py +0 -1
  35. cli/tools/can/__init__.py +0 -1
  36. cli/tools/can/can.py +8 -8
  37. cli/tools/tools.py +2 -2
  38. {remotivelabs_cli-0.0.25.dist-info → remotivelabs_cli-0.0.27.dist-info}/METADATA +5 -3
  39. remotivelabs_cli-0.0.27.dist-info/RECORD +45 -0
  40. remotivelabs_cli-0.0.25.dist-info/RECORD +0 -44
  41. {remotivelabs_cli-0.0.25.dist-info → remotivelabs_cli-0.0.27.dist-info}/LICENSE +0 -0
  42. {remotivelabs_cli-0.0.25.dist-info → remotivelabs_cli-0.0.27.dist-info}/WHEEL +0 -0
  43. {remotivelabs_cli-0.0.25.dist-info → remotivelabs_cli-0.0.27.dist-info}/entry_points.txt +0 -0
cli/broker/playback.py CHANGED
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import List
3
+ from typing import Dict, List
4
4
 
5
5
  import grpc
6
6
  import typer
@@ -9,7 +9,15 @@ from cli.errors import ErrorPrinter
9
9
 
10
10
  from .lib.broker import Broker
11
11
 
12
- app = typer.Typer(help=help)
12
+ app = typer.Typer(help=help) # type: ignore
13
+
14
+
15
+ def recording_and_namespace(recording: str) -> Dict[str, str]:
16
+ splitted = recording.split("::")
17
+ if len(splitted) != 2:
18
+ print("Invalid --recording option, expected file_name::namespace")
19
+ raise typer.Exit(1)
20
+ return {"recording": splitted[0], "namespace": splitted[1]}
13
21
 
14
22
 
15
23
  @app.command()
@@ -17,7 +25,7 @@ def play(
17
25
  recording: List[str] = typer.Option(..., help="Which recording and which namespace to play"),
18
26
  url: str = typer.Option(..., help="Broker URL", envvar="REMOTIVE_BROKER_URL"),
19
27
  api_key: str = typer.Option("offline", help="Cloud Broker API-KEY or access token", envvar="REMOTIVE_BROKER_API_KEY"),
20
- ):
28
+ ) -> None:
21
29
  """
22
30
  Play recording files on broker.
23
31
 
@@ -26,13 +34,6 @@ def play(
26
34
  remotive broker playback play --recording myrecording_can0::can0 --recording myrecording_can1::can1
27
35
  """
28
36
 
29
- def recording_and_namespace(recording: str):
30
- splitted = recording.split("::")
31
- if len(splitted) != 2:
32
- print("Invalid --recording option, expected file_name::namespace")
33
- raise typer.Exit(1)
34
- return {"recording": splitted[0], "namespace": splitted[1]}
35
-
36
37
  rec = list(map(recording_and_namespace, recording))
37
38
  try:
38
39
  broker = Broker(url, api_key)
@@ -48,7 +49,7 @@ def stop(
48
49
  recording: List[str] = typer.Option(..., help="Which recording and which namespace to stop"),
49
50
  url: str = typer.Option(..., help="Broker URL", envvar="REMOTIVE_BROKER_URL"),
50
51
  api_key: str = typer.Option("offline", help="Cloud Broker API-KEY or access token", envvar="REMOTIVE_BROKER_API_KEY"),
51
- ):
52
+ ) -> None:
52
53
  """
53
54
  Stop recordings that are beeing played on brokers are done with the same syntax as when you start them.
54
55
 
@@ -57,13 +58,6 @@ def stop(
57
58
  remotive broker playback stop --recording myrecording_can0::can0 --recording myrecording_can1::can1
58
59
  """
59
60
 
60
- def recording_and_namespace(recording: str):
61
- splitted = recording.split("::")
62
- if len(splitted) != 2:
63
- print("Invalid --recording option, expected file_name::namespace")
64
- raise typer.Exit(1)
65
- return {"recording": splitted[0], "namespace": splitted[1]}
66
-
67
61
  rec = list(map(recording_and_namespace, recording))
68
62
 
69
63
  try:
@@ -79,7 +73,7 @@ def pause(
79
73
  recording: List[str] = typer.Option(..., help="Which recording and which namespace to stop"),
80
74
  url: str = typer.Option(..., help="Broker URL", envvar="REMOTIVE_BROKER_URL"),
81
75
  api_key: str = typer.Option("offline", help="Cloud Broker API-KEY or access token", envvar="REMOTIVE_BROKER_API_KEY"),
82
- ):
76
+ ) -> None:
83
77
  """
84
78
  Pause recordings that are beeing played on brokers are done with the same syntax as when you start them.
85
79
 
@@ -88,13 +82,6 @@ def pause(
88
82
  remotive broker playback pause --recording myrecording_can0::can0 --recording myrecording_can1::can1
89
83
  """
90
84
 
91
- def recording_and_namespace(recording: str):
92
- splitted = recording.split("::")
93
- if len(splitted) != 2:
94
- print("Invalid --recording option, expected file_name::namespace")
95
- raise typer.Exit(1)
96
- return {"recording": splitted[0], "namespace": splitted[1]}
97
-
98
85
  rec = list(map(recording_and_namespace, recording))
99
86
  try:
100
87
  broker = Broker(url, api_key)
@@ -110,7 +97,7 @@ def seek(
110
97
  seconds: float = typer.Option(..., help="Target offset in seconds"),
111
98
  url: str = typer.Option(..., help="Broker URL", envvar="REMOTIVE_BROKER_URL"),
112
99
  api_key: str = typer.Option("offline", help="Cloud Broker API-KEY or access token", envvar="REMOTIVE_BROKER_API_KEY"),
113
- ):
100
+ ) -> None:
114
101
  """
115
102
  Seeks to a position in seconds into the recording
116
103
 
@@ -121,13 +108,6 @@ def seek(
121
108
 
122
109
  broker = Broker(url, api_key)
123
110
 
124
- def recording_and_namespace(recording: str):
125
- splitted = recording.split("::")
126
- if len(splitted) != 2:
127
- print("Invalid --recording option, expected file_name::namespace")
128
- raise typer.Exit(1)
129
- return {"recording": splitted[0], "namespace": splitted[1]}
130
-
131
111
  rec = list(map(recording_and_namespace, recording))
132
112
 
133
113
  try:
cli/broker/record.py CHANGED
@@ -9,7 +9,7 @@ from cli.errors import ErrorPrinter
9
9
 
10
10
  from .lib.broker import Broker
11
11
 
12
- app = typer.Typer(help=help)
12
+ app = typer.Typer(help=help) # type: ignore
13
13
 
14
14
 
15
15
  @app.command()
@@ -18,7 +18,7 @@ def start(
18
18
  namespace: List[str] = typer.Option(..., help="Namespace to record"),
19
19
  url: str = typer.Option(..., help="Broker URL", envvar="REMOTIVE_BROKER_URL"),
20
20
  api_key: str = typer.Option("offline", help="Cloud Broker API-KEY or access token", envvar="REMOTIVE_BROKER_API_KEY"),
21
- ):
21
+ ) -> None:
22
22
  try:
23
23
  broker = Broker(url, api_key)
24
24
  broker.record_multiple(namespace, filename)
@@ -32,7 +32,7 @@ def stop(
32
32
  namespace: List[str] = typer.Option(..., help="Namespace to record"),
33
33
  url: str = typer.Option(..., help="Broker URL", envvar="REMOTIVE_BROKER_URL"),
34
34
  api_key: str = typer.Option("offline", help="Cloud Broker API-KEY or access token", envvar="REMOTIVE_BROKER_API_KEY"),
35
- ):
35
+ ) -> None:
36
36
  try:
37
37
  broker = Broker(url, api_key)
38
38
  broker.stop_multiple(namespace, filename)
cli/broker/scripting.py CHANGED
@@ -16,7 +16,7 @@ app = typer.Typer(
16
16
  )
17
17
 
18
18
 
19
- def write(signal_name, script):
19
+ def write(signal_name: str, script: str) -> None:
20
20
  path = f"{signal_name}.lua"
21
21
  f = open(path, "w")
22
22
  f.write(script)
@@ -29,7 +29,7 @@ def new_script(
29
29
  input_signal: List[str] = typer.Option(..., help="Required input signal names"),
30
30
  output_signal: str = typer.Option(..., help="Name of output signal"),
31
31
  save: bool = typer.Option(False, help="Save file to disk - Default stored as __output_signal__.lua"),
32
- ):
32
+ ) -> None:
33
33
  def to_subscribable_signal(sig: str) -> tuple[str, str]:
34
34
  arr = sig.split(":")
35
35
  if len(arr) != 2:
@@ -39,7 +39,7 @@ def new_script(
39
39
 
40
40
  signals_to_subscribe_to = list(map(to_subscribable_signal, input_signal))
41
41
 
42
- def to_local_signal(sig_name: tuple[str, str]):
42
+ def to_local_signal(sig_name: tuple[str, str]) -> str:
43
43
  t = Template(
44
44
  """
45
45
  {
@@ -51,7 +51,7 @@ def new_script(
51
51
 
52
52
  local_signals = ",".join(list(map(to_local_signal, signals_to_subscribe_to)))
53
53
 
54
- def to_subscribe_pattern(sig_name: tuple[str, str]):
54
+ def to_subscribe_pattern(sig_name: tuple[str, str]) -> str:
55
55
  t = Template(
56
56
  """
57
57
  if (signals["$sig_name"] ~= nil) then
cli/broker/signals.py CHANGED
@@ -5,10 +5,10 @@ import numbers
5
5
  import os
6
6
  import signal as os_signal
7
7
  from pathlib import Path
8
- from typing import List, TypedDict
8
+ from typing import Any, Dict, Iterable, List, TypedDict
9
9
 
10
10
  import grpc
11
- import plotext as plt
11
+ import plotext as plt # type: ignore
12
12
  import typer
13
13
  from rich import print as rich_rprint
14
14
 
@@ -16,7 +16,7 @@ from cli.errors import ErrorPrinter
16
16
 
17
17
  from .lib.broker import Broker, SubscribableSignal
18
18
 
19
- app = typer.Typer(help=help)
19
+ app = typer.Typer(help=help) # type: ignore
20
20
 
21
21
 
22
22
  # signal_values:list = list()
@@ -24,17 +24,25 @@ app = typer.Typer(help=help)
24
24
 
25
25
  class Signals(TypedDict):
26
26
  name: str
27
- signals: List
27
+ signals: List[Any]
28
28
 
29
29
 
30
- signal_values = {}
30
+ signal_values: Dict[Any, Any] = {}
31
31
 
32
32
 
33
- @app.command(help="List signals names on broker")
33
+ @app.command(name="list", help="List frame and signal metadata on broker")
34
+ def list_signals(
35
+ url: str = typer.Option(..., help="Broker URL", envvar="REMOTIVE_BROKER_URL"),
36
+ api_key: str = typer.Option(None, help="Cloud Broker API-KEY or access token", envvar="REMOTIVE_BROKER_API_KEY"),
37
+ ) -> None:
38
+ signal_names(url, api_key)
39
+
40
+
41
+ @app.command(help="List signals names on broker", deprecated=True)
34
42
  def signal_names(
35
43
  url: str = typer.Option(..., help="Broker URL", envvar="REMOTIVE_BROKER_URL"),
36
44
  api_key: str = typer.Option(None, help="Cloud Broker API-KEY or access token", envvar="REMOTIVE_BROKER_API_KEY"),
37
- ):
45
+ ) -> None:
38
46
  try:
39
47
  broker = Broker(url, api_key)
40
48
  # print("Listing available signals")
@@ -73,7 +81,7 @@ def subscribe( # noqa: C901
73
81
  x_plot: bool = typer.Option(default=False, help="Experimental: Plot the signal in terminal. Note graphs are not " "aligned by time"),
74
82
  x_plot_size: int = typer.Option(default=100, help="Experimental: how many points show for each plot"),
75
83
  # samples: int = typer.Option(default=0, he)
76
- ):
84
+ ) -> None:
77
85
  """
78
86
  Subscribe to a selection of signals
79
87
 
@@ -100,10 +108,10 @@ def subscribe( # noqa: C901
100
108
 
101
109
  plt.title("Signals")
102
110
 
103
- def exit_on_ctrlc(sig, frame):
111
+ def exit_on_ctrlc(sig: Any, frame: Any) -> None:
104
112
  os._exit(0)
105
113
 
106
- def on_frame_plot(x):
114
+ def on_frame_plot(x: Iterable[Any]) -> None:
107
115
  global signal_values
108
116
 
109
117
  plt.clt() # to clear the terminal
@@ -144,7 +152,7 @@ def subscribe( # noqa: C901
144
152
  plt.sleep(0.001) # to add
145
153
  plt.show()
146
154
 
147
- def on_frame_print(x):
155
+ def on_frame_print(x: Iterable[Any]) -> None:
148
156
  rich_rprint(json.dumps(list(x)))
149
157
 
150
158
  os_signal.signal(os_signal.SIGINT, exit_on_ctrlc)
@@ -181,7 +189,7 @@ def subscribe( # noqa: C901
181
189
  def namespaces(
182
190
  url: str = typer.Option(..., help="Broker URL", envvar="REMOTIVE_BROKER_URL"),
183
191
  api_key: str = typer.Option(None, help="Cloud Broker API-KEY or access token", envvar="REMOTIVE_BROKER_API_KEY"),
184
- ):
192
+ ) -> None:
185
193
  try:
186
194
  broker = Broker(url, api_key)
187
195
  namespaces_json = broker.list_namespaces()
cli/cloud/__init__.py CHANGED
@@ -1 +0,0 @@
1
-
cli/cloud/auth.py CHANGED
@@ -4,29 +4,33 @@ import webbrowser
4
4
  from http.server import BaseHTTPRequestHandler, HTTPServer
5
5
  from pathlib import Path
6
6
  from threading import Thread
7
+ from typing import Any
7
8
 
8
9
  import typer
10
+ from typing_extensions import override
9
11
 
10
12
  from cli import settings
11
13
 
12
14
  from . import auth_tokens
13
- from . import rest_helper as rest
15
+ from .rest_helper import RestHelper as Rest
14
16
 
15
- apa = settings.config_dir_name
17
+ APA = settings.CONFIG_DIR_NAME
16
18
 
17
- help = """
19
+ HELP = """
18
20
  Manage how you authenticate with our cloud platform
19
21
  """
20
22
 
21
- app = typer.Typer(help=help)
23
+ httpd: HTTPServer
24
+
25
+ app = typer.Typer(help=HELP)
22
26
 
23
27
  app.add_typer(auth_tokens.app, name="tokens", help="Manage users personal access tokens")
24
- config_dir_name = settings.config_dir_name # str(Path.home()) + "/.config/.remotive/"
25
- token_file_name = settings.token_file_name # str(Path.home()) + "/.config/.remotive/cloud.secret.token"
28
+ CONFIG_DIR_NAME = settings.CONFIG_DIR_NAME # str(Path.home()) + "/.config/.remotive/"
29
+ TOKEN_FILE_NAME = settings.TOKEN_FILE_NAME # str(Path.home()) + "/.config/.remotive/cloud.secret.token"
26
30
 
27
31
 
28
32
  class S(BaseHTTPRequestHandler):
29
- def _set_response(self):
33
+ def _set_response(self) -> None:
30
34
  self.send_response(200)
31
35
  # self.send_response(301)
32
36
  # self.send_header('Location', 'https://cloud.remotivelabs.com')
@@ -34,10 +38,13 @@ class S(BaseHTTPRequestHandler):
34
38
  self.send_header("Content-type", "text/html")
35
39
  self.end_headers()
36
40
 
37
- def log_message(self, format, *args):
41
+ def log_message(self, format: Any, *args: Any) -> None: # pylint: disable=W0622
38
42
  return
39
43
 
40
- def do_GET(self): # noqa
44
+ # Please do not change this into lowercase!
45
+ @override
46
+ # type: ignore
47
+ def do_GET(self): # pylint: disable=invalid-name,
41
48
  self._set_response()
42
49
  self.wfile.write("Successfully setup CLI, return to your terminal to continue".encode("utf-8"))
43
50
  path = self.path
@@ -47,15 +54,15 @@ class S(BaseHTTPRequestHandler):
47
54
  killerthread = Thread(target=httpd.shutdown)
48
55
  killerthread.start()
49
56
 
50
- if not os.path.exists(config_dir_name):
51
- os.makedirs(config_dir_name)
57
+ if not os.path.exists(CONFIG_DIR_NAME):
58
+ os.makedirs(CONFIG_DIR_NAME)
52
59
  write_token(path[1:])
53
60
  print("Successfully logged on, you are ready to go with cli")
54
61
 
55
62
 
56
- def start_local_webserver(server_class=HTTPServer, handler_class=S, port=0):
63
+ def start_local_webserver(server_class: type = HTTPServer, handler_class: type = S, port: int = 0) -> None:
57
64
  server_address = ("", port)
58
- global httpd
65
+ global httpd # pylint: disable=W0603
59
66
  httpd = server_class(server_address, handler_class)
60
67
 
61
68
 
@@ -65,7 +72,7 @@ def start_local_webserver(server_class=HTTPServer, handler_class=S, port=0):
65
72
 
66
73
 
67
74
  @app.command(name="login")
68
- def login():
75
+ def login() -> None:
69
76
  """
70
77
  Login to the cli using browser
71
78
 
@@ -73,20 +80,21 @@ def login():
73
80
  be the same as activating a personal access key or service-account access key.
74
81
  """
75
82
  start_local_webserver()
76
- webbrowser.open(f"{rest.base_url}/login?redirectUrl=http://localhost:{httpd.server_address[1]}", new=1, autoraise=True)
83
+ webbrowser.open(f"{Rest.get_base_url()}/login?redirectUrl=http://localhost:{httpd.server_address[1]}", new=1, autoraise=True)
84
+
77
85
  httpd.serve_forever()
78
86
 
79
87
 
80
88
  @app.command()
81
- def whoami():
89
+ def whoami() -> None:
82
90
  """
83
91
  Validates authentication and fetches your user information
84
92
  """
85
- rest.handle_get("/api/whoami")
93
+ Rest.handle_get("/api/whoami")
86
94
 
87
95
 
88
96
  @app.command()
89
- def print_access_token():
97
+ def print_access_token() -> None:
90
98
  """
91
99
  Print current active access token
92
100
  """
@@ -94,36 +102,33 @@ def print_access_token():
94
102
 
95
103
 
96
104
  @app.command(help="Clear access token")
97
- def logout():
98
- os.remove(settings.token_file_name)
105
+ def logout() -> None:
106
+ os.remove(settings.TOKEN_FILE_NAME)
99
107
  print("Access token removed")
100
108
 
101
109
 
102
- def read_token():
110
+ def read_token() -> str:
103
111
  # f = open(token_file_name, "r")
104
112
  # token = f.read()
105
113
  # f.close()
106
114
  return settings.read_token()
107
115
 
108
116
 
109
- def read_file_with_path(file):
110
- f = open(file, "r")
111
- token = f.read()
112
- f.close()
113
- return token
117
+ def read_file_with_path(file: str) -> str:
118
+ with open(file, "r", encoding="utf8") as f:
119
+ token = f.read()
120
+ return token
114
121
 
115
122
 
116
- def read_file(file):
117
- f = open(str(Path.home()) + f"/.config/.remotive/{file}", "r")
118
- token = f.read()
119
- f.close()
120
- return token
123
+ def read_file(file: str) -> str:
124
+ with open(str(Path.home()) + f"/.config/.remotive/{file}", "r", encoding="utf8") as f:
125
+ token = f.read()
126
+ return token
121
127
 
122
128
 
123
- def write_token(token):
124
- f = open(token_file_name, "w")
125
- f.write(token)
126
- f.close()
129
+ def write_token(token: str) -> None:
130
+ with open(TOKEN_FILE_NAME, "w", encoding="utf8") as f:
131
+ f.write(token)
127
132
 
128
133
 
129
134
  # Key stuff
cli/cloud/auth_tokens.py CHANGED
@@ -1,22 +1,26 @@
1
1
  import json
2
2
  import os
3
3
  import sys
4
+ from json.decoder import JSONDecodeError
4
5
  from pathlib import Path
5
6
 
6
7
  import typer
7
8
 
8
- from . import rest_helper as rest
9
+ from .rest_helper import RestHelper as Rest
9
10
 
10
11
  app = typer.Typer()
11
12
 
12
- token_file_name = str(Path.home()) + "/.config/.remotive/cloud.secret.token"
13
- config_dir_name = str(Path.home()) + "/.config/.remotive/"
13
+ TOKEN_FILE_NAME = str(Path.home()) + "/.config/.remotive/cloud.secret.token"
14
+ CONFIG_DIR_NAME = str(Path.home()) + "/.config/.remotive/"
14
15
 
15
16
 
16
17
  @app.command(name="create", help="Create and download a new personal access token")
17
- def get_personal_access_token(activate: bool = typer.Option(False, help="Activate the token for use after download")):
18
- rest.ensure_auth_token()
19
- response = rest.handle_post(url="/api/me/keys", return_response=True)
18
+ def get_personal_access_token(activate: bool = typer.Option(False, help="Activate the token for use after download")) -> None: # pylint: disable=W0621
19
+ Rest.ensure_auth_token()
20
+ response = Rest.handle_post(url="/api/me/keys", return_response=True)
21
+
22
+ if response is None:
23
+ return
20
24
 
21
25
  if response.status_code == 200:
22
26
  name = response.json()["name"]
@@ -34,19 +38,42 @@ def get_personal_access_token(activate: bool = typer.Option(False, help="Activat
34
38
 
35
39
 
36
40
  @app.command(name="list", help="List personal access tokens")
37
- def list_personal_access_tokens():
38
- rest.ensure_auth_token()
39
- rest.handle_get("/api/me/keys")
41
+ def list_personal_access_tokens() -> None:
42
+ Rest.ensure_auth_token()
43
+ Rest.handle_get("/api/me/keys")
44
+
40
45
 
46
+ @app.command(name="revoke")
47
+ def revoke(name_or_file: str = typer.Argument(help="Name or file path of the access token to revoke")) -> None:
48
+ """
49
+ Revoke an access token by token name or path to a file containing that token
41
50
 
42
- @app.command(name="revoke", help="Revoke the specified access token")
43
- def revoke(name: str = typer.Option(..., help="Name of the access token to revoke")):
44
- rest.ensure_auth_token()
45
- rest.handle_delete(f"/api/me/keys/{name}", success_msg="Successfully revoked")
51
+ Name is found in the json file
52
+
53
+ {
54
+ "expires": "2034-07-31",
55
+ "token": "xxx",
56
+ "created": "2024-07-31T09:18:50.406+02:00",
57
+ "name": "token_name"
58
+ }
59
+ """
60
+ name = name_or_file
61
+ if "." in name_or_file:
62
+ json_str = read_file(name_or_file)
63
+ try:
64
+ name = json.loads(json_str)["name"]
65
+ except JSONDecodeError:
66
+ sys.stderr.write("Failed to parse json, make sure its a correct access token file\n")
67
+ sys.exit(1)
68
+ except KeyError:
69
+ sys.stderr.write("Json does not contain a name property, make sure its a correct access token file\n")
70
+ sys.exit(1)
71
+ Rest.ensure_auth_token()
72
+ Rest.handle_delete(f"/api/me/keys/{name}")
46
73
 
47
74
 
48
75
  @app.command()
49
- def describe(file: str = typer.Option(..., help="File name")):
76
+ def describe(file: str = typer.Argument(help="File name")) -> None:
50
77
  """
51
78
  Show contents of specified access token file
52
79
  """
@@ -54,7 +81,7 @@ def describe(file: str = typer.Option(..., help="File name")):
54
81
 
55
82
 
56
83
  @app.command()
57
- def activate(file: str = typer.Argument(..., help="File name")):
84
+ def activate(file: str = typer.Argument(..., help="File name")) -> None:
58
85
  """
59
86
  Activate a access token file to be used for authentication.
60
87
 
@@ -66,7 +93,7 @@ def activate(file: str = typer.Argument(..., help="File name")):
66
93
  do_activate(file)
67
94
 
68
95
 
69
- def do_activate(file: str):
96
+ def do_activate(file: str) -> None:
70
97
  # Best effort to read file
71
98
  if os.path.exists(file):
72
99
  token_file = json.loads(read_file_with_path(file))
@@ -79,38 +106,47 @@ def do_activate(file: str):
79
106
 
80
107
 
81
108
  @app.command(name="list-files")
82
- def list_files():
109
+ def list_files() -> None:
83
110
  """
84
111
  List personal access token files in remotivelabs config directory
85
112
  """
86
- personal_files = filter(lambda f: f.startswith("personal"), os.listdir(config_dir_name))
113
+ personal_files = filter(lambda f: f.startswith("personal"), os.listdir(CONFIG_DIR_NAME))
87
114
  for file in personal_files:
88
115
  print(file)
89
116
 
90
117
 
91
- def read_file(file):
92
- f = open(str(Path.home()) + f"/.config/.remotive/{file}", "r")
93
- token = f.read()
94
- f.close()
95
- return token
118
+ def read_file(file: str) -> str:
119
+ """
120
+ Reads a file using file path or if that does not exist check under ~/.config/.remotive
121
+ """
122
+ path = file
123
+ if not Path(file).exists():
124
+ path = str(Path.home()) + f"/.config/.remotive/{file}"
125
+ if not Path(path).exists():
126
+ sys.stderr.write(f"Failed to find file using {file} or {path}\n")
127
+ sys.exit(1)
128
+ with open(path, "r", encoding="utf8") as f:
129
+ token = f.read()
130
+ f.close()
131
+ return token
96
132
 
97
133
 
98
- def read_file_with_path(file):
99
- f = open(file, "r")
100
- token = f.read()
101
- f.close()
102
- return token
134
+ def read_file_with_path(file: str) -> str:
135
+ with open(file, "r", encoding="utf8") as f:
136
+ token = f.read()
137
+ f.close()
138
+ return token
103
139
 
104
140
 
105
- def write_token(token):
106
- f = open(token_file_name, "w")
107
- f.write(token)
108
- f.close()
141
+ def write_token(token: str) -> None:
142
+ with open(TOKEN_FILE_NAME, "w", encoding="utf8") as f:
143
+ f.write(token)
144
+ f.close()
109
145
 
110
146
 
111
- def write_personal_token(file: str, token: str):
147
+ def write_personal_token(file: str, token: str) -> str:
112
148
  path = str(Path.home()) + f"/.config/.remotive/{file}"
113
- f = open(path, "w")
114
- f.write(token)
115
- f.close()
116
- return path
149
+ with open(path, "w", encoding="utf8") as f:
150
+ f.write(token)
151
+ f.close()
152
+ return path