kscale 0.1.0__py3-none-any.whl → 0.1.2__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
kscale/__init__.py CHANGED
@@ -1,6 +1,6 @@
1
1
  """Defines the common interface for the K-Scale Python API."""
2
2
 
3
- __version__ = "0.1.0"
3
+ __version__ = "0.1.2"
4
4
 
5
5
  from pathlib import Path
6
6
 
kscale/conf.py CHANGED
@@ -22,6 +22,7 @@ def get_path() -> Path:
22
22
  class WWWSettings:
23
23
  api_root: str = field(default=DEFAULT_API_ROOT)
24
24
  cache_dir: str = field(default=II("oc.env:KSCALE_CACHE_DIR,'~/.kscale/cache/'"))
25
+ refresh_interval_minutes: int = field(default=60 * 24)
25
26
 
26
27
 
27
28
  @dataclass
kscale/requirements.txt CHANGED
@@ -3,7 +3,6 @@
3
3
  # Configuration
4
4
  omegaconf
5
5
  email_validator
6
- colorlogging
7
6
 
8
7
  # HTTP requests
9
8
  aiohttp
@@ -17,6 +16,7 @@ yarl
17
16
  # CLI
18
17
  aiofiles
19
18
  click
19
+ colorlogging
20
20
  tabulate
21
21
 
22
22
  # Async
@@ -1,6 +1,7 @@
1
1
  """Defines the client for interacting with the K-Scale robot class endpoints."""
2
2
 
3
3
  import hashlib
4
+ import json
4
5
  import logging
5
6
  from pathlib import Path
6
7
 
@@ -8,10 +9,13 @@ import httpx
8
9
 
9
10
  from kscale.web.clients.base import BaseClient
10
11
  from kscale.web.gen.api import RobotClass, RobotDownloadURDFResponse, RobotUploadURDFResponse
11
- from kscale.web.utils import get_cache_dir
12
+ from kscale.web.utils import get_cache_dir, should_refresh_file
12
13
 
13
14
  logger = logging.getLogger(__name__)
14
15
 
16
+ UPLOAD_TIMEOUT = 300.0
17
+ DOWNLOAD_TIMEOUT = 60.0
18
+
15
19
 
16
20
  class RobotClassClient(BaseClient):
17
21
  async def get_robot_classes(self) -> list[RobotClass]:
@@ -77,7 +81,9 @@ class RobotClassClient(BaseClient):
77
81
  auth=True,
78
82
  )
79
83
  response = RobotUploadURDFResponse.model_validate(data)
80
- async with httpx.AsyncClient() as client:
84
+ async with httpx.AsyncClient(
85
+ timeout=httpx.Timeout(UPLOAD_TIMEOUT),
86
+ ) as client:
81
87
  async with client.stream(
82
88
  "PUT",
83
89
  response.url,
@@ -89,15 +95,26 @@ class RobotClassClient(BaseClient):
89
95
 
90
96
  async def download_robot_class_urdf(self, class_name: str, *, cache: bool = True) -> Path:
91
97
  cache_path = get_cache_dir() / class_name / "robot.tgz"
92
- if cache and cache_path.exists():
98
+ if cache and cache_path.exists() and not should_refresh_file(cache_path):
93
99
  return cache_path
94
100
  data = await self._request("GET", f"/robots/urdf/{class_name}", auth=True)
95
101
  response = RobotDownloadURDFResponse.model_validate(data)
96
102
  expected_hash = response.md5_hash
97
103
  cache_path.parent.mkdir(parents=True, exist_ok=True)
98
104
 
105
+ # Checks the md5 hash of the file.
106
+ cache_path_info = cache_path.parent / "info.json"
107
+ if cache_path_info.exists():
108
+ with open(cache_path_info, "r") as f:
109
+ info = json.load(f)
110
+ if info["md5_hash"] == expected_hash:
111
+ cache_path.touch()
112
+ return cache_path
113
+
99
114
  logger.info("Downloading URDF file from %s", response.url)
100
- async with httpx.AsyncClient() as client:
115
+ async with httpx.AsyncClient(
116
+ timeout=httpx.Timeout(DOWNLOAD_TIMEOUT),
117
+ ) as client:
101
118
  with open(cache_path, "wb") as file:
102
119
  hash_value = hashlib.md5()
103
120
  async with client.stream("GET", response.url) as r:
@@ -111,4 +128,10 @@ class RobotClassClient(BaseClient):
111
128
  if hash_value_hex != expected_hash:
112
129
  raise ValueError(f"MD5 hash mismatch: {hash_value_hex} != {expected_hash}")
113
130
 
131
+ # Updates the info file.
132
+ logger.info("Updating downloaded file information")
133
+ info = {"md5_hash": hash_value_hex}
134
+ with open(cache_path_info, "w") as f:
135
+ json.dump(info, f)
136
+
114
137
  return cache_path
kscale/web/utils.py CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  import functools
4
4
  import logging
5
+ import time
5
6
  from pathlib import Path
6
7
 
7
8
  from kscale.conf import Settings
@@ -17,6 +18,11 @@ def get_cache_dir() -> Path:
17
18
  return Path(Settings.load().www.cache_dir).expanduser().resolve()
18
19
 
19
20
 
21
+ def should_refresh_file(file: Path) -> bool:
22
+ """Returns whether the file should be refreshed."""
23
+ return file.exists() and file.stat().st_mtime < time.time() - Settings.load().www.refresh_interval_minutes * 60
24
+
25
+
20
26
  @functools.lru_cache
21
27
  def get_artifact_dir(artifact_id: str) -> Path:
22
28
  """Returns the directory for a specific artifact."""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: kscale
3
- Version: 0.1.0
3
+ Version: 0.1.2
4
4
  Summary: The kscale project
5
5
  Home-page: https://github.com/kscalelabs/kscale
6
6
  Author: Benjamin Bolte
@@ -9,7 +9,6 @@ Description-Content-Type: text/markdown
9
9
  License-File: LICENSE
10
10
  Requires-Dist: omegaconf
11
11
  Requires-Dist: email_validator
12
- Requires-Dist: colorlogging
13
12
  Requires-Dist: aiohttp
14
13
  Requires-Dist: cryptography
15
14
  Requires-Dist: httpx
@@ -19,6 +18,7 @@ Requires-Dist: requests
19
18
  Requires-Dist: yarl
20
19
  Requires-Dist: aiofiles
21
20
  Requires-Dist: click
21
+ Requires-Dist: colorlogging
22
22
  Requires-Dist: tabulate
23
23
  Requires-Dist: async-lru
24
24
  Requires-Dist: krec
@@ -1,10 +1,10 @@
1
- kscale/__init__.py,sha256=2jGtLxds4EdX8FEGT3yA74b0VgrkOEBx_PxiWvoh924,172
1
+ kscale/__init__.py,sha256=Do_oyhXSMNLURbvLDeTx4nJqgYgPhuREBynKDoeJyX4,172
2
2
  kscale/api.py,sha256=jmiuFurTN_Gj_-k-6asqxw8wp-_bgJUXgMPFgJ4lqHA,230
3
3
  kscale/cli.py,sha256=PMHLKR5UwdbbReVmqHXpJ-K9-mGHv_0I7KQkwxmFcUA,881
4
- kscale/conf.py,sha256=i5gDTs8D73l2OvX4pOlPWtT2PC26MmoKw3PPwf8HM7U,1526
4
+ kscale/conf.py,sha256=x9jJ8pmSweUsLScN741B21SySCrnRZioV-1ZkhmERbI,1585
5
5
  kscale/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
6
  kscale/requirements-dev.txt,sha256=WI7-ea4IRJakmqVMN8QKhOsDGrghwtvk03aIsFaNSIw,130
7
- kscale/requirements.txt,sha256=lKDpyebp9nHZ2uHIuS6FQ6SAg7YO_0sDyb67MFmP4h4,214
7
+ kscale/requirements.txt,sha256=_BGbnKTQaXKx0bNEG0wguod9swsiCb2mF6rLm7sFJ2Q,214
8
8
  kscale/artifacts/__init__.py,sha256=RK8wdybtCJPgdLLJ8R8-YMi1Ph5ojqAKVJZowHONtgo,232
9
9
  kscale/artifacts/plane.obj,sha256=x59-IIrWpLjhotChiqT2Ul6U8s0RcHkaEeUZb4KXL1c,348
10
10
  kscale/artifacts/plane.urdf,sha256=LCiTk14AyTHjkZ1jvsb0hNaEaJUxDb8Z1JjsgpXu3YM,819
@@ -14,7 +14,7 @@ kscale/utils/checksum.py,sha256=jt6QmmQND9zrOEnUtOfZpLYROhgto4Gh3OpdUWk4tZA,1093
14
14
  kscale/utils/cli.py,sha256=JoaY5x5SdUx97KmMM9j5AjRRUqqrTlJ9qVckZptEsYA,827
15
15
  kscale/web/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
16
  kscale/web/api.py,sha256=YioIdruq7LCKSBf9SvOGkv914W36_zBmpTzsJqKc0wE,439
17
- kscale/web/utils.py,sha256=KFB9lrgn_2BRY38Sfbb_QOKZ8fWyINqIqLPwN0rjbyk,806
17
+ kscale/web/utils.py,sha256=l_m8GETCThhnXbHfXq53j1b-E__uZ5GuUbsQ8HR0WA0,1037
18
18
  kscale/web/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
19
  kscale/web/cli/robot.py,sha256=rI-A4_0uvJPeA71Apl4Z3mV5fIfWkgmzT9JRmJYxz3A,3307
20
20
  kscale/web/cli/robot_class.py,sha256=ymC5phUqofvOXv5P6f51b9lMK5eRDaavvnzS0x9rDbU,3574
@@ -24,13 +24,13 @@ kscale/web/clients/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuF
24
24
  kscale/web/clients/base.py,sha256=NRqRH2JTXjJLUMDM93txKgwFu9bQRYhCYLYy7vE76Ik,11462
25
25
  kscale/web/clients/client.py,sha256=QjBicdHQYNoUG9XRjAYmGu3THae9DzWa_hQox3OO1Gw,214
26
26
  kscale/web/clients/robot.py,sha256=HMfJnkDxaJ_o7X2vdYYS9iob1JRoBG2qiGmQpCQZpAk,1485
27
- kscale/web/clients/robot_class.py,sha256=yC0G4pVPDoEskf0QfQLxejyDgHLT8gR1RLJ8ioCGoOM,4236
27
+ kscale/web/clients/robot_class.py,sha256=vFXlvwQBmt7zEOixAI-mByBEkXCMZIW5OZjiESMavps,5050
28
28
  kscale/web/clients/user.py,sha256=9iv8J-ROm_yBIwi-0oqldReLkNBFktdHRv3UCOxBzjY,377
29
29
  kscale/web/gen/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
30
  kscale/web/gen/api.py,sha256=SovcII36JFgK9jd2CXlLPMjiUROGB4vEnapOsYMUrkU,2188
31
- kscale-0.1.0.dist-info/LICENSE,sha256=HCN2bImAzUOXldAZZI7JZ9PYq6OwMlDAP_PpX1HnuN0,1071
32
- kscale-0.1.0.dist-info/METADATA,sha256=EBGeAHNhtgjRQ38M_3n_mQ1LFlaogmA3qgUsVa45zEA,2340
33
- kscale-0.1.0.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
34
- kscale-0.1.0.dist-info/entry_points.txt,sha256=N_0pCpPnwGDYVzOeuaSOrbJkS5L3lS9d8CxpJF1f8UI,62
35
- kscale-0.1.0.dist-info/top_level.txt,sha256=C2ynjYwopg6YjgttnI2dJjasyq3EKNmYp-IfQg9Xms4,7
36
- kscale-0.1.0.dist-info/RECORD,,
31
+ kscale-0.1.2.dist-info/LICENSE,sha256=HCN2bImAzUOXldAZZI7JZ9PYq6OwMlDAP_PpX1HnuN0,1071
32
+ kscale-0.1.2.dist-info/METADATA,sha256=YEcA0zqZwXvW9BnfZn51PrTXcwemCxMuolC1jB7NG-g,2340
33
+ kscale-0.1.2.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
34
+ kscale-0.1.2.dist-info/entry_points.txt,sha256=N_0pCpPnwGDYVzOeuaSOrbJkS5L3lS9d8CxpJF1f8UI,62
35
+ kscale-0.1.2.dist-info/top_level.txt,sha256=C2ynjYwopg6YjgttnI2dJjasyq3EKNmYp-IfQg9Xms4,7
36
+ kscale-0.1.2.dist-info/RECORD,,
File without changes