kscale 0.0.11__tar.gz → 0.0.13__tar.gz
Sign up to get free protection for your applications and to get access to all the features.
- {kscale-0.0.11/kscale.egg-info → kscale-0.0.13}/PKG-INFO +5 -33
- {kscale-0.0.11 → kscale-0.0.13}/README.md +1 -32
- {kscale-0.0.11 → kscale-0.0.13}/kscale/__init__.py +2 -2
- {kscale-0.0.11 → kscale-0.0.13}/kscale/api.py +3 -3
- kscale-0.0.13/kscale/cli.py +25 -0
- {kscale-0.0.11 → kscale-0.0.13}/kscale/conf.py +2 -2
- {kscale-0.0.11 → kscale-0.0.13}/kscale/requirements.txt +7 -0
- kscale-0.0.13/kscale/utils/checksum.py +41 -0
- kscale-0.0.13/kscale/utils/cli.py +28 -0
- {kscale-0.0.11/kscale/store → kscale-0.0.13/kscale/web}/api.py +38 -4
- {kscale-0.0.11/kscale/store → kscale-0.0.13/kscale/web}/gen/api.py +308 -93
- kscale-0.0.13/kscale/web/kernels.py +207 -0
- kscale-0.0.13/kscale/web/krec.py +175 -0
- {kscale-0.0.11/kscale/store → kscale-0.0.13/kscale/web}/pybullet.py +35 -27
- {kscale-0.0.11/kscale/store → kscale-0.0.13/kscale/web}/urdf.py +40 -48
- kscale-0.0.13/kscale/web/utils.py +48 -0
- kscale-0.0.13/kscale/web/www_client.py +134 -0
- {kscale-0.0.11 → kscale-0.0.13/kscale.egg-info}/PKG-INFO +5 -33
- {kscale-0.0.11 → kscale-0.0.13}/kscale.egg-info/SOURCES.txt +13 -9
- kscale-0.0.13/kscale.egg-info/entry_points.txt +2 -0
- {kscale-0.0.11 → kscale-0.0.13}/kscale.egg-info/requires.txt +3 -0
- {kscale-0.0.11 → kscale-0.0.13}/pyproject.toml +32 -6
- {kscale-0.0.11 → kscale-0.0.13}/setup.py +22 -2
- kscale-0.0.11/kscale/store/cli.py +0 -35
- kscale-0.0.11/kscale/store/client.py +0 -82
- kscale-0.0.11/kscale/store/utils.py +0 -33
- kscale-0.0.11/kscale.egg-info/entry_points.txt +0 -2
- {kscale-0.0.11 → kscale-0.0.13}/LICENSE +0 -0
- {kscale-0.0.11 → kscale-0.0.13}/MANIFEST.in +0 -0
- {kscale-0.0.11 → kscale-0.0.13}/kscale/artifacts/__init__.py +0 -0
- {kscale-0.0.11 → kscale-0.0.13}/kscale/artifacts/plane.obj +0 -0
- {kscale-0.0.11 → kscale-0.0.13}/kscale/artifacts/plane.urdf +0 -0
- {kscale-0.0.11 → kscale-0.0.13}/kscale/py.typed +0 -0
- {kscale-0.0.11 → kscale-0.0.13}/kscale/requirements-dev.txt +0 -0
- {kscale-0.0.11/kscale/store → kscale-0.0.13/kscale/utils}/__init__.py +0 -0
- {kscale-0.0.11 → kscale-0.0.13}/kscale/utils/api_base.py +0 -0
- {kscale-0.0.11/kscale/store/gen → kscale-0.0.13/kscale/web}/__init__.py +0 -0
- {kscale-0.0.11/kscale/utils → kscale-0.0.13/kscale/web/gen}/__init__.py +0 -0
- {kscale-0.0.11 → kscale-0.0.13}/kscale.egg-info/dependency_links.txt +0 -0
- {kscale-0.0.11 → kscale-0.0.13}/kscale.egg-info/not-zip-safe +0 -0
- {kscale-0.0.11 → kscale-0.0.13}/kscale.egg-info/top_level.txt +0 -0
- {kscale-0.0.11 → kscale-0.0.13}/setup.cfg +0 -0
- {kscale-0.0.11 → 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.
|
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,
|
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,
|
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,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
|
8
|
-
|
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
|
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
|
-
|
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)
|
@@ -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
|
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
|
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))
|