kleinkram 0.36.2__py3-none-any.whl → 0.36.2.dev20241118065826__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.

Potentially problematic release.


This version of kleinkram might be problematic. Click here for more details.

Files changed (48) hide show
  1. kleinkram/__init__.py +6 -0
  2. kleinkram/__main__.py +6 -0
  3. kleinkram/_version.py +6 -0
  4. kleinkram/api/__init__.py +0 -0
  5. kleinkram/api/client.py +65 -0
  6. kleinkram/api/file_transfer.py +328 -0
  7. kleinkram/api/routes.py +460 -0
  8. kleinkram/app.py +180 -0
  9. kleinkram/auth.py +96 -0
  10. kleinkram/commands/__init__.py +1 -0
  11. kleinkram/commands/download.py +103 -0
  12. kleinkram/commands/endpoint.py +62 -0
  13. kleinkram/commands/list.py +93 -0
  14. kleinkram/commands/mission.py +57 -0
  15. kleinkram/commands/project.py +24 -0
  16. kleinkram/commands/upload.py +138 -0
  17. kleinkram/commands/verify.py +117 -0
  18. kleinkram/config.py +171 -0
  19. kleinkram/consts.py +8 -1
  20. kleinkram/core.py +14 -0
  21. kleinkram/enums.py +10 -0
  22. kleinkram/errors.py +59 -0
  23. kleinkram/main.py +6 -484
  24. kleinkram/models.py +186 -0
  25. kleinkram/utils.py +179 -0
  26. {kleinkram-0.36.2.dist-info/licenses → kleinkram-0.36.2.dev20241118065826.dist-info}/LICENSE +1 -1
  27. kleinkram-0.36.2.dev20241118065826.dist-info/METADATA +113 -0
  28. kleinkram-0.36.2.dev20241118065826.dist-info/RECORD +33 -0
  29. {kleinkram-0.36.2.dist-info → kleinkram-0.36.2.dev20241118065826.dist-info}/WHEEL +2 -1
  30. kleinkram-0.36.2.dev20241118065826.dist-info/entry_points.txt +2 -0
  31. kleinkram-0.36.2.dev20241118065826.dist-info/top_level.txt +2 -0
  32. tests/__init__.py +0 -0
  33. tests/test_utils.py +153 -0
  34. kleinkram/api_client.py +0 -63
  35. kleinkram/auth/auth.py +0 -160
  36. kleinkram/endpoint/endpoint.py +0 -58
  37. kleinkram/error_handling.py +0 -177
  38. kleinkram/file/file.py +0 -144
  39. kleinkram/helper.py +0 -272
  40. kleinkram/mission/mission.py +0 -310
  41. kleinkram/project/project.py +0 -138
  42. kleinkram/queue/queue.py +0 -8
  43. kleinkram/tag/tag.py +0 -71
  44. kleinkram/topic/topic.py +0 -55
  45. kleinkram/user/user.py +0 -75
  46. kleinkram-0.36.2.dist-info/METADATA +0 -25
  47. kleinkram-0.36.2.dist-info/RECORD +0 -20
  48. kleinkram-0.36.2.dist-info/entry_points.txt +0 -2
kleinkram/config.py ADDED
@@ -0,0 +1,171 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import os
5
+ import tempfile
6
+ from dataclasses import dataclass
7
+ from enum import Enum
8
+ from pathlib import Path
9
+ from typing import Dict
10
+ from typing import NamedTuple
11
+ from typing import Optional
12
+
13
+ from kleinkram._version import __local__
14
+ from kleinkram._version import __version__
15
+ from kleinkram.errors import CorruptedConfigFile
16
+ from kleinkram.errors import InvalidConfigFile
17
+
18
+ CONFIG_PATH = Path().home() / ".kleinkram.json"
19
+
20
+
21
+ class Environment(Enum):
22
+ LOCAL = "local"
23
+ DEV = "dev"
24
+ PROD = "prod"
25
+
26
+
27
+ DEFAULT_API = {
28
+ Environment.LOCAL: "http://localhost:3000",
29
+ Environment.DEV: "https://api.datasets.dev.leggedrobotics.com",
30
+ Environment.PROD: "https://api.datasets.leggedrobotics.com",
31
+ }
32
+
33
+ LOCAL_S3 = "http://localhost:9000"
34
+
35
+
36
+ def get_env() -> Environment:
37
+ if __local__:
38
+ return Environment.LOCAL
39
+ if "dev" in __version__:
40
+ return Environment.DEV
41
+ return Environment.PROD
42
+
43
+
44
+ def get_default_endpoints() -> str:
45
+ env = get_env()
46
+ return DEFAULT_API[env]
47
+
48
+
49
+ class Credentials(NamedTuple):
50
+ auth_token: Optional[str] = None
51
+ refresh_token: Optional[str] = None
52
+ cli_key: Optional[str] = None
53
+
54
+
55
+ JSON_ENDPOINT_KEY = "endpoint"
56
+ JSON_CREDENTIALS_KEY = "credentials"
57
+
58
+
59
+ class Config:
60
+ endpoint: str
61
+ credentials: Dict[str, Credentials]
62
+
63
+ def __init__(self, overwrite: bool = False) -> None:
64
+ default_endpoint = get_default_endpoints()
65
+
66
+ self.credentials = {}
67
+ self.endpoint = default_endpoint
68
+
69
+ if not CONFIG_PATH.exists():
70
+ self.save()
71
+
72
+ try:
73
+ self._read_config()
74
+ except (InvalidConfigFile, CorruptedConfigFile):
75
+ if not overwrite:
76
+ self.credentials = {}
77
+ self.endpoint = default_endpoint
78
+ self.save()
79
+ else:
80
+ raise
81
+
82
+ def _read_config(self) -> None:
83
+ with open(CONFIG_PATH, "r") as file:
84
+ try:
85
+ content = json.load(file)
86
+ except Exception:
87
+ raise CorruptedConfigFile
88
+
89
+ endpoint = content.get(JSON_ENDPOINT_KEY, None)
90
+ if not isinstance(endpoint, str):
91
+ raise InvalidConfigFile
92
+
93
+ credentials = content.get(JSON_CREDENTIALS_KEY, None)
94
+ if not isinstance(credentials, dict):
95
+ raise InvalidConfigFile
96
+
97
+ try:
98
+ parsed_creds = {}
99
+ for ep, creds in credentials.items():
100
+ parsed_creds[ep] = Credentials(**creds)
101
+ except Exception:
102
+ raise InvalidConfigFile
103
+
104
+ self.endpoint = endpoint
105
+ self.credentials = parsed_creds
106
+
107
+ @property
108
+ def has_cli_key(self) -> bool:
109
+ if self.endpoint not in self.credentials:
110
+ return False
111
+ return self.credentials[self.endpoint].cli_key is not None
112
+
113
+ @property
114
+ def has_refresh_token(self) -> bool:
115
+ if self.endpoint not in self.credentials:
116
+ return False
117
+ return self.credentials[self.endpoint].refresh_token is not None
118
+
119
+ @property
120
+ def auth_token(self) -> Optional[str]:
121
+ return self.credentials[self.endpoint].auth_token
122
+
123
+ @property
124
+ def refresh_token(self) -> Optional[str]:
125
+ return self.credentials[self.endpoint].refresh_token
126
+
127
+ @property
128
+ def cli_key(self) -> Optional[str]:
129
+ return self.credentials[self.endpoint].cli_key
130
+
131
+ def save(self) -> None:
132
+ serialized_tokens = {}
133
+ for endpoint, auth in self.credentials.items():
134
+ serialized_tokens[endpoint] = auth._asdict()
135
+
136
+ data = {
137
+ JSON_ENDPOINT_KEY: self.endpoint,
138
+ JSON_CREDENTIALS_KEY: serialized_tokens,
139
+ }
140
+
141
+ # atomically write to file
142
+ fd, tmp_path = tempfile.mkstemp()
143
+ with open(fd, "w") as file:
144
+ json.dump(data, file)
145
+
146
+ os.replace(tmp_path, CONFIG_PATH)
147
+
148
+ def clear_credentials(self, all: bool = False) -> None:
149
+ if all:
150
+ self.credentials = {}
151
+ elif self.endpoint in self.credentials:
152
+ del self.credentials[self.endpoint]
153
+ self.save()
154
+
155
+ def save_credentials(self, creds: Credentials) -> None:
156
+ self.credentials[self.endpoint] = creds
157
+ self.save()
158
+
159
+
160
+ @dataclass
161
+ class _SharedState:
162
+ log_file: Optional[Path] = None
163
+ verbose: bool = True
164
+ debug: bool = False
165
+
166
+
167
+ SHARED_STATE = _SharedState()
168
+
169
+
170
+ def get_shared_state() -> _SharedState:
171
+ return SHARED_STATE
kleinkram/consts.py CHANGED
@@ -1 +1,8 @@
1
- API_URL='https://api.datasets.leggedrobotics.com'
1
+ from __future__ import annotations
2
+
3
+ LOCAL_API_URL = "http://localhost:3000"
4
+ LOCAL_S3_URL = "http://localhost:9000"
5
+
6
+
7
+ API_URL = "http://localhost:3000"
8
+ # API_URL = "https://api.datasets.leggedrobotics.com"
kleinkram/core.py ADDED
@@ -0,0 +1,14 @@
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+ from typing import List
5
+ from uuid import UUID
6
+
7
+
8
+ def upload() -> None: ...
9
+
10
+
11
+ def download() -> None: ...
12
+
13
+
14
+ def download_file(ids: List[UUID], dest: Path) -> None: ...
kleinkram/enums.py ADDED
@@ -0,0 +1,10 @@
1
+ from __future__ import annotations
2
+
3
+ from enum import Enum
4
+
5
+
6
+ class PermissionLevel(Enum):
7
+ READ = 0
8
+ CREATE = 10
9
+ WRITE = 20
10
+ DELETE = 30
kleinkram/errors.py ADDED
@@ -0,0 +1,59 @@
1
+ from __future__ import annotations
2
+
3
+ LOGIN_MESSAGE = "Please login using `klein login`."
4
+
5
+
6
+ class InvalidMissionSpec(Exception): ...
7
+
8
+
9
+ class InvalidFileSpec(Exception): ...
10
+
11
+
12
+ class MissionExists(Exception): ...
13
+
14
+
15
+ class MissionDoesNotExist(Exception): ...
16
+
17
+
18
+ class NoPermission(Exception): ...
19
+
20
+
21
+ class AccessDeniedException(Exception):
22
+ def __init__(self, message: str, api_error: str):
23
+ self.message = message
24
+ self.api_error = api_error
25
+
26
+
27
+ class NotAuthenticatedException(Exception):
28
+ def __init__(self, endpoint: str):
29
+ message = (
30
+ f"You are not authenticated on endpoint '{endpoint}'.\n{LOGIN_MESSAGE}"
31
+ )
32
+ super().__init__(message)
33
+
34
+
35
+ class CorruptedFile(Exception): ...
36
+
37
+
38
+ class NameIsValidUUID(Exception): ...
39
+
40
+
41
+ class UploadFailed(Exception): ...
42
+
43
+
44
+ class InvalidCLIVersion(Exception): ...
45
+
46
+
47
+ class FileTypeNotSupported(Exception): ...
48
+
49
+
50
+ class InvalidConfigFile(Exception):
51
+ def __init__(self) -> None:
52
+ super().__init__("Invalid config file.")
53
+
54
+
55
+ class CorruptedConfigFile(Exception):
56
+ def __init__(self) -> None:
57
+ super().__init__(
58
+ "Config file is corrupted.\nPlease run `klein login` to re-authenticate."
59
+ )