kscale 0.0.10__tar.gz → 0.0.13__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. {kscale-0.0.10/kscale.egg-info → kscale-0.0.13}/PKG-INFO +5 -33
  2. {kscale-0.0.10 → kscale-0.0.13}/README.md +1 -32
  3. {kscale-0.0.10 → kscale-0.0.13}/kscale/__init__.py +2 -2
  4. {kscale-0.0.10 → kscale-0.0.13}/kscale/api.py +3 -3
  5. kscale-0.0.13/kscale/cli.py +25 -0
  6. {kscale-0.0.10 → kscale-0.0.13}/kscale/conf.py +2 -2
  7. {kscale-0.0.10 → kscale-0.0.13}/kscale/requirements.txt +7 -0
  8. kscale-0.0.13/kscale/utils/checksum.py +41 -0
  9. kscale-0.0.13/kscale/utils/cli.py +28 -0
  10. {kscale-0.0.10/kscale/store → kscale-0.0.13/kscale/web}/api.py +38 -4
  11. kscale-0.0.13/kscale/web/gen/api.py +612 -0
  12. kscale-0.0.13/kscale/web/kernels.py +207 -0
  13. kscale-0.0.13/kscale/web/krec.py +175 -0
  14. {kscale-0.0.10/kscale/store → kscale-0.0.13/kscale/web}/pybullet.py +35 -27
  15. {kscale-0.0.10/kscale/store → kscale-0.0.13/kscale/web}/urdf.py +40 -48
  16. kscale-0.0.13/kscale/web/utils.py +48 -0
  17. kscale-0.0.13/kscale/web/www_client.py +134 -0
  18. {kscale-0.0.10 → kscale-0.0.13/kscale.egg-info}/PKG-INFO +5 -33
  19. {kscale-0.0.10 → kscale-0.0.13}/kscale.egg-info/SOURCES.txt +13 -9
  20. kscale-0.0.13/kscale.egg-info/entry_points.txt +2 -0
  21. {kscale-0.0.10 → kscale-0.0.13}/kscale.egg-info/requires.txt +3 -0
  22. {kscale-0.0.10 → kscale-0.0.13}/pyproject.toml +32 -6
  23. {kscale-0.0.10 → kscale-0.0.13}/setup.py +22 -2
  24. kscale-0.0.10/kscale/store/cli.py +0 -35
  25. kscale-0.0.10/kscale/store/client.py +0 -82
  26. kscale-0.0.10/kscale/store/gen/api.py +0 -301
  27. kscale-0.0.10/kscale/store/utils.py +0 -33
  28. kscale-0.0.10/kscale.egg-info/entry_points.txt +0 -2
  29. {kscale-0.0.10 → kscale-0.0.13}/LICENSE +0 -0
  30. {kscale-0.0.10 → kscale-0.0.13}/MANIFEST.in +0 -0
  31. {kscale-0.0.10 → kscale-0.0.13}/kscale/artifacts/__init__.py +0 -0
  32. {kscale-0.0.10 → kscale-0.0.13}/kscale/artifacts/plane.obj +0 -0
  33. {kscale-0.0.10 → kscale-0.0.13}/kscale/artifacts/plane.urdf +0 -0
  34. {kscale-0.0.10 → kscale-0.0.13}/kscale/py.typed +0 -0
  35. {kscale-0.0.10 → kscale-0.0.13}/kscale/requirements-dev.txt +0 -0
  36. {kscale-0.0.10/kscale/store → kscale-0.0.13/kscale/utils}/__init__.py +0 -0
  37. {kscale-0.0.10 → kscale-0.0.13}/kscale/utils/api_base.py +0 -0
  38. {kscale-0.0.10/kscale/store/gen → kscale-0.0.13/kscale/web}/__init__.py +0 -0
  39. {kscale-0.0.10/kscale/utils → kscale-0.0.13/kscale/web/gen}/__init__.py +0 -0
  40. {kscale-0.0.10 → kscale-0.0.13}/kscale.egg-info/dependency_links.txt +0 -0
  41. {kscale-0.0.10 → kscale-0.0.13}/kscale.egg-info/not-zip-safe +0 -0
  42. {kscale-0.0.10 → kscale-0.0.13}/kscale.egg-info/top_level.txt +0 -0
  43. {kscale-0.0.10 → kscale-0.0.13}/setup.cfg +0 -0
  44. {kscale-0.0.10 → kscale-0.0.13}/tests/test_dummy.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: kscale
3
- Version: 0.0.10
3
+ Version: 0.0.13
4
4
  Summary: The kscale project
5
5
  Home-page: https://github.com/kscalelabs/kscale
6
6
  Author: Benjamin Bolte
@@ -12,6 +12,9 @@ Requires-Dist: email_validator
12
12
  Requires-Dist: httpx
13
13
  Requires-Dist: requests
14
14
  Requires-Dist: pydantic
15
+ Requires-Dist: click
16
+ Requires-Dist: aiofiles
17
+ Requires-Dist: krec
15
18
  Provides-Extra: dev
16
19
  Requires-Dist: black; extra == "dev"
17
20
  Requires-Dist: darglint; extra == "dev"
@@ -43,41 +46,10 @@ Requires-Dist: datamodel-code-generator; extra == "dev"
43
46
 
44
47
  # K-Scale Command Line Interface
45
48
 
46
- This is a command line tool for interacting with various services provided by K-Scale Labs, such as:
47
-
48
- - [K-Scale Store](https://kscale.store/)
49
+ This is a command line tool for interacting with various services provided by K-Scale Labs. For more information, see the [documentation](https://docs.kscale.dev/pkg/intro).
49
50
 
50
51
  ## Installation
51
52
 
52
53
  ```bash
53
54
  pip install kscale
54
55
  ```
55
-
56
- ## Usage
57
-
58
- ### CLI
59
-
60
- Download a URDF from the K-Scale Store:
61
-
62
- ```bash
63
- kscale urdf download <artifact_id>
64
- ```
65
-
66
- Upload a URDF to the K-Scale Store:
67
-
68
- ```bash
69
- kscale urdf upload <artifact_id> <root_dir>
70
- ```
71
-
72
- ### Python API
73
-
74
- Reference a URDF by ID from the K-Scale Store:
75
-
76
- ```python
77
- from kscale import KScale
78
-
79
- async def main():
80
- kscale = KScale()
81
- urdf_dir_path = await kscale.store.urdf("123456")
82
- print(urdf_dir_path)
83
- ```
@@ -21,41 +21,10 @@
21
21
 
22
22
  # K-Scale Command Line Interface
23
23
 
24
- This is a command line tool for interacting with various services provided by K-Scale Labs, such as:
25
-
26
- - [K-Scale Store](https://kscale.store/)
24
+ This is a command line tool for interacting with various services provided by K-Scale Labs. For more information, see the [documentation](https://docs.kscale.dev/pkg/intro).
27
25
 
28
26
  ## Installation
29
27
 
30
28
  ```bash
31
29
  pip install kscale
32
30
  ```
33
-
34
- ## Usage
35
-
36
- ### CLI
37
-
38
- Download a URDF from the K-Scale Store:
39
-
40
- ```bash
41
- kscale urdf download <artifact_id>
42
- ```
43
-
44
- Upload a URDF to the K-Scale Store:
45
-
46
- ```bash
47
- kscale urdf upload <artifact_id> <root_dir>
48
- ```
49
-
50
- ### Python API
51
-
52
- Reference a URDF by ID from the K-Scale Store:
53
-
54
- ```python
55
- from kscale import KScale
56
-
57
- async def main():
58
- kscale = KScale()
59
- urdf_dir_path = await kscale.store.urdf("123456")
60
- print(urdf_dir_path)
61
- ```
@@ -1,9 +1,9 @@
1
1
  """Defines the common interface for the K-Scale Python API."""
2
2
 
3
- __version__ = "0.0.10"
3
+ __version__ = "0.0.13"
4
4
 
5
5
  from pathlib import Path
6
6
 
7
- from kscale.api import KScale
7
+ from kscale.api import K
8
8
 
9
9
  ROOT_DIR = Path(__file__).parent
@@ -1,11 +1,11 @@
1
1
  """Defines common functionality for the K-Scale API."""
2
2
 
3
- from kscale.store.api import StoreAPI
4
3
  from kscale.utils.api_base import APIBase
4
+ from kscale.web.api import WebAPI
5
5
 
6
6
 
7
- class KScale(
8
- StoreAPI,
7
+ class K(
8
+ WebAPI,
9
9
  APIBase,
10
10
  ):
11
11
  """Defines a common interface for the K-Scale API."""
@@ -0,0 +1,25 @@
1
+ """Defines the top-level KOL CLI."""
2
+
3
+ import click
4
+
5
+ from kscale.utils.cli import recursive_help
6
+ from kscale.web.kernels import cli as kernel_images_cli
7
+ from kscale.web.krec import cli as krec_cli
8
+ from kscale.web.pybullet import cli as pybullet_cli
9
+ from kscale.web.urdf import cli as urdf_cli
10
+
11
+
12
+ @click.group()
13
+ def cli() -> None:
14
+ """Command line interface for interacting with the K-Scale web API."""
15
+ pass
16
+
17
+
18
+ cli.add_command(urdf_cli, "urdf")
19
+ cli.add_command(pybullet_cli, "pybullet")
20
+ cli.add_command(kernel_images_cli, "kernel")
21
+ cli.add_command(krec_cli, "krec")
22
+
23
+ if __name__ == "__main__":
24
+ # python -m kscale.cli
25
+ print(recursive_help(cli))
@@ -16,14 +16,14 @@ def get_path() -> Path:
16
16
 
17
17
 
18
18
  @dataclass
19
- class StoreSettings:
19
+ class WWWSettings:
20
20
  api_key: str | None = field(default=None)
21
21
  cache_dir: str = field(default=II("oc.env:KSCALE_CACHE_DIR,'~/.kscale/cache/'"))
22
22
 
23
23
 
24
24
  @dataclass
25
25
  class Settings:
26
- store: StoreSettings = field(default_factory=StoreSettings)
26
+ www: WWWSettings = field(default_factory=WWWSettings)
27
27
 
28
28
  def save(self) -> None:
29
29
  (dir_path := get_path()).mkdir(parents=True, exist_ok=True)
@@ -8,3 +8,10 @@ email_validator
8
8
  httpx
9
9
  requests
10
10
  pydantic
11
+
12
+ # CLI
13
+ click
14
+ aiofiles
15
+
16
+ # K-Scale
17
+ krec
@@ -0,0 +1,41 @@
1
+ """Utility functions for file checksums."""
2
+
3
+ import hashlib
4
+ from pathlib import Path
5
+ from typing import Tuple
6
+
7
+ CHUNK_SIZE = 8192
8
+
9
+
10
+ async def calculate_sha256(file_path: str | Path) -> Tuple[str, int]:
11
+ """Calculate SHA256 checksum and size of a file.
12
+
13
+ Args:
14
+ file_path: Path to the file
15
+
16
+ Returns:
17
+ Tuple of (checksum hex string, file size in bytes)
18
+ """
19
+ sha256_hash = hashlib.sha256()
20
+ file_size = 0
21
+
22
+ with open(file_path, "rb") as f:
23
+ for chunk in iter(lambda: f.read(CHUNK_SIZE), b""):
24
+ sha256_hash.update(chunk)
25
+ file_size += len(chunk)
26
+
27
+ return sha256_hash.hexdigest(), file_size
28
+
29
+
30
+ class FileChecksum:
31
+ """Helper class for handling file checksums."""
32
+
33
+ @staticmethod
34
+ async def calculate(file_path: str | Path) -> Tuple[str, int]:
35
+ """Calculate SHA256 checksum and size of a file."""
36
+ return await calculate_sha256(file_path)
37
+
38
+ @staticmethod
39
+ def update_hash(hash_obj: "hashlib._Hash", chunk: bytes) -> None:
40
+ """Update a hash object with new data."""
41
+ hash_obj.update(chunk)
@@ -0,0 +1,28 @@
1
+ """Defines utilities for working with asyncio."""
2
+
3
+ import asyncio
4
+ import textwrap
5
+ from functools import wraps
6
+ from typing import Any, Callable, Coroutine, ParamSpec, TypeVar
7
+
8
+ import click
9
+
10
+ T = TypeVar("T")
11
+ P = ParamSpec("P")
12
+
13
+
14
+ def coro(f: Callable[P, Coroutine[Any, Any, T]]) -> Callable[P, T]:
15
+ @wraps(f)
16
+ def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
17
+ return asyncio.run(f(*args, **kwargs))
18
+
19
+ return wrapper
20
+
21
+
22
+ def recursive_help(cmd: click.Command, parent: click.Context | None = None, indent: int = 0) -> str:
23
+ ctx = click.core.Context(cmd, info_name=cmd.name, parent=parent)
24
+ help_text = cmd.get_help(ctx)
25
+ commands = getattr(cmd, "commands", {})
26
+ for sub in commands.values():
27
+ help_text += recursive_help(sub, ctx, indent + 2)
28
+ return textwrap.indent(help_text, " " * indent)
@@ -1,14 +1,15 @@
1
- """Defines a common interface for the K-Scale Store API."""
1
+ """Defines a common interface for the K-Scale WWW API."""
2
2
 
3
+ import asyncio
3
4
  from pathlib import Path
4
5
  from typing import overload
5
6
 
6
- from kscale.store.gen.api import UploadArtifactResponse
7
- from kscale.store.urdf import download_urdf, upload_urdf
8
7
  from kscale.utils.api_base import APIBase
8
+ from kscale.web.gen.api import UploadArtifactResponse
9
+ from kscale.web.urdf import download_urdf, upload_urdf
9
10
 
10
11
 
11
- class StoreAPI(APIBase):
12
+ class WebAPI(APIBase):
12
13
  def __init__(
13
14
  self,
14
15
  *,
@@ -34,6 +35,15 @@ class StoreAPI(APIBase):
34
35
  raise FileNotFoundError(f"No URDF found for artifact {artifact_id}")
35
36
  return urdf_path
36
37
 
38
+ @overload
39
+ def urdf_path_sync(self, artifact_id: str) -> Path: ...
40
+
41
+ @overload
42
+ def urdf_path_sync(self, artifact_id: str, *, throw_if_missing: bool = True) -> Path | None: ...
43
+
44
+ def urdf_path_sync(self, artifact_id: str, *, throw_if_missing: bool = True) -> Path | None:
45
+ return asyncio.run(self.urdf_path(artifact_id, throw_if_missing=throw_if_missing))
46
+
37
47
  @overload
38
48
  async def mjcf_path(self, artifact_id: str) -> Path: ...
39
49
 
@@ -47,6 +57,15 @@ class StoreAPI(APIBase):
47
57
  raise FileNotFoundError(f"No MJCF found for artifact {artifact_id}")
48
58
  return mjcf_path
49
59
 
60
+ @overload
61
+ def mjcf_path_sync(self, artifact_id: str) -> Path: ...
62
+
63
+ @overload
64
+ def mjcf_path_sync(self, artifact_id: str, *, throw_if_missing: bool = True) -> Path | None: ...
65
+
66
+ def mjcf_path_sync(self, artifact_id: str, *, throw_if_missing: bool = True) -> Path | None:
67
+ return asyncio.run(self.mjcf_path(artifact_id, throw_if_missing=throw_if_missing))
68
+
50
69
  @overload
51
70
  async def xml_path(self, artifact_id: str) -> Path: ...
52
71
 
@@ -62,3 +81,18 @@ class StoreAPI(APIBase):
62
81
 
63
82
  async def upload_urdf(self, listing_id: str, root_dir: Path) -> UploadArtifactResponse:
64
83
  return await upload_urdf(listing_id, root_dir)
84
+
85
+ def artifact_root_sync(self, artifact_id: str) -> Path:
86
+ return asyncio.run(self.artifact_root(artifact_id))
87
+
88
+ @overload
89
+ def xml_path_sync(self, artifact_id: str) -> Path: ...
90
+
91
+ @overload
92
+ def xml_path_sync(self, artifact_id: str, *, throw_if_missing: bool = True) -> Path | None: ...
93
+
94
+ def xml_path_sync(self, artifact_id: str, *, throw_if_missing: bool = True) -> Path | None:
95
+ return asyncio.run(self.xml_path(artifact_id, throw_if_missing=throw_if_missing))
96
+
97
+ def upload_urdf_sync(self, listing_id: str, root_dir: Path) -> UploadArtifactResponse:
98
+ return asyncio.run(self.upload_urdf(listing_id, root_dir))