kscale 0.2.0__py3-none-any.whl → 0.2.2__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 +2 -2
- kscale/conf.py +9 -4
- kscale/web/cli/robot_class.py +1 -1
- kscale/web/clients/client.py +4 -0
- kscale/web/clients/robot_class.py +38 -2
- {kscale-0.2.0.dist-info → kscale-0.2.2.dist-info}/METADATA +1 -1
- {kscale-0.2.0.dist-info → kscale-0.2.2.dist-info}/RECORD +11 -13
- kscale/api.py +0 -11
- kscale/web/api.py +0 -18
- {kscale-0.2.0.dist-info → kscale-0.2.2.dist-info}/LICENSE +0 -0
- {kscale-0.2.0.dist-info → kscale-0.2.2.dist-info}/WHEEL +0 -0
- {kscale-0.2.0.dist-info → kscale-0.2.2.dist-info}/entry_points.txt +0 -0
- {kscale-0.2.0.dist-info → kscale-0.2.2.dist-info}/top_level.txt +0 -0
kscale/__init__.py
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
"""Defines the common interface for the K-Scale Python API."""
|
2
2
|
|
3
|
-
__version__ = "0.2.
|
3
|
+
__version__ = "0.2.2"
|
4
4
|
|
5
5
|
from pathlib import Path
|
6
6
|
|
7
|
-
from kscale.
|
7
|
+
from kscale.web.clients.client import WWWClient as K
|
8
8
|
|
9
9
|
ROOT_DIR = Path(__file__).parent
|
kscale/conf.py
CHANGED
@@ -11,6 +11,8 @@ from omegaconf import II, OmegaConf
|
|
11
11
|
# This is the public API endpoint for the K-Scale WWW API.
|
12
12
|
DEFAULT_API_ROOT = "https://api.kscale.dev"
|
13
13
|
|
14
|
+
SETTINGS_FILE_NAME = "settings.yaml"
|
15
|
+
|
14
16
|
|
15
17
|
def get_path() -> Path:
|
16
18
|
if "KSCALE_CONFIG_DIR" in os.environ:
|
@@ -41,9 +43,12 @@ class Settings:
|
|
41
43
|
if not (dir_path := get_path()).exists():
|
42
44
|
warnings.warn(f"Settings directory does not exist: {dir_path}. Creating it now.")
|
43
45
|
dir_path.mkdir(parents=True)
|
44
|
-
OmegaConf.save(config, dir_path /
|
46
|
+
OmegaConf.save(config, dir_path / SETTINGS_FILE_NAME)
|
45
47
|
else:
|
46
|
-
|
47
|
-
|
48
|
-
|
48
|
+
try:
|
49
|
+
with open(dir_path / SETTINGS_FILE_NAME, "r") as f:
|
50
|
+
raw_settings = OmegaConf.load(f)
|
51
|
+
config = OmegaConf.merge(config, raw_settings)
|
52
|
+
except Exception as e:
|
53
|
+
warnings.warn(f"Failed to load settings: {e}")
|
49
54
|
return config
|
kscale/web/cli/robot_class.py
CHANGED
@@ -105,7 +105,7 @@ async def upload(class_name: str, urdf_file: str) -> None:
|
|
105
105
|
async def download(class_name: str, no_cache: bool) -> None:
|
106
106
|
"""Downloads a URDF file from a robot class."""
|
107
107
|
async with RobotClassClient() as client:
|
108
|
-
urdf_file = await client.
|
108
|
+
urdf_file = await client.download_compressed_urdf(class_name, cache=not no_cache)
|
109
109
|
click.echo(f"URDF downloaded: {click.style(urdf_file, fg='green')}")
|
110
110
|
|
111
111
|
|
kscale/web/clients/client.py
CHANGED
@@ -1,10 +1,14 @@
|
|
1
1
|
"""Defines a unified client for the K-Scale WWW API."""
|
2
2
|
|
3
3
|
from kscale.web.clients.base import BaseClient
|
4
|
+
from kscale.web.clients.robot import RobotClient
|
5
|
+
from kscale.web.clients.robot_class import RobotClassClient
|
4
6
|
from kscale.web.clients.user import UserClient
|
5
7
|
|
6
8
|
|
7
9
|
class WWWClient(
|
10
|
+
RobotClient,
|
11
|
+
RobotClassClient,
|
8
12
|
UserClient,
|
9
13
|
BaseClient,
|
10
14
|
):
|
@@ -3,6 +3,7 @@
|
|
3
3
|
import hashlib
|
4
4
|
import json
|
5
5
|
import logging
|
6
|
+
import tarfile
|
6
7
|
from pathlib import Path
|
7
8
|
|
8
9
|
import httpx
|
@@ -20,6 +21,8 @@ logger = logging.getLogger(__name__)
|
|
20
21
|
UPLOAD_TIMEOUT = 300.0
|
21
22
|
DOWNLOAD_TIMEOUT = 60.0
|
22
23
|
|
24
|
+
INFO_FILE_NAME = ".info.json"
|
25
|
+
|
23
26
|
|
24
27
|
class RobotClassClient(BaseClient):
|
25
28
|
async def get_robot_classes(self) -> list[RobotClass]:
|
@@ -97,7 +100,7 @@ class RobotClassClient(BaseClient):
|
|
97
100
|
r.raise_for_status()
|
98
101
|
return response
|
99
102
|
|
100
|
-
async def
|
103
|
+
async def download_compressed_urdf(self, class_name: str, *, cache: bool = True) -> Path:
|
101
104
|
cache_path = get_robots_dir() / class_name / "robot.tgz"
|
102
105
|
if cache and cache_path.exists() and not should_refresh_file(cache_path):
|
103
106
|
return cache_path
|
@@ -107,7 +110,7 @@ class RobotClassClient(BaseClient):
|
|
107
110
|
cache_path.parent.mkdir(parents=True, exist_ok=True)
|
108
111
|
|
109
112
|
# Checks the md5 hash of the file.
|
110
|
-
cache_path_info = cache_path.parent /
|
113
|
+
cache_path_info = cache_path.parent / INFO_FILE_NAME
|
111
114
|
if cache_path_info.exists():
|
112
115
|
with open(cache_path_info, "r") as f:
|
113
116
|
info = json.load(f)
|
@@ -138,3 +141,36 @@ class RobotClassClient(BaseClient):
|
|
138
141
|
json.dump(info, f)
|
139
142
|
|
140
143
|
return cache_path
|
144
|
+
|
145
|
+
async def download_and_extract_urdf(self, class_name: str, *, cache: bool = True) -> Path:
|
146
|
+
cache_path = await self.download_compressed_urdf(class_name, cache=cache)
|
147
|
+
|
148
|
+
# Reads the MD5 hash from the info file.
|
149
|
+
cache_path_info = cache_path.parent / INFO_FILE_NAME
|
150
|
+
with open(cache_path_info, "r") as f:
|
151
|
+
info = json.load(f)
|
152
|
+
expected_hash = info["md5_hash"]
|
153
|
+
|
154
|
+
# Unpacks the file if requested.
|
155
|
+
unpack_path = cache_path.parent / "robot"
|
156
|
+
unpack_path.mkdir(parents=True, exist_ok=True)
|
157
|
+
unpacked_path_info = unpack_path / INFO_FILE_NAME
|
158
|
+
|
159
|
+
# If the file has already been unpacked, return the path.
|
160
|
+
if unpacked_path_info.exists():
|
161
|
+
with open(unpacked_path_info, "r") as f:
|
162
|
+
info = json.load(f)
|
163
|
+
if info["md5_hash"] == expected_hash:
|
164
|
+
unpack_path.touch()
|
165
|
+
return unpack_path
|
166
|
+
|
167
|
+
logger.info("Unpacking URDF file")
|
168
|
+
with tarfile.open(cache_path, "r:gz") as tar:
|
169
|
+
tar.extractall(path=unpack_path)
|
170
|
+
|
171
|
+
logger.info("Updating downloaded file information")
|
172
|
+
info = {"md5_hash": expected_hash}
|
173
|
+
with open(unpacked_path_info, "w") as f:
|
174
|
+
json.dump(info, f)
|
175
|
+
|
176
|
+
return unpack_path
|
@@ -1,7 +1,6 @@
|
|
1
|
-
kscale/__init__.py,sha256=
|
2
|
-
kscale/api.py,sha256=jmiuFurTN_Gj_-k-6asqxw8wp-_bgJUXgMPFgJ4lqHA,230
|
1
|
+
kscale/__init__.py,sha256=A1hh6cfKSMUSIDxYuPny2OkMm4-WcRlZsRYow_TO9Vo,200
|
3
2
|
kscale/cli.py,sha256=PMHLKR5UwdbbReVmqHXpJ-K9-mGHv_0I7KQkwxmFcUA,881
|
4
|
-
kscale/conf.py,sha256=
|
3
|
+
kscale/conf.py,sha256=dm35XSnzJp93St-ixVtYN4Nvqvb5upPGBrWkSI6Yb-4,1743
|
5
4
|
kscale/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
6
5
|
kscale/requirements-dev.txt,sha256=WI7-ea4IRJakmqVMN8QKhOsDGrghwtvk03aIsFaNSIw,130
|
7
6
|
kscale/requirements.txt,sha256=_BGbnKTQaXKx0bNEG0wguod9swsiCb2mF6rLm7sFJ2Q,214
|
@@ -13,24 +12,23 @@ kscale/utils/api_base.py,sha256=Kk_WtRDdJHmOg6NtHmVxVrcfARSUkhfr29ypLch_pO0,112
|
|
13
12
|
kscale/utils/checksum.py,sha256=jt6QmmQND9zrOEnUtOfZpLYROhgto4Gh3OpdUWk4tZA,1093
|
14
13
|
kscale/utils/cli.py,sha256=JoaY5x5SdUx97KmMM9j5AjRRUqqrTlJ9qVckZptEsYA,827
|
15
14
|
kscale/web/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
16
|
-
kscale/web/api.py,sha256=oyW0XLfX96RPe1xNgdf8ejfATdLlNlP0CL1lP0FN1nM,593
|
17
15
|
kscale/web/utils.py,sha256=Mme-FAQ0_zbjjOQeX8wyq8F4kL4i9fH7ytri16U6qOA,1046
|
18
16
|
kscale/web/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
19
17
|
kscale/web/cli/robot.py,sha256=rI-A4_0uvJPeA71Apl4Z3mV5fIfWkgmzT9JRmJYxz3A,3307
|
20
|
-
kscale/web/cli/robot_class.py,sha256=
|
18
|
+
kscale/web/cli/robot_class.py,sha256=8igY6oWUDafb644QzmqC5cX6Az0rNJau_jvLr7YkCd0,3573
|
21
19
|
kscale/web/cli/token.py,sha256=1rFC8MYKtqbNsQa2KIqwW1tqpaMtFaxuNsallwejXTU,787
|
22
20
|
kscale/web/cli/user.py,sha256=aaJJCL1P5lfhK6ZC9OwOHXKA-I3MWqVZ_k7TYnx33CY,1303
|
23
21
|
kscale/web/clients/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
24
22
|
kscale/web/clients/base.py,sha256=uovIxtkotRxrIFL0PhLYDIhhfXrIGjXxNY2FLVO3L18,15110
|
25
|
-
kscale/web/clients/client.py,sha256=
|
23
|
+
kscale/web/clients/client.py,sha256=rzW2s8T7bKVuybOSQ65-ghl02rcXBoOxnx_nUDwgEPw,362
|
26
24
|
kscale/web/clients/robot.py,sha256=HMfJnkDxaJ_o7X2vdYYS9iob1JRoBG2qiGmQpCQZpAk,1485
|
27
|
-
kscale/web/clients/robot_class.py,sha256=
|
25
|
+
kscale/web/clients/robot_class.py,sha256=zCFL-Y_jIfO9_qOwzBAHzfgFxzpltMte4LM5haCC24U,6385
|
28
26
|
kscale/web/clients/user.py,sha256=jsa1_s6qXRM-AGBbHlPhd1NierUtynjY9tVAPNr6_Os,568
|
29
27
|
kscale/web/gen/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
30
28
|
kscale/web/gen/api.py,sha256=SovcII36JFgK9jd2CXlLPMjiUROGB4vEnapOsYMUrkU,2188
|
31
|
-
kscale-0.2.
|
32
|
-
kscale-0.2.
|
33
|
-
kscale-0.2.
|
34
|
-
kscale-0.2.
|
35
|
-
kscale-0.2.
|
36
|
-
kscale-0.2.
|
29
|
+
kscale-0.2.2.dist-info/LICENSE,sha256=HCN2bImAzUOXldAZZI7JZ9PYq6OwMlDAP_PpX1HnuN0,1071
|
30
|
+
kscale-0.2.2.dist-info/METADATA,sha256=c0duCLdAcxXv6SXhRpkoSO99-ahyAwV2HRhYiBZXK1k,2340
|
31
|
+
kscale-0.2.2.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
32
|
+
kscale-0.2.2.dist-info/entry_points.txt,sha256=N_0pCpPnwGDYVzOeuaSOrbJkS5L3lS9d8CxpJF1f8UI,62
|
33
|
+
kscale-0.2.2.dist-info/top_level.txt,sha256=C2ynjYwopg6YjgttnI2dJjasyq3EKNmYp-IfQg9Xms4,7
|
34
|
+
kscale-0.2.2.dist-info/RECORD,,
|
kscale/api.py
DELETED
kscale/web/api.py
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
"""Defines a common interface for the K-Scale WWW API."""
|
2
|
-
|
3
|
-
from kscale.utils.api_base import APIBase
|
4
|
-
from kscale.web.clients.client import WWWClient
|
5
|
-
from kscale.web.gen.api import ProfileResponse
|
6
|
-
|
7
|
-
|
8
|
-
class WebAPI(APIBase):
|
9
|
-
async def www_client(self) -> WWWClient:
|
10
|
-
return WWWClient()
|
11
|
-
|
12
|
-
async def get_profile_info(self) -> ProfileResponse:
|
13
|
-
client = await self.www_client()
|
14
|
-
return await client.get_profile_info()
|
15
|
-
|
16
|
-
async def get_api_key(self, num_hours: int = 24) -> str:
|
17
|
-
client = await self.www_client()
|
18
|
-
return await client.get_api_key(num_hours)
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|