kscale 0.3.6__py3-none-any.whl → 0.3.8__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.
- kscale/__init__.py +1 -1
- kscale/cli.py +0 -2
- kscale/requirements.txt +0 -3
- kscale/web/cli/robot_class.py +39 -25
- kscale/web/cli/user.py +1 -2
- kscale/web/clients/base.py +13 -2
- {kscale-0.3.6.dist-info → kscale-0.3.8.dist-info}/METADATA +1 -2
- {kscale-0.3.6.dist-info → kscale-0.3.8.dist-info}/RECORD +12 -13
- kscale/web/cli/token.py +0 -33
- {kscale-0.3.6.dist-info → kscale-0.3.8.dist-info}/LICENSE +0 -0
- {kscale-0.3.6.dist-info → kscale-0.3.8.dist-info}/WHEEL +0 -0
- {kscale-0.3.6.dist-info → kscale-0.3.8.dist-info}/entry_points.txt +0 -0
- {kscale-0.3.6.dist-info → kscale-0.3.8.dist-info}/top_level.txt +0 -0
kscale/__init__.py
CHANGED
kscale/cli.py
CHANGED
@@ -8,7 +8,6 @@ import colorlogging
|
|
8
8
|
from kscale.utils.cli import recursive_help
|
9
9
|
from kscale.web.cli.robot import cli as robot_cli
|
10
10
|
from kscale.web.cli.robot_class import cli as robot_class_cli
|
11
|
-
from kscale.web.cli.token import cli as token_cli
|
12
11
|
from kscale.web.cli.user import cli as user_cli
|
13
12
|
|
14
13
|
|
@@ -22,7 +21,6 @@ def cli() -> None:
|
|
22
21
|
logging.getLogger("aiohttp.access").setLevel(logging.WARNING)
|
23
22
|
|
24
23
|
|
25
|
-
cli.add_command(token_cli, "token")
|
26
24
|
cli.add_command(user_cli, "user")
|
27
25
|
cli.add_command(robot_class_cli, "robots")
|
28
26
|
cli.add_command(robot_cli, "robot")
|
kscale/requirements.txt
CHANGED
kscale/web/cli/robot_class.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
"""Defines the CLI for getting information about robot classes."""
|
2
2
|
|
3
|
+
import itertools
|
3
4
|
import json
|
4
5
|
import logging
|
5
6
|
import math
|
@@ -80,7 +81,23 @@ async def update(current_name: str, name: str | None = None, description: str |
|
|
80
81
|
click.echo(f" Description: {click.style(robot_class.description or 'N/A', fg='yellow')}")
|
81
82
|
|
82
83
|
|
83
|
-
@cli.command()
|
84
|
+
@cli.command("delete")
|
85
|
+
@click.argument("name")
|
86
|
+
@coro
|
87
|
+
async def delete_robot_class(name: str) -> None:
|
88
|
+
"""Deletes a robot class."""
|
89
|
+
async with RobotClassClient() as client:
|
90
|
+
await client.delete_robot_class(name)
|
91
|
+
click.echo(f"Robot class deleted: {click.style(name, fg='red')}")
|
92
|
+
|
93
|
+
|
94
|
+
@cli.group()
|
95
|
+
def metadata() -> None:
|
96
|
+
"""Handle the robot class metadata."""
|
97
|
+
pass
|
98
|
+
|
99
|
+
|
100
|
+
@metadata.command("update")
|
84
101
|
@click.argument("name")
|
85
102
|
@click.argument("json_path", type=click.Path(exists=True))
|
86
103
|
@coro
|
@@ -96,7 +113,7 @@ async def update_metadata(name: str, json_path: str) -> None:
|
|
96
113
|
click.echo(f" Name: {click.style(robot_class.class_name, fg='green')}")
|
97
114
|
|
98
115
|
|
99
|
-
@
|
116
|
+
@metadata.command("get")
|
100
117
|
@click.argument("name")
|
101
118
|
@click.option("--json-path", type=click.Path(exists=False))
|
102
119
|
@coro
|
@@ -115,27 +132,17 @@ async def get_metadata(name: str, json_path: str | None = None) -> None:
|
|
115
132
|
json.dump(metadata.model_dump(), f)
|
116
133
|
|
117
134
|
|
118
|
-
@cli.command()
|
119
|
-
@click.argument("name")
|
120
|
-
@coro
|
121
|
-
async def delete(name: str) -> None:
|
122
|
-
"""Deletes a robot class."""
|
123
|
-
async with RobotClassClient() as client:
|
124
|
-
await client.delete_robot_class(name)
|
125
|
-
click.echo(f"Robot class deleted: {click.style(name, fg='red')}")
|
126
|
-
|
127
|
-
|
128
135
|
@cli.group()
|
129
136
|
def urdf() -> None:
|
130
137
|
"""Handle the robot class URDF."""
|
131
138
|
pass
|
132
139
|
|
133
140
|
|
134
|
-
@urdf.command()
|
141
|
+
@urdf.command("upload")
|
135
142
|
@click.argument("class_name")
|
136
143
|
@click.argument("urdf_file")
|
137
144
|
@coro
|
138
|
-
async def
|
145
|
+
async def upload_urdf(class_name: str, urdf_file: str) -> None:
|
139
146
|
"""Uploads a URDF file to a robot class."""
|
140
147
|
async with RobotClassClient() as client:
|
141
148
|
response = await client.upload_robot_class_urdf(class_name, urdf_file)
|
@@ -143,18 +150,18 @@ async def upload(class_name: str, urdf_file: str) -> None:
|
|
143
150
|
click.echo(f" Filename: {click.style(response.filename, fg='green')}")
|
144
151
|
|
145
152
|
|
146
|
-
@urdf.command()
|
153
|
+
@urdf.command("download")
|
147
154
|
@click.argument("class_name")
|
148
155
|
@click.option("--cache", is_flag=True, default=False)
|
149
156
|
@coro
|
150
|
-
async def
|
157
|
+
async def download_urdf(class_name: str, cache: bool) -> None:
|
151
158
|
"""Downloads a URDF file from a robot class."""
|
152
159
|
async with RobotClassClient() as client:
|
153
160
|
urdf_file = await client.download_and_extract_urdf(class_name, cache=cache)
|
154
161
|
click.echo(f"URDF downloaded: {click.style(urdf_file, fg='green')}")
|
155
162
|
|
156
163
|
|
157
|
-
@urdf.command()
|
164
|
+
@urdf.command("pybullet")
|
158
165
|
@click.argument("class_name")
|
159
166
|
@click.option("--no-cache", is_flag=True, default=False)
|
160
167
|
@click.option("--hide-gui", is_flag=True, default=False)
|
@@ -168,7 +175,7 @@ async def download(class_name: str, cache: bool) -> None:
|
|
168
175
|
@click.option("--start-height", type=float, default=0.0)
|
169
176
|
@click.option("--cycle-duration", type=float, default=2.0)
|
170
177
|
@coro
|
171
|
-
async def
|
178
|
+
async def run_pybullet(
|
172
179
|
class_name: str,
|
173
180
|
no_cache: bool,
|
174
181
|
hide_gui: bool,
|
@@ -432,11 +439,11 @@ async def pybullet(
|
|
432
439
|
last_time = cur_time
|
433
440
|
|
434
441
|
|
435
|
-
@urdf.command()
|
442
|
+
@urdf.command("mujoco")
|
436
443
|
@click.argument("class_name")
|
437
444
|
@click.option("--no-cache", is_flag=True, default=False)
|
438
445
|
@coro
|
439
|
-
async def
|
446
|
+
async def run_mujoco(class_name: str, no_cache: bool) -> None:
|
440
447
|
"""Shows the URDF file for a robot class in Mujoco.
|
441
448
|
|
442
449
|
This command downloads and extracts the robot class URDF folder,
|
@@ -444,11 +451,11 @@ async def mujoco(class_name: str, no_cache: bool) -> None:
|
|
444
451
|
launches the Mujoco viewer using the provided MJCF file.
|
445
452
|
"""
|
446
453
|
try:
|
447
|
-
|
454
|
+
import mujoco.viewer
|
448
455
|
except ImportError:
|
449
456
|
click.echo(
|
450
457
|
click.style(
|
451
|
-
"Mujoco and mujoco-python-viewer are required; install with `pip install mujoco
|
458
|
+
"Mujoco and mujoco-python-viewer are required; install with `pip install mujoco`",
|
452
459
|
fg="red",
|
453
460
|
)
|
454
461
|
)
|
@@ -458,13 +465,20 @@ async def mujoco(class_name: str, no_cache: bool) -> None:
|
|
458
465
|
extracted_folder = await client.download_and_extract_urdf(class_name, cache=not no_cache)
|
459
466
|
|
460
467
|
try:
|
461
|
-
mjcf_file = next(
|
468
|
+
mjcf_file = next(
|
469
|
+
itertools.chain(
|
470
|
+
extracted_folder.glob("*.scene.mjcf"),
|
471
|
+
extracted_folder.glob("*.mjcf"),
|
472
|
+
extracted_folder.glob("*.xml"),
|
473
|
+
)
|
474
|
+
)
|
462
475
|
except StopIteration:
|
463
476
|
click.echo(click.style(f"No MJCF file found in {extracted_folder}", fg="red"))
|
464
477
|
return
|
465
478
|
|
466
|
-
|
467
|
-
|
479
|
+
mjcf_path_str = str(mjcf_file.resolve())
|
480
|
+
click.echo(f"Launching Mujoco viewer with: {click.style(mjcf_path_str, fg='green')}")
|
481
|
+
mujoco.viewer.launch_from_path(mjcf_path_str)
|
468
482
|
|
469
483
|
|
470
484
|
if __name__ == "__main__":
|
kscale/web/cli/user.py
CHANGED
@@ -45,8 +45,7 @@ async def key() -> None:
|
|
45
45
|
"""Get an API key for the currently-authenticated user."""
|
46
46
|
client = UserClient()
|
47
47
|
api_key = await client.get_api_key()
|
48
|
-
click.echo("API key:")
|
49
|
-
click.echo(click.style(api_key, fg="green"))
|
48
|
+
click.echo(f"API key: {click.style(api_key, fg='green')}")
|
50
49
|
|
51
50
|
|
52
51
|
if __name__ == "__main__":
|
kscale/web/clients/base.py
CHANGED
@@ -5,6 +5,7 @@ import json
|
|
5
5
|
import logging
|
6
6
|
import os
|
7
7
|
import secrets
|
8
|
+
import sys
|
8
9
|
import time
|
9
10
|
import webbrowser
|
10
11
|
from types import TracebackType
|
@@ -31,6 +32,10 @@ OAUTH_PORT = 16821
|
|
31
32
|
HEADER_NAME = "x-kscale-api-key"
|
32
33
|
|
33
34
|
|
35
|
+
def verbose_error() -> bool:
|
36
|
+
return os.environ.get("KSCALE_VERBOSE_ERROR", "0") == "1"
|
37
|
+
|
38
|
+
|
34
39
|
class OAuthCallback:
|
35
40
|
def __init__(self) -> None:
|
36
41
|
self.token_type: str | None = None
|
@@ -375,8 +380,14 @@ class BaseClient:
|
|
375
380
|
response = await client.request(method, url, **kwargs)
|
376
381
|
|
377
382
|
if response.is_error:
|
378
|
-
logger.error("
|
379
|
-
|
383
|
+
logger.error("Got %d error K-Scale: %s", response.status_code, response.text)
|
384
|
+
if verbose_error():
|
385
|
+
response.raise_for_status()
|
386
|
+
else:
|
387
|
+
logger.error("Use KSCALE_VERBOSE_ERROR=1 to see the full error message")
|
388
|
+
logger.error("If this persists, please create an issue here: https://github.com/kscalelabs/kscale")
|
389
|
+
sys.exit(1)
|
390
|
+
|
380
391
|
return response.json()
|
381
392
|
|
382
393
|
async def close(self) -> None:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.2
|
2
2
|
Name: kscale
|
3
|
-
Version: 0.3.
|
3
|
+
Version: 0.3.8
|
4
4
|
Summary: The kscale project
|
5
5
|
Home-page: https://github.com/kscalelabs/kscale
|
6
6
|
Author: Benjamin Bolte
|
@@ -21,7 +21,6 @@ Requires-Dist: click
|
|
21
21
|
Requires-Dist: colorlogging
|
22
22
|
Requires-Dist: tabulate
|
23
23
|
Requires-Dist: async-lru
|
24
|
-
Requires-Dist: krec
|
25
24
|
Provides-Extra: dev
|
26
25
|
Requires-Dist: black; extra == "dev"
|
27
26
|
Requires-Dist: darglint; extra == "dev"
|
@@ -1,9 +1,9 @@
|
|
1
|
-
kscale/__init__.py,sha256=
|
2
|
-
kscale/cli.py,sha256=
|
1
|
+
kscale/__init__.py,sha256=XbIxlxq1doXA2L5v8II07s5g2CoqvIRp8LT5AfdFJDY,200
|
2
|
+
kscale/cli.py,sha256=JvaPtmWvF7s0D4I3K98eZAItf3oOi2ULsn5aPGxDcu4,795
|
3
3
|
kscale/conf.py,sha256=dm35XSnzJp93St-ixVtYN4Nvqvb5upPGBrWkSI6Yb-4,1743
|
4
4
|
kscale/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
5
|
kscale/requirements-dev.txt,sha256=WI7-ea4IRJakmqVMN8QKhOsDGrghwtvk03aIsFaNSIw,130
|
6
|
-
kscale/requirements.txt,sha256=
|
6
|
+
kscale/requirements.txt,sha256=tj8wBkIhl6ijk5rFqtPCdkpdOS7XSIcUvsz_Eq1axcw,198
|
7
7
|
kscale/artifacts/__init__.py,sha256=RK8wdybtCJPgdLLJ8R8-YMi1Ph5ojqAKVJZowHONtgo,232
|
8
8
|
kscale/artifacts/plane.obj,sha256=x59-IIrWpLjhotChiqT2Ul6U8s0RcHkaEeUZb4KXL1c,348
|
9
9
|
kscale/artifacts/plane.urdf,sha256=LCiTk14AyTHjkZ1jvsb0hNaEaJUxDb8Z1JjsgpXu3YM,819
|
@@ -15,20 +15,19 @@ kscale/web/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
15
|
kscale/web/utils.py,sha256=Mme-FAQ0_zbjjOQeX8wyq8F4kL4i9fH7ytri16U6qOA,1046
|
16
16
|
kscale/web/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
17
17
|
kscale/web/cli/robot.py,sha256=rI-A4_0uvJPeA71Apl4Z3mV5fIfWkgmzT9JRmJYxz3A,3307
|
18
|
-
kscale/web/cli/robot_class.py,sha256=
|
19
|
-
kscale/web/cli/
|
20
|
-
kscale/web/cli/user.py,sha256=aaJJCL1P5lfhK6ZC9OwOHXKA-I3MWqVZ_k7TYnx33CY,1303
|
18
|
+
kscale/web/cli/robot_class.py,sha256=Vg-KWROdmz3yqBEG-RS5xiUviKwEz8DZ3q2estK66ZQ,19082
|
19
|
+
kscale/web/cli/user.py,sha256=9IGsJBPyhjsmT04mZ2RGOs35ePfqB2RltYP0Ty5zF5o,1290
|
21
20
|
kscale/web/clients/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
22
|
-
kscale/web/clients/base.py,sha256=
|
21
|
+
kscale/web/clients/base.py,sha256=ThQakuvGh7CquiGQ4wiCyCWAgqmKInsJcANo910X9Uw,15585
|
23
22
|
kscale/web/clients/client.py,sha256=rzW2s8T7bKVuybOSQ65-ghl02rcXBoOxnx_nUDwgEPw,362
|
24
23
|
kscale/web/clients/robot.py,sha256=PI8HHkU-4Re9I5rLpp6dGbekRE-rBNVfXZxR_mO2MqE,1485
|
25
24
|
kscale/web/clients/robot_class.py,sha256=G8Nk6V7LGJE9Wpg9tyyCkIfz1fRTsxXQRgHtleiUVqo,6834
|
26
25
|
kscale/web/clients/user.py,sha256=jsa1_s6qXRM-AGBbHlPhd1NierUtynjY9tVAPNr6_Os,568
|
27
26
|
kscale/web/gen/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
28
27
|
kscale/web/gen/api.py,sha256=VWlt8TaMZaAhCPPIWhx1d4yE0BRfpVz0-_Cps8hjgAI,4662
|
29
|
-
kscale-0.3.
|
30
|
-
kscale-0.3.
|
31
|
-
kscale-0.3.
|
32
|
-
kscale-0.3.
|
33
|
-
kscale-0.3.
|
34
|
-
kscale-0.3.
|
28
|
+
kscale-0.3.8.dist-info/LICENSE,sha256=HCN2bImAzUOXldAZZI7JZ9PYq6OwMlDAP_PpX1HnuN0,1071
|
29
|
+
kscale-0.3.8.dist-info/METADATA,sha256=b2ThYIovV2vTrR_uU44Hl4VHWKZEJkNJa56_1VqyvrE,2320
|
30
|
+
kscale-0.3.8.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
31
|
+
kscale-0.3.8.dist-info/entry_points.txt,sha256=N_0pCpPnwGDYVzOeuaSOrbJkS5L3lS9d8CxpJF1f8UI,62
|
32
|
+
kscale-0.3.8.dist-info/top_level.txt,sha256=C2ynjYwopg6YjgttnI2dJjasyq3EKNmYp-IfQg9Xms4,7
|
33
|
+
kscale-0.3.8.dist-info/RECORD,,
|
kscale/web/cli/token.py
DELETED
@@ -1,33 +0,0 @@
|
|
1
|
-
"""Defines the CLI for interacting with K-Scale's OpenID Connect server."""
|
2
|
-
|
3
|
-
import logging
|
4
|
-
|
5
|
-
import click
|
6
|
-
|
7
|
-
from kscale.utils.cli import coro
|
8
|
-
from kscale.web.clients.base import BaseClient
|
9
|
-
|
10
|
-
logger = logging.getLogger(__name__)
|
11
|
-
|
12
|
-
|
13
|
-
@click.group()
|
14
|
-
def cli() -> None:
|
15
|
-
"""Retrieve an OICD token from the K-Scale authentication server."""
|
16
|
-
pass
|
17
|
-
|
18
|
-
|
19
|
-
@cli.command()
|
20
|
-
@coro
|
21
|
-
async def get() -> None:
|
22
|
-
"""Get a bearer token from OpenID Connect."""
|
23
|
-
logging.getLogger("aiohttp.access").setLevel(logging.WARNING)
|
24
|
-
async with BaseClient() as client:
|
25
|
-
try:
|
26
|
-
token = await client.get_bearer_token()
|
27
|
-
logger.info("Bearer token: %s", token)
|
28
|
-
except Exception:
|
29
|
-
logger.exception("Error getting bearer token")
|
30
|
-
|
31
|
-
|
32
|
-
if __name__ == "__main__":
|
33
|
-
cli()
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|