kscale 0.0.5__tar.gz → 0.0.7__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. {kscale-0.0.5/kscale.egg-info → kscale-0.0.7}/PKG-INFO +6 -5
  2. {kscale-0.0.5 → kscale-0.0.7}/kscale/__init__.py +1 -1
  3. kscale-0.0.7/kscale/requirements.txt +10 -0
  4. {kscale-0.0.5 → kscale-0.0.7}/kscale/store/api.py +5 -1
  5. {kscale-0.0.5 → kscale-0.0.7}/kscale/store/client.py +6 -3
  6. {kscale-0.0.5 → kscale-0.0.7}/kscale/store/pybullet.py +4 -1
  7. {kscale-0.0.5 → kscale-0.0.7}/kscale/store/urdf.py +26 -9
  8. {kscale-0.0.5 → kscale-0.0.7}/kscale/store/utils.py +15 -1
  9. {kscale-0.0.5 → kscale-0.0.7/kscale.egg-info}/PKG-INFO +6 -5
  10. kscale-0.0.7/kscale.egg-info/requires.txt +13 -0
  11. kscale-0.0.5/kscale/requirements.txt +0 -9
  12. kscale-0.0.5/kscale.egg-info/requires.txt +0 -12
  13. {kscale-0.0.5 → kscale-0.0.7}/LICENSE +0 -0
  14. {kscale-0.0.5 → kscale-0.0.7}/MANIFEST.in +0 -0
  15. {kscale-0.0.5 → kscale-0.0.7}/README.md +0 -0
  16. {kscale-0.0.5 → kscale-0.0.7}/kscale/api.py +0 -0
  17. {kscale-0.0.5 → kscale-0.0.7}/kscale/conf.py +0 -0
  18. {kscale-0.0.5 → kscale-0.0.7}/kscale/py.typed +0 -0
  19. {kscale-0.0.5 → kscale-0.0.7}/kscale/requirements-dev.txt +0 -0
  20. {kscale-0.0.5 → kscale-0.0.7}/kscale/store/__init__.py +0 -0
  21. {kscale-0.0.5 → kscale-0.0.7}/kscale/store/bullet/MANIFEST.in +0 -0
  22. {kscale-0.0.5 → kscale-0.0.7}/kscale/store/cli.py +0 -0
  23. {kscale-0.0.5 → kscale-0.0.7}/kscale/store/gen/__init__.py +0 -0
  24. {kscale-0.0.5 → kscale-0.0.7}/kscale/store/gen/api.py +0 -0
  25. {kscale-0.0.5 → kscale-0.0.7}/kscale/utils/__init__.py +0 -0
  26. {kscale-0.0.5 → kscale-0.0.7}/kscale/utils/api_base.py +0 -0
  27. {kscale-0.0.5 → kscale-0.0.7}/kscale.egg-info/SOURCES.txt +0 -0
  28. {kscale-0.0.5 → kscale-0.0.7}/kscale.egg-info/dependency_links.txt +0 -0
  29. {kscale-0.0.5 → kscale-0.0.7}/kscale.egg-info/entry_points.txt +0 -0
  30. {kscale-0.0.5 → kscale-0.0.7}/kscale.egg-info/top_level.txt +0 -0
  31. {kscale-0.0.5 → kscale-0.0.7}/pyproject.toml +0 -0
  32. {kscale-0.0.5 → kscale-0.0.7}/setup.cfg +0 -0
  33. {kscale-0.0.5 → kscale-0.0.7}/setup.py +0 -0
  34. {kscale-0.0.5 → kscale-0.0.7}/tests/test_dummy.py +0 -0
@@ -1,16 +1,17 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: kscale
3
- Version: 0.0.5
3
+ Version: 0.0.7
4
4
  Summary: The kscale project
5
5
  Home-page: https://github.com/kscalelabs/kscale
6
6
  Author: Benjamin Bolte
7
7
  Requires-Python: >=3.11
8
8
  Description-Content-Type: text/markdown
9
9
  License-File: LICENSE
10
- Requires-Dist: omegaconf==2.3.0
11
- Requires-Dist: email_validator==2.2.0
12
- Requires-Dist: httpx==0.27.0
13
- Requires-Dist: requests==2.32.2
10
+ Requires-Dist: omegaconf
11
+ Requires-Dist: email_validator
12
+ Requires-Dist: httpx
13
+ Requires-Dist: requests
14
+ Requires-Dist: pydantic
14
15
  Provides-Extra: dev
15
16
  Requires-Dist: black; extra == "dev"
16
17
  Requires-Dist: darglint; extra == "dev"
@@ -1,5 +1,5 @@
1
1
  """Defines the common interface for the K-Scale Python API."""
2
2
 
3
- __version__ = "0.0.5"
3
+ __version__ = "0.0.7"
4
4
 
5
5
  from kscale.api import KScale
@@ -0,0 +1,10 @@
1
+ # requirements.txt
2
+
3
+ # Configuration
4
+ omegaconf
5
+ email_validator
6
+
7
+ # HTTP requests
8
+ httpx
9
+ requests
10
+ pydantic
@@ -2,7 +2,8 @@
2
2
 
3
3
  from pathlib import Path
4
4
 
5
- from kscale.store.urdf import download_urdf
5
+ from kscale.store.gen.api import UploadArtifactResponse
6
+ from kscale.store.urdf import download_urdf, upload_urdf
6
7
  from kscale.utils.api_base import APIBase
7
8
 
8
9
 
@@ -18,3 +19,6 @@ class StoreAPI(APIBase):
18
19
 
19
20
  async def urdf(self, artifact_id: str) -> Path:
20
21
  return await download_urdf(artifact_id)
22
+
23
+ async def upload_urdf(self, listing_id: str, root_dir: Path) -> UploadArtifactResponse:
24
+ return await upload_urdf(listing_id, root_dir)
@@ -1,6 +1,7 @@
1
1
  """Defines a typed client for the K-Scale Store API."""
2
2
 
3
3
  import logging
4
+ from pathlib import Path
4
5
  from types import TracebackType
5
6
  from typing import Any, Dict, Type
6
7
  from urllib.parse import urljoin
@@ -14,17 +15,18 @@ from kscale.store.gen.api import (
14
15
  SingleArtifactResponse,
15
16
  UploadArtifactResponse,
16
17
  )
17
- from kscale.store.utils import API_ROOT, get_api_key
18
+ from kscale.store.utils import get_api_key, get_api_root
18
19
 
19
20
  logger = logging.getLogger(__name__)
20
21
 
21
22
 
22
23
  class KScaleStoreClient:
23
- def __init__(self, base_url: str = API_ROOT) -> None:
24
+ def __init__(self, base_url: str = get_api_root()) -> None:
24
25
  self.base_url = base_url
25
26
  self.client = httpx.AsyncClient(
26
27
  base_url=self.base_url,
27
28
  headers={"Authorization": f"Bearer {get_api_key()}"},
29
+ timeout=httpx.Timeout(30.0),
28
30
  )
29
31
 
30
32
  async def _request(
@@ -55,8 +57,9 @@ class KScaleStoreClient:
55
57
  return SingleArtifactResponse(**data)
56
58
 
57
59
  async def upload_artifact(self, listing_id: str, file_path: str) -> UploadArtifactResponse:
60
+ file_name = Path(file_path).name
58
61
  with open(file_path, "rb") as f:
59
- files = {"files": (f.name, f, "application/gzip")}
62
+ files = {"files": (file_name, f, "application/gzip")}
60
63
  data = await self._request("POST", f"/artifacts/upload/{listing_id}", files=files)
61
64
  return UploadArtifactResponse(**data)
62
65
 
@@ -27,7 +27,10 @@ async def main(args: Sequence[str] | None = None) -> None:
27
27
  parsed_args = parser.parse_args(args)
28
28
 
29
29
  # Gets the URDF path.
30
- urdf_path = await download_urdf(parsed_args.listing_id)
30
+ urdf_dir = await download_urdf(parsed_args.listing_id)
31
+ urdf_path = next(urdf_dir.glob("*.urdf"), None)
32
+ if urdf_path is None:
33
+ raise ValueError(f"No URDF found in {urdf_dir}")
31
34
 
32
35
  try:
33
36
  import pybullet as p # type: ignore[import-not-found]
@@ -14,13 +14,24 @@ import requests
14
14
 
15
15
  from kscale.conf import Settings
16
16
  from kscale.store.client import KScaleStoreClient
17
- from kscale.store.gen.api import SingleArtifactResponse
17
+ from kscale.store.gen.api import SingleArtifactResponse, UploadArtifactResponse
18
18
  from kscale.store.utils import get_api_key
19
19
 
20
20
  # Set up logging
21
21
  logging.basicConfig(level=logging.INFO)
22
22
  logger = logging.getLogger(__name__)
23
23
 
24
+ ALLOWED_SUFFIXES = {
25
+ ".urdf",
26
+ ".mjcf",
27
+ ".stl",
28
+ ".obj",
29
+ ".dae",
30
+ ".png",
31
+ ".jpg",
32
+ ".jpeg",
33
+ }
34
+
24
35
 
25
36
  def get_cache_dir() -> Path:
26
37
  return Path(Settings.load().store.cache_dir).expanduser().resolve()
@@ -75,7 +86,7 @@ def create_tarball(folder_path: Path, output_filename: str, cache_dir: Path) ->
75
86
  tarball_path = cache_dir / output_filename
76
87
  with tarfile.open(tarball_path, "w:gz") as tar:
77
88
  for file_path in folder_path.rglob("*"):
78
- if file_path.is_file() and file_path.suffix.lower() in (".urdf", ".mjcf", ".stl", ".obj", ".dae"):
89
+ if file_path.is_file() and file_path.suffix.lower() in ALLOWED_SUFFIXES:
79
90
  tar.add(file_path, arcname=file_path.relative_to(folder_path))
80
91
  logger.info("Added %s to tarball", file_path)
81
92
  else:
@@ -128,18 +139,24 @@ async def remove_local_urdf(artifact_id: str) -> None:
128
139
  raise
129
140
 
130
141
 
131
- async def upload_urdf(listing_id: str, args: Sequence[str]) -> None:
132
- parser = argparse.ArgumentParser(description="K-Scale URDF Store", add_help=False)
133
- parser.add_argument("root_dir", type=Path, help="The path to the root directory to upload")
134
- parsed_args = parser.parse_args(args)
135
-
136
- root_dir = parsed_args.root_dir
142
+ async def upload_urdf(listing_id: str, root_dir: Path) -> UploadArtifactResponse:
137
143
  tarball_path = create_tarball(root_dir, "robot.tgz", get_artifact_dir(listing_id))
138
144
 
139
145
  async with KScaleStoreClient() as client:
140
146
  response = await client.upload_artifact(listing_id, str(tarball_path))
141
147
 
142
148
  logger.info("Uploaded artifacts: %s", [artifact.artifact_id for artifact in response.artifacts])
149
+ return response
150
+
151
+
152
+ async def upload_urdf_cli(listing_id: str, args: Sequence[str]) -> UploadArtifactResponse:
153
+ parser = argparse.ArgumentParser(description="K-Scale URDF Store", add_help=False)
154
+ parser.add_argument("root_dir", type=Path, help="The path to the root directory to upload")
155
+ parsed_args = parser.parse_args(args)
156
+
157
+ root_dir = parsed_args.root_dir
158
+ response = await upload_urdf(listing_id, root_dir)
159
+ return response
143
160
 
144
161
 
145
162
  Command = Literal["download", "info", "upload", "remove-local"]
@@ -165,7 +182,7 @@ async def main(args: Sequence[str] | None = None) -> None:
165
182
  await remove_local_urdf(id)
166
183
 
167
184
  case "upload":
168
- await upload_urdf(id, remaining_args)
185
+ await upload_urdf_cli(id, remaining_args)
169
186
 
170
187
  case _:
171
188
  logger.error("Invalid command")
@@ -4,10 +4,24 @@ import os
4
4
 
5
5
  from kscale.conf import Settings
6
6
 
7
- API_ROOT = "https://api.kscale.store"
7
+
8
+ def get_api_root() -> str:
9
+ """Returns the base URL for the K-Scale Store API.
10
+
11
+ This can be overridden when targetting a different server.
12
+
13
+ Returns:
14
+ The base URL for the K-Scale Store API.
15
+ """
16
+ return os.getenv("KSCALE_API_ROOT", "https://api.kscale.store")
8
17
 
9
18
 
10
19
  def get_api_key() -> str:
20
+ """Returns the API key for the K-Scale Store API.
21
+
22
+ Returns:
23
+ The API key for the K-Scale Store API.
24
+ """
11
25
  api_key = Settings.load().store.api_key
12
26
  if api_key is None:
13
27
  api_key = os.getenv("KSCALE_API_KEY")
@@ -1,16 +1,17 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: kscale
3
- Version: 0.0.5
3
+ Version: 0.0.7
4
4
  Summary: The kscale project
5
5
  Home-page: https://github.com/kscalelabs/kscale
6
6
  Author: Benjamin Bolte
7
7
  Requires-Python: >=3.11
8
8
  Description-Content-Type: text/markdown
9
9
  License-File: LICENSE
10
- Requires-Dist: omegaconf==2.3.0
11
- Requires-Dist: email_validator==2.2.0
12
- Requires-Dist: httpx==0.27.0
13
- Requires-Dist: requests==2.32.2
10
+ Requires-Dist: omegaconf
11
+ Requires-Dist: email_validator
12
+ Requires-Dist: httpx
13
+ Requires-Dist: requests
14
+ Requires-Dist: pydantic
14
15
  Provides-Extra: dev
15
16
  Requires-Dist: black; extra == "dev"
16
17
  Requires-Dist: darglint; extra == "dev"
@@ -0,0 +1,13 @@
1
+ omegaconf
2
+ email_validator
3
+ httpx
4
+ requests
5
+ pydantic
6
+
7
+ [dev]
8
+ black
9
+ darglint
10
+ mypy
11
+ pytest
12
+ ruff
13
+ datamodel-code-generator
@@ -1,9 +0,0 @@
1
- # requirements.txt
2
-
3
- # Configuration
4
- omegaconf==2.3.0
5
- email_validator==2.2.0
6
-
7
- # HTTP requests
8
- httpx==0.27.0
9
- requests==2.32.2
@@ -1,12 +0,0 @@
1
- omegaconf==2.3.0
2
- email_validator==2.2.0
3
- httpx==0.27.0
4
- requests==2.32.2
5
-
6
- [dev]
7
- black
8
- darglint
9
- mypy
10
- pytest
11
- ruff
12
- datamodel-code-generator
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes