kscale 0.0.11__py3-none-any.whl → 0.1.0__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.
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: kscale
3
- Version: 0.0.11
3
+ Version: 0.1.0
4
4
  Summary: The kscale project
5
5
  Home-page: https://github.com/kscalelabs/kscale
6
6
  Author: Benjamin Bolte
@@ -8,23 +8,35 @@ Requires-Python: >=3.11
8
8
  Description-Content-Type: text/markdown
9
9
  License-File: LICENSE
10
10
  Requires-Dist: omegaconf
11
- Requires-Dist: email-validator
11
+ Requires-Dist: email_validator
12
+ Requires-Dist: colorlogging
13
+ Requires-Dist: aiohttp
14
+ Requires-Dist: cryptography
12
15
  Requires-Dist: httpx
13
- Requires-Dist: requests
14
16
  Requires-Dist: pydantic
17
+ Requires-Dist: pyjwt
18
+ Requires-Dist: requests
19
+ Requires-Dist: yarl
20
+ Requires-Dist: aiofiles
21
+ Requires-Dist: click
22
+ Requires-Dist: tabulate
23
+ Requires-Dist: async-lru
24
+ Requires-Dist: krec
15
25
  Provides-Extra: dev
16
- Requires-Dist: black ; extra == 'dev'
17
- Requires-Dist: darglint ; extra == 'dev'
18
- Requires-Dist: mypy ; extra == 'dev'
19
- Requires-Dist: pytest ; extra == 'dev'
20
- Requires-Dist: ruff ; extra == 'dev'
21
- Requires-Dist: datamodel-code-generator ; extra == 'dev'
22
-
23
- <p align="center">
24
- <picture>
25
- <img alt="K-Scale Open Source Robotics" src="https://media.kscale.dev/kscale-open-source-header.png" style="max-width: 100%;">
26
- </picture>
27
- </p>
26
+ Requires-Dist: black; extra == "dev"
27
+ Requires-Dist: darglint; extra == "dev"
28
+ Requires-Dist: mypy; extra == "dev"
29
+ Requires-Dist: pytest; extra == "dev"
30
+ Requires-Dist: ruff; extra == "dev"
31
+ Requires-Dist: datamodel-code-generator; extra == "dev"
32
+ Dynamic: author
33
+ Dynamic: description
34
+ Dynamic: description-content-type
35
+ Dynamic: home-page
36
+ Dynamic: provides-extra
37
+ Dynamic: requires-dist
38
+ Dynamic: requires-python
39
+ Dynamic: summary
28
40
 
29
41
  <div align="center">
30
42
 
@@ -43,41 +55,10 @@ Requires-Dist: datamodel-code-generator ; extra == 'dev'
43
55
 
44
56
  # K-Scale Command Line Interface
45
57
 
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/)
58
+ 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
59
 
50
60
  ## Installation
51
61
 
52
62
  ```bash
53
63
  pip install kscale
54
64
  ```
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
- ```
@@ -0,0 +1,36 @@
1
+ kscale/__init__.py,sha256=2jGtLxds4EdX8FEGT3yA74b0VgrkOEBx_PxiWvoh924,172
2
+ kscale/api.py,sha256=jmiuFurTN_Gj_-k-6asqxw8wp-_bgJUXgMPFgJ4lqHA,230
3
+ kscale/cli.py,sha256=PMHLKR5UwdbbReVmqHXpJ-K9-mGHv_0I7KQkwxmFcUA,881
4
+ kscale/conf.py,sha256=i5gDTs8D73l2OvX4pOlPWtT2PC26MmoKw3PPwf8HM7U,1526
5
+ kscale/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ kscale/requirements-dev.txt,sha256=WI7-ea4IRJakmqVMN8QKhOsDGrghwtvk03aIsFaNSIw,130
7
+ kscale/requirements.txt,sha256=lKDpyebp9nHZ2uHIuS6FQ6SAg7YO_0sDyb67MFmP4h4,214
8
+ kscale/artifacts/__init__.py,sha256=RK8wdybtCJPgdLLJ8R8-YMi1Ph5ojqAKVJZowHONtgo,232
9
+ kscale/artifacts/plane.obj,sha256=x59-IIrWpLjhotChiqT2Ul6U8s0RcHkaEeUZb4KXL1c,348
10
+ kscale/artifacts/plane.urdf,sha256=LCiTk14AyTHjkZ1jvsb0hNaEaJUxDb8Z1JjsgpXu3YM,819
11
+ kscale/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
+ kscale/utils/api_base.py,sha256=Kk_WtRDdJHmOg6NtHmVxVrcfARSUkhfr29ypLch_pO0,112
13
+ kscale/utils/checksum.py,sha256=jt6QmmQND9zrOEnUtOfZpLYROhgto4Gh3OpdUWk4tZA,1093
14
+ kscale/utils/cli.py,sha256=JoaY5x5SdUx97KmMM9j5AjRRUqqrTlJ9qVckZptEsYA,827
15
+ kscale/web/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
+ kscale/web/api.py,sha256=YioIdruq7LCKSBf9SvOGkv914W36_zBmpTzsJqKc0wE,439
17
+ kscale/web/utils.py,sha256=KFB9lrgn_2BRY38Sfbb_QOKZ8fWyINqIqLPwN0rjbyk,806
18
+ kscale/web/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
+ kscale/web/cli/robot.py,sha256=rI-A4_0uvJPeA71Apl4Z3mV5fIfWkgmzT9JRmJYxz3A,3307
20
+ kscale/web/cli/robot_class.py,sha256=ymC5phUqofvOXv5P6f51b9lMK5eRDaavvnzS0x9rDbU,3574
21
+ kscale/web/cli/token.py,sha256=1rFC8MYKtqbNsQa2KIqwW1tqpaMtFaxuNsallwejXTU,787
22
+ kscale/web/cli/user.py,sha256=qO0z2K5uA48hEiOOYEzv6BO2nOlCpITTDZFuiNl6d34,817
23
+ kscale/web/clients/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
+ kscale/web/clients/base.py,sha256=NRqRH2JTXjJLUMDM93txKgwFu9bQRYhCYLYy7vE76Ik,11462
25
+ kscale/web/clients/client.py,sha256=QjBicdHQYNoUG9XRjAYmGu3THae9DzWa_hQox3OO1Gw,214
26
+ kscale/web/clients/robot.py,sha256=HMfJnkDxaJ_o7X2vdYYS9iob1JRoBG2qiGmQpCQZpAk,1485
27
+ kscale/web/clients/robot_class.py,sha256=yC0G4pVPDoEskf0QfQLxejyDgHLT8gR1RLJ8ioCGoOM,4236
28
+ kscale/web/clients/user.py,sha256=9iv8J-ROm_yBIwi-0oqldReLkNBFktdHRv3UCOxBzjY,377
29
+ kscale/web/gen/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
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,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.2.0)
2
+ Generator: setuptools (75.8.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ ks = kscale.cli:cli
3
+ kscale = kscale.cli:cli
kscale/store/api.py DELETED
@@ -1,64 +0,0 @@
1
- """Defines a common interface for the K-Scale Store API."""
2
-
3
- from pathlib import Path
4
- from typing import overload
5
-
6
- from kscale.store.gen.api import UploadArtifactResponse
7
- from kscale.store.urdf import download_urdf, upload_urdf
8
- from kscale.utils.api_base import APIBase
9
-
10
-
11
- class StoreAPI(APIBase):
12
- def __init__(
13
- self,
14
- *,
15
- api_key: str | None = None,
16
- ) -> None:
17
- super().__init__()
18
-
19
- self.api_key = api_key
20
-
21
- async def artifact_root(self, artifact_id: str) -> Path:
22
- return await download_urdf(artifact_id)
23
-
24
- @overload
25
- async def urdf_path(self, artifact_id: str) -> Path: ...
26
-
27
- @overload
28
- async def urdf_path(self, artifact_id: str, *, throw_if_missing: bool = True) -> Path | None: ...
29
-
30
- async def urdf_path(self, artifact_id: str, *, throw_if_missing: bool = True) -> Path | None:
31
- root_dir = await self.artifact_root(artifact_id)
32
- urdf_path = next(root_dir.glob("*.urdf"), None)
33
- if urdf_path is None and throw_if_missing:
34
- raise FileNotFoundError(f"No URDF found for artifact {artifact_id}")
35
- return urdf_path
36
-
37
- @overload
38
- async def mjcf_path(self, artifact_id: str) -> Path: ...
39
-
40
- @overload
41
- async def mjcf_path(self, artifact_id: str, *, throw_if_missing: bool = True) -> Path | None: ...
42
-
43
- async def mjcf_path(self, artifact_id: str, *, throw_if_missing: bool = True) -> Path | None:
44
- root_dir = await self.artifact_root(artifact_id)
45
- mjcf_path = next(root_dir.glob("*.mjcf"), None)
46
- if mjcf_path is None and throw_if_missing:
47
- raise FileNotFoundError(f"No MJCF found for artifact {artifact_id}")
48
- return mjcf_path
49
-
50
- @overload
51
- async def xml_path(self, artifact_id: str) -> Path: ...
52
-
53
- @overload
54
- async def xml_path(self, artifact_id: str, *, throw_if_missing: bool = True) -> Path | None: ...
55
-
56
- async def xml_path(self, artifact_id: str, *, throw_if_missing: bool = True) -> Path | None:
57
- root_dir = await self.artifact_root(artifact_id)
58
- xml_path = next(root_dir.glob("*.xml"), None)
59
- if xml_path is None and throw_if_missing:
60
- raise FileNotFoundError(f"No XML found for artifact {artifact_id}")
61
- return xml_path
62
-
63
- async def upload_urdf(self, listing_id: str, root_dir: Path) -> UploadArtifactResponse:
64
- return await upload_urdf(listing_id, root_dir)
kscale/store/cli.py DELETED
@@ -1,35 +0,0 @@
1
- """Defines the top-level KOL CLI."""
2
-
3
- import argparse
4
- import asyncio
5
- from typing import Sequence
6
-
7
- from kscale.store import pybullet, urdf
8
-
9
-
10
- async def main(args: Sequence[str] | None = None) -> None:
11
- parser = argparse.ArgumentParser(description="K-Scale OnShape Library", add_help=False)
12
- parser.add_argument(
13
- "subcommand",
14
- choices=[
15
- "urdf",
16
- "pybullet",
17
- ],
18
- help="The subcommand to run",
19
- )
20
- parsed_args, remaining_args = parser.parse_known_args(args)
21
-
22
- match parsed_args.subcommand:
23
- case "urdf":
24
- await urdf.main(remaining_args)
25
- case "pybullet":
26
- await pybullet.main(remaining_args)
27
-
28
-
29
- def sync_main(args: Sequence[str] | None = None) -> None:
30
- asyncio.run(main(args))
31
-
32
-
33
- if __name__ == "__main__":
34
- # python3 -m kscale.store.cli
35
- sync_main()
kscale/store/client.py DELETED
@@ -1,82 +0,0 @@
1
- """Defines a typed client for the K-Scale Store API."""
2
-
3
- import logging
4
- from pathlib import Path
5
- from types import TracebackType
6
- from typing import Any, Dict, Type
7
- from urllib.parse import urljoin
8
-
9
- import httpx
10
- from pydantic import BaseModel
11
-
12
- from kscale.store.gen.api import (
13
- NewListingRequest,
14
- NewListingResponse,
15
- SingleArtifactResponse,
16
- UploadArtifactResponse,
17
- )
18
- from kscale.store.utils import get_api_key, get_api_root
19
-
20
- logger = logging.getLogger(__name__)
21
-
22
-
23
- class KScaleStoreClient:
24
- def __init__(self, base_url: str = get_api_root()) -> None:
25
- self.base_url = base_url
26
- self.client = httpx.AsyncClient(
27
- base_url=self.base_url,
28
- headers={"Authorization": f"Bearer {get_api_key()}"},
29
- timeout=httpx.Timeout(30.0),
30
- )
31
-
32
- async def _request(
33
- self,
34
- method: str,
35
- endpoint: str,
36
- *,
37
- params: Dict[str, Any] | None = None,
38
- data: BaseModel | None = None,
39
- files: Dict[str, Any] | None = None,
40
- ) -> Dict[str, Any]:
41
- url = urljoin(self.base_url, endpoint)
42
- kwargs: Dict[str, Any] = {"params": params}
43
-
44
- if data:
45
- kwargs["json"] = data.dict(exclude_unset=True)
46
- if files:
47
- kwargs["files"] = files
48
-
49
- response = await self.client.request(method, url, **kwargs)
50
- if response.is_error:
51
- logger.error(f"Error response from K-Scale Store: {response.text}")
52
- response.raise_for_status()
53
- return response.json()
54
-
55
- async def get_artifact_info(self, artifact_id: str) -> SingleArtifactResponse:
56
- data = await self._request("GET", f"/artifacts/info/{artifact_id}")
57
- return SingleArtifactResponse(**data)
58
-
59
- async def upload_artifact(self, listing_id: str, file_path: str) -> UploadArtifactResponse:
60
- file_name = Path(file_path).name
61
- with open(file_path, "rb") as f:
62
- files = {"files": (file_name, f, "application/gzip")}
63
- data = await self._request("POST", f"/artifacts/upload/{listing_id}", files=files)
64
- return UploadArtifactResponse(**data)
65
-
66
- async def create_listing(self, request: NewListingRequest) -> NewListingResponse:
67
- data = await self._request("POST", "/listings", data=request)
68
- return NewListingResponse(**data)
69
-
70
- async def close(self) -> None:
71
- await self.client.aclose()
72
-
73
- async def __aenter__(self) -> "KScaleStoreClient":
74
- return self
75
-
76
- async def __aexit__(
77
- self,
78
- exc_type: Type[BaseException] | None,
79
- exc_val: BaseException | None,
80
- exc_tb: TracebackType | None,
81
- ) -> None:
82
- await self.close()