kscale 0.1.5__py3-none-any.whl → 0.2.1__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 CHANGED
@@ -1,9 +1,9 @@
1
1
  """Defines the common interface for the K-Scale Python API."""
2
2
 
3
- __version__ = "0.1.5"
3
+ __version__ = "0.2.1"
4
4
 
5
5
  from pathlib import Path
6
6
 
7
- from kscale.api import K
7
+ from kscale.web.clients.client import WWWClient as K
8
8
 
9
9
  ROOT_DIR = Path(__file__).parent
@@ -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.download_robot_class_urdf(class_name, cache=not no_cache)
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
 
@@ -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
  ):
@@ -21,6 +21,8 @@ logger = logging.getLogger(__name__)
21
21
  UPLOAD_TIMEOUT = 300.0
22
22
  DOWNLOAD_TIMEOUT = 60.0
23
23
 
24
+ INFO_FILE_NAME = ".info.json"
25
+
24
26
 
25
27
  class RobotClassClient(BaseClient):
26
28
  async def get_robot_classes(self) -> list[RobotClass]:
@@ -98,7 +100,7 @@ class RobotClassClient(BaseClient):
98
100
  r.raise_for_status()
99
101
  return response
100
102
 
101
- async def download_robot_class_urdf(self, class_name: str, *, cache: bool = True) -> Path:
103
+ async def download_compressed_urdf(self, class_name: str, *, cache: bool = True) -> Path:
102
104
  cache_path = get_robots_dir() / class_name / "robot.tgz"
103
105
  if cache and cache_path.exists() and not should_refresh_file(cache_path):
104
106
  return cache_path
@@ -108,7 +110,7 @@ class RobotClassClient(BaseClient):
108
110
  cache_path.parent.mkdir(parents=True, exist_ok=True)
109
111
 
110
112
  # Checks the md5 hash of the file.
111
- cache_path_info = cache_path.parent / "info.json"
113
+ cache_path_info = cache_path.parent / INFO_FILE_NAME
112
114
  if cache_path_info.exists():
113
115
  with open(cache_path_info, "r") as f:
114
116
  info = json.load(f)
@@ -133,13 +135,42 @@ class RobotClassClient(BaseClient):
133
135
  if hash_value_hex != expected_hash:
134
136
  raise ValueError(f"MD5 hash mismatch: {hash_value_hex} != {expected_hash}")
135
137
 
136
- logger.info("Unpacking downloaded file")
137
- with tarfile.open(cache_path, "r:gz") as tar:
138
- tar.extractall(path=cache_path.parent)
139
-
140
138
  logger.info("Updating downloaded file information")
141
139
  info = {"md5_hash": hash_value_hex}
142
140
  with open(cache_path_info, "w") as f:
143
141
  json.dump(info, f)
144
142
 
145
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,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: kscale
3
- Version: 0.1.5
3
+ Version: 0.2.1
4
4
  Summary: The kscale project
5
5
  Home-page: https://github.com/kscalelabs/kscale
6
6
  Author: Benjamin Bolte
@@ -1,5 +1,4 @@
1
- kscale/__init__.py,sha256=SnUAXz1ZDSR98-rg2Ne7pAF7YPDh5i3v3EyoZxR3l-4,172
2
- kscale/api.py,sha256=jmiuFurTN_Gj_-k-6asqxw8wp-_bgJUXgMPFgJ4lqHA,230
1
+ kscale/__init__.py,sha256=73uaPK_Lj05TFJYmYf9BjFp9JHB5XKLlMIwORoDZpx0,200
3
2
  kscale/cli.py,sha256=PMHLKR5UwdbbReVmqHXpJ-K9-mGHv_0I7KQkwxmFcUA,881
4
3
  kscale/conf.py,sha256=OLGz2J-9NAaICoWeN9-hCxrOsyf0vpJBNw-UzIsHdwE,1572
5
4
  kscale/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -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=ymC5phUqofvOXv5P6f51b9lMK5eRDaavvnzS0x9rDbU,3574
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=QjBicdHQYNoUG9XRjAYmGu3THae9DzWa_hQox3OO1Gw,214
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=O_6lAKAcdNGFVtojuiQlgPCVP5MQlYPZG5Z-awwauho,5206
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.1.5.dist-info/LICENSE,sha256=HCN2bImAzUOXldAZZI7JZ9PYq6OwMlDAP_PpX1HnuN0,1071
32
- kscale-0.1.5.dist-info/METADATA,sha256=kTsVXOA7Zh2m_eqBm2iaxanfL92BNpPadsC-vasZ018,2340
33
- kscale-0.1.5.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
34
- kscale-0.1.5.dist-info/entry_points.txt,sha256=N_0pCpPnwGDYVzOeuaSOrbJkS5L3lS9d8CxpJF1f8UI,62
35
- kscale-0.1.5.dist-info/top_level.txt,sha256=C2ynjYwopg6YjgttnI2dJjasyq3EKNmYp-IfQg9Xms4,7
36
- kscale-0.1.5.dist-info/RECORD,,
29
+ kscale-0.2.1.dist-info/LICENSE,sha256=HCN2bImAzUOXldAZZI7JZ9PYq6OwMlDAP_PpX1HnuN0,1071
30
+ kscale-0.2.1.dist-info/METADATA,sha256=IeSxq7ApAKrn48Xep-kRu-wQpOq14y_M3GbUgcpHbJE,2340
31
+ kscale-0.2.1.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
32
+ kscale-0.2.1.dist-info/entry_points.txt,sha256=N_0pCpPnwGDYVzOeuaSOrbJkS5L3lS9d8CxpJF1f8UI,62
33
+ kscale-0.2.1.dist-info/top_level.txt,sha256=C2ynjYwopg6YjgttnI2dJjasyq3EKNmYp-IfQg9Xms4,7
34
+ kscale-0.2.1.dist-info/RECORD,,
kscale/api.py DELETED
@@ -1,11 +0,0 @@
1
- """Defines common functionality for the K-Scale API."""
2
-
3
- from kscale.utils.api_base import APIBase
4
- from kscale.web.api import WebAPI
5
-
6
-
7
- class K(
8
- WebAPI,
9
- APIBase,
10
- ):
11
- """Defines a common interface for the K-Scale API."""
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