kubetorch 0.0.1__tar.gz β 0.2.1__tar.gz
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 kubetorch might be problematic. Click here for more details.
- kubetorch-0.2.1/PKG-INFO +121 -0
- kubetorch-0.2.1/README.md +66 -0
- kubetorch-0.2.1/kubetorch/__init__.py +60 -0
- kubetorch-0.2.1/kubetorch/cli.py +1985 -0
- kubetorch-0.2.1/kubetorch/cli_utils.py +1025 -0
- kubetorch-0.2.1/kubetorch/config.py +453 -0
- kubetorch-0.2.1/kubetorch/constants.py +18 -0
- kubetorch-0.2.1/kubetorch/docs/Makefile +18 -0
- kubetorch-0.2.1/kubetorch/docs/__init__.py +0 -0
- kubetorch-0.2.1/kubetorch/docs/_ext/json_globaltoc.py +42 -0
- kubetorch-0.2.1/kubetorch/docs/api/cli.rst +10 -0
- kubetorch-0.2.1/kubetorch/docs/api/python/app.rst +21 -0
- kubetorch-0.2.1/kubetorch/docs/api/python/cls.rst +19 -0
- kubetorch-0.2.1/kubetorch/docs/api/python/compute.rst +25 -0
- kubetorch-0.2.1/kubetorch/docs/api/python/config.rst +11 -0
- kubetorch-0.2.1/kubetorch/docs/api/python/fn.rst +19 -0
- kubetorch-0.2.1/kubetorch/docs/api/python/image.rst +14 -0
- kubetorch-0.2.1/kubetorch/docs/api/python/secret.rst +18 -0
- kubetorch-0.2.1/kubetorch/docs/api/python/volumes.rst +13 -0
- kubetorch-0.2.1/kubetorch/docs/api/python.rst +101 -0
- kubetorch-0.2.1/kubetorch/docs/conf.py +69 -0
- kubetorch-0.2.1/kubetorch/docs/index.rst +20 -0
- kubetorch-0.2.1/kubetorch/docs/requirements.txt +5 -0
- kubetorch-0.2.1/kubetorch/globals.py +285 -0
- kubetorch-0.2.1/kubetorch/logger.py +59 -0
- kubetorch-0.2.1/kubetorch/resources/__init__.py +0 -0
- kubetorch-0.2.1/kubetorch/resources/callables/__init__.py +0 -0
- kubetorch-0.2.1/kubetorch/resources/callables/cls/__init__.py +0 -0
- kubetorch-0.2.1/kubetorch/resources/callables/cls/cls.py +157 -0
- kubetorch-0.2.1/kubetorch/resources/callables/fn/__init__.py +0 -0
- kubetorch-0.2.1/kubetorch/resources/callables/fn/fn.py +133 -0
- kubetorch-0.2.1/kubetorch/resources/callables/module.py +1416 -0
- kubetorch-0.2.1/kubetorch/resources/callables/utils.py +174 -0
- kubetorch-0.2.1/kubetorch/resources/compute/__init__.py +0 -0
- kubetorch-0.2.1/kubetorch/resources/compute/app.py +261 -0
- kubetorch-0.2.1/kubetorch/resources/compute/compute.py +2596 -0
- kubetorch-0.2.1/kubetorch/resources/compute/decorators.py +139 -0
- kubetorch-0.2.1/kubetorch/resources/compute/rbac.py +74 -0
- kubetorch-0.2.1/kubetorch/resources/compute/utils.py +1114 -0
- kubetorch-0.2.1/kubetorch/resources/compute/websocket.py +137 -0
- kubetorch-0.2.1/kubetorch/resources/images/__init__.py +1 -0
- kubetorch-0.2.1/kubetorch/resources/images/image.py +414 -0
- kubetorch-0.2.1/kubetorch/resources/images/images.py +74 -0
- kubetorch-0.2.1/kubetorch/resources/secrets/__init__.py +2 -0
- kubetorch-0.2.1/kubetorch/resources/secrets/kubernetes_secrets_client.py +412 -0
- kubetorch-0.2.1/kubetorch/resources/secrets/provider_secrets/__init__.py +0 -0
- kubetorch-0.2.1/kubetorch/resources/secrets/provider_secrets/anthropic_secret.py +12 -0
- kubetorch-0.2.1/kubetorch/resources/secrets/provider_secrets/aws_secret.py +16 -0
- kubetorch-0.2.1/kubetorch/resources/secrets/provider_secrets/azure_secret.py +14 -0
- kubetorch-0.2.1/kubetorch/resources/secrets/provider_secrets/cohere_secret.py +12 -0
- kubetorch-0.2.1/kubetorch/resources/secrets/provider_secrets/gcp_secret.py +16 -0
- kubetorch-0.2.1/kubetorch/resources/secrets/provider_secrets/github_secret.py +13 -0
- kubetorch-0.2.1/kubetorch/resources/secrets/provider_secrets/huggingface_secret.py +20 -0
- kubetorch-0.2.1/kubetorch/resources/secrets/provider_secrets/kubeconfig_secret.py +12 -0
- kubetorch-0.2.1/kubetorch/resources/secrets/provider_secrets/lambda_secret.py +13 -0
- kubetorch-0.2.1/kubetorch/resources/secrets/provider_secrets/langchain_secret.py +12 -0
- kubetorch-0.2.1/kubetorch/resources/secrets/provider_secrets/openai_secret.py +11 -0
- kubetorch-0.2.1/kubetorch/resources/secrets/provider_secrets/pinecone_secret.py +12 -0
- kubetorch-0.2.1/kubetorch/resources/secrets/provider_secrets/providers.py +93 -0
- kubetorch-0.2.1/kubetorch/resources/secrets/provider_secrets/ssh_secret.py +12 -0
- kubetorch-0.2.1/kubetorch/resources/secrets/provider_secrets/wandb_secret.py +11 -0
- kubetorch-0.2.1/kubetorch/resources/secrets/secret.py +238 -0
- kubetorch-0.2.1/kubetorch/resources/secrets/secret_factory.py +70 -0
- kubetorch-0.2.1/kubetorch/resources/secrets/utils.py +209 -0
- kubetorch-0.2.1/kubetorch/resources/volumes/__init__.py +0 -0
- kubetorch-0.2.1/kubetorch/resources/volumes/volume.py +365 -0
- kubetorch-0.2.1/kubetorch/servers/__init__.py +0 -0
- kubetorch-0.2.1/kubetorch/servers/http/__init__.py +0 -0
- kubetorch-0.2.1/kubetorch/servers/http/distributed_utils.py +3223 -0
- kubetorch-0.2.1/kubetorch/servers/http/http_client.py +730 -0
- kubetorch-0.2.1/kubetorch/servers/http/http_server.py +1788 -0
- kubetorch-0.2.1/kubetorch/servers/http/server_metrics.py +278 -0
- kubetorch-0.2.1/kubetorch/servers/http/utils.py +728 -0
- kubetorch-0.2.1/kubetorch/serving/__init__.py +0 -0
- kubetorch-0.2.1/kubetorch/serving/autoscaling.py +173 -0
- kubetorch-0.2.1/kubetorch/serving/base_service_manager.py +363 -0
- kubetorch-0.2.1/kubetorch/serving/constants.py +83 -0
- kubetorch-0.2.1/kubetorch/serving/deployment_service_manager.py +478 -0
- kubetorch-0.2.1/kubetorch/serving/knative_service_manager.py +519 -0
- kubetorch-0.2.1/kubetorch/serving/raycluster_service_manager.py +582 -0
- kubetorch-0.2.1/kubetorch/serving/service_manager.py +18 -0
- kubetorch-0.2.1/kubetorch/serving/templates/deployment_template.yaml +17 -0
- kubetorch-0.2.1/kubetorch/serving/templates/knative_service_template.yaml +19 -0
- kubetorch-0.2.1/kubetorch/serving/templates/kt_setup_template.sh.j2 +81 -0
- kubetorch-0.2.1/kubetorch/serving/templates/pod_template.yaml +194 -0
- kubetorch-0.2.1/kubetorch/serving/templates/raycluster_service_template.yaml +42 -0
- kubetorch-0.2.1/kubetorch/serving/templates/raycluster_template.yaml +35 -0
- kubetorch-0.2.1/kubetorch/serving/templates/service_template.yaml +21 -0
- kubetorch-0.2.1/kubetorch/serving/templates/workerset_template.yaml +36 -0
- kubetorch-0.2.1/kubetorch/serving/utils.py +377 -0
- kubetorch-0.2.1/kubetorch/utils.py +263 -0
- kubetorch-0.2.1/pyproject.toml +100 -0
- kubetorch-0.0.1/PKG-INFO +0 -9
- kubetorch-0.0.1/kubetorch.egg-info/PKG-INFO +0 -9
- kubetorch-0.0.1/kubetorch.egg-info/SOURCES.txt +0 -5
- kubetorch-0.0.1/kubetorch.egg-info/dependency_links.txt +0 -1
- kubetorch-0.0.1/kubetorch.egg-info/top_level.txt +0 -1
- kubetorch-0.0.1/setup.cfg +0 -4
- kubetorch-0.0.1/setup.py +0 -15
kubetorch-0.2.1/PKG-INFO
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: kubetorch
|
|
3
|
+
Version: 0.2.1
|
|
4
|
+
Summary: A Python interface for running ML workloads on Kubernetes
|
|
5
|
+
License: Apache 2.0
|
|
6
|
+
Author: Runhouse Team
|
|
7
|
+
Requires-Python: >=3.9,<=3.13
|
|
8
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
9
|
+
Classifier: License :: Other/Proprietary License
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
17
|
+
Classifier: Topic :: System :: Distributed Computing
|
|
18
|
+
Provides-Extra: client
|
|
19
|
+
Provides-Extra: docs
|
|
20
|
+
Provides-Extra: otel
|
|
21
|
+
Provides-Extra: server
|
|
22
|
+
Requires-Dist: fastapi ; extra == "server"
|
|
23
|
+
Requires-Dist: httpx (>=0.28.0,<0.29.0)
|
|
24
|
+
Requires-Dist: jinja2 (>=3.0)
|
|
25
|
+
Requires-Dist: kubernetes (>=20.0.0,!=32.0.0)
|
|
26
|
+
Requires-Dist: myst-parser ; extra == "docs"
|
|
27
|
+
Requires-Dist: opentelemetry-api ; extra == "otel"
|
|
28
|
+
Requires-Dist: opentelemetry-distro ; extra == "otel"
|
|
29
|
+
Requires-Dist: opentelemetry-exporter-otlp ; extra == "otel"
|
|
30
|
+
Requires-Dist: opentelemetry-exporter-prometheus ; extra == "otel"
|
|
31
|
+
Requires-Dist: opentelemetry-instrumentation ; extra == "otel"
|
|
32
|
+
Requires-Dist: opentelemetry-instrumentation-fastapi ; extra == "otel"
|
|
33
|
+
Requires-Dist: opentelemetry-instrumentation-logging ; extra == "otel"
|
|
34
|
+
Requires-Dist: opentelemetry-instrumentation-requests ; extra == "otel"
|
|
35
|
+
Requires-Dist: opentelemetry-sdk ; extra == "otel"
|
|
36
|
+
Requires-Dist: prometheus-api-client ; extra == "client"
|
|
37
|
+
Requires-Dist: prometheus-client ; extra == "otel"
|
|
38
|
+
Requires-Dist: pydantic (>=2.5.0)
|
|
39
|
+
Requires-Dist: python-json-logger ; extra == "server"
|
|
40
|
+
Requires-Dist: pyyaml (>=6.0,<7.0)
|
|
41
|
+
Requires-Dist: requests (>=2.32.0,<3.0.0)
|
|
42
|
+
Requires-Dist: rich ; extra == "client"
|
|
43
|
+
Requires-Dist: sphinx ; extra == "docs"
|
|
44
|
+
Requires-Dist: sphinx-autodoc-typehints ; extra == "docs"
|
|
45
|
+
Requires-Dist: sphinx-book-theme ; extra == "docs"
|
|
46
|
+
Requires-Dist: typer ; extra == "client"
|
|
47
|
+
Requires-Dist: uvicorn[standard] ; extra == "server"
|
|
48
|
+
Requires-Dist: web-pdb ; extra == "server"
|
|
49
|
+
Requires-Dist: websockets (>=15.0,<16.0)
|
|
50
|
+
Project-URL: Documentation, https://www.run.house/kubetorch/introduction
|
|
51
|
+
Project-URL: Homepage, https://run.house
|
|
52
|
+
Project-URL: Repository, https://github.com/run-house/kubetorch
|
|
53
|
+
Description-Content-Type: text/markdown
|
|
54
|
+
|
|
55
|
+
# π¦Kubetorchπ₯
|
|
56
|
+
|
|
57
|
+
**A Python interface for running ML workloads on Kubernetes**
|
|
58
|
+
|
|
59
|
+
Kubetorch enables you to run any Python code on Kubernetes at any scale by specifying required resources, distribution, and scaling directly in code. It provides caching and hot redeployment for 1-2 second iteration cycles, handles hardware faults and preemptions programmatically, and orchestrates complex, heterogeneous workloads with built-in observability and fault tolerance.
|
|
60
|
+
|
|
61
|
+
## Hello World
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
from kubetorch import fn
|
|
65
|
+
|
|
66
|
+
def hello_world():
|
|
67
|
+
return "Hello from Kubetorch!"
|
|
68
|
+
|
|
69
|
+
if __name__ == "__main__":
|
|
70
|
+
# Define your compute
|
|
71
|
+
compute = kt.Compute(cpus=".1")
|
|
72
|
+
|
|
73
|
+
# Send local function to freshly launched remote compute
|
|
74
|
+
remote_hello = kt.fn(hello_world).to(compute)
|
|
75
|
+
|
|
76
|
+
# Runs remotely on your Kubernetes cluster
|
|
77
|
+
result = hello_world()
|
|
78
|
+
print(result) # "Hello from Kubetorch!"
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## What Kubetorch Enables
|
|
82
|
+
|
|
83
|
+
- **100x faster iteration** from 10+ minutes to 1-3 seconds for complex ML applications like RL and distributed training
|
|
84
|
+
- **50%+ compute cost savings** through intelligent resource allocation, bin-packing, and dynamic scaling
|
|
85
|
+
- **95% fewer production faults** with built-in fault handling with programmatic error recovery and resource adjustment
|
|
86
|
+
|
|
87
|
+
## Installation
|
|
88
|
+
|
|
89
|
+
### 1. Python Client
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
pip install "kubetorch[client]"
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### 2. Kubernetes Deployment (Helm)
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
# Option 1: Install directly from OCI registry
|
|
99
|
+
helm upgrade --install kubetorch oci://ghcr.io/run-house/charts/kubetorch \
|
|
100
|
+
--version 0.2.1 -n kubetorch --create-namespace
|
|
101
|
+
|
|
102
|
+
# Option 2: Download chart locally first
|
|
103
|
+
helm pull oci://ghcr.io/run-house/charts/kubetorch --version 0.2.1 --untar
|
|
104
|
+
helm upgrade --install kubetorch ./kubetorch -n kubetorch --create-namespace
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
For detailed setup instructions, see our [Installation Guide](https://www.run.house/kubetorch/installation).
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
## Learn More
|
|
111
|
+
|
|
112
|
+
- **[Documentation](https://www.run.house/kubetorch/introduction)** - API Reference, concepts, and guides
|
|
113
|
+
- **[Examples](https://www.run.house/kubetorch/examples)** - Real-world usage patterns and tutorials
|
|
114
|
+
- **[Join our Slack](https://join.slack.com/t/kubetorch/shared_invite/zt-3g76q5i4j-uP60AdydxnAmjGVAQhtALA)** - Connect with the community and get support
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
[Apache 2.0 License](LICENSE)
|
|
119
|
+
|
|
120
|
+
**πββοΈ Built by [Runhouse](https://www.run.house) π **
|
|
121
|
+
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# π¦Kubetorchπ₯
|
|
2
|
+
|
|
3
|
+
**A Python interface for running ML workloads on Kubernetes**
|
|
4
|
+
|
|
5
|
+
Kubetorch enables you to run any Python code on Kubernetes at any scale by specifying required resources, distribution, and scaling directly in code. It provides caching and hot redeployment for 1-2 second iteration cycles, handles hardware faults and preemptions programmatically, and orchestrates complex, heterogeneous workloads with built-in observability and fault tolerance.
|
|
6
|
+
|
|
7
|
+
## Hello World
|
|
8
|
+
|
|
9
|
+
```python
|
|
10
|
+
from kubetorch import fn
|
|
11
|
+
|
|
12
|
+
def hello_world():
|
|
13
|
+
return "Hello from Kubetorch!"
|
|
14
|
+
|
|
15
|
+
if __name__ == "__main__":
|
|
16
|
+
# Define your compute
|
|
17
|
+
compute = kt.Compute(cpus=".1")
|
|
18
|
+
|
|
19
|
+
# Send local function to freshly launched remote compute
|
|
20
|
+
remote_hello = kt.fn(hello_world).to(compute)
|
|
21
|
+
|
|
22
|
+
# Runs remotely on your Kubernetes cluster
|
|
23
|
+
result = hello_world()
|
|
24
|
+
print(result) # "Hello from Kubetorch!"
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## What Kubetorch Enables
|
|
28
|
+
|
|
29
|
+
- **100x faster iteration** from 10+ minutes to 1-3 seconds for complex ML applications like RL and distributed training
|
|
30
|
+
- **50%+ compute cost savings** through intelligent resource allocation, bin-packing, and dynamic scaling
|
|
31
|
+
- **95% fewer production faults** with built-in fault handling with programmatic error recovery and resource adjustment
|
|
32
|
+
|
|
33
|
+
## Installation
|
|
34
|
+
|
|
35
|
+
### 1. Python Client
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install "kubetorch[client]"
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### 2. Kubernetes Deployment (Helm)
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# Option 1: Install directly from OCI registry
|
|
45
|
+
helm upgrade --install kubetorch oci://ghcr.io/run-house/charts/kubetorch \
|
|
46
|
+
--version 0.2.1 -n kubetorch --create-namespace
|
|
47
|
+
|
|
48
|
+
# Option 2: Download chart locally first
|
|
49
|
+
helm pull oci://ghcr.io/run-house/charts/kubetorch --version 0.2.1 --untar
|
|
50
|
+
helm upgrade --install kubetorch ./kubetorch -n kubetorch --create-namespace
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
For detailed setup instructions, see our [Installation Guide](https://www.run.house/kubetorch/installation).
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
## Learn More
|
|
57
|
+
|
|
58
|
+
- **[Documentation](https://www.run.house/kubetorch/introduction)** - API Reference, concepts, and guides
|
|
59
|
+
- **[Examples](https://www.run.house/kubetorch/examples)** - Real-world usage patterns and tutorials
|
|
60
|
+
- **[Join our Slack](https://join.slack.com/t/kubetorch/shared_invite/zt-3g76q5i4j-uP60AdydxnAmjGVAQhtALA)** - Connect with the community and get support
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
[Apache 2.0 License](LICENSE)
|
|
65
|
+
|
|
66
|
+
**πββοΈ Built by [Runhouse](https://www.run.house) π **
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
from kubetorch.globals import config # noqa: F401
|
|
2
|
+
from kubetorch.resources.callables.cls.cls import Cls, cls # noqa: F401
|
|
3
|
+
from kubetorch.resources.callables.fn.fn import Fn, fn # noqa: F401
|
|
4
|
+
from kubetorch.resources.compute.app import App, app # noqa: F401
|
|
5
|
+
from kubetorch.resources.compute.compute import Compute # noqa: F401
|
|
6
|
+
from kubetorch.resources.compute.decorators import ( # noqa: F401
|
|
7
|
+
async_,
|
|
8
|
+
autoscale,
|
|
9
|
+
compute,
|
|
10
|
+
distribute,
|
|
11
|
+
)
|
|
12
|
+
from kubetorch.resources.compute.utils import (
|
|
13
|
+
ImagePullError,
|
|
14
|
+
KnativeServiceConflictError,
|
|
15
|
+
PodContainerError,
|
|
16
|
+
QueueUnschedulableError,
|
|
17
|
+
ResourceNotAvailableError,
|
|
18
|
+
RsyncError,
|
|
19
|
+
SecretNotFound,
|
|
20
|
+
ServiceHealthError,
|
|
21
|
+
ServiceTimeoutError,
|
|
22
|
+
VersionMismatchError,
|
|
23
|
+
) # noqa: F401
|
|
24
|
+
from kubetorch.resources.images.image import Image # noqa: F401
|
|
25
|
+
from kubetorch.resources.secrets import Secret, secret # noqa: F401
|
|
26
|
+
from kubetorch.resources.volumes.volume import Volume # noqa: F401 # noqa: F401
|
|
27
|
+
from kubetorch.servers.http.utils import ( # noqa: F401
|
|
28
|
+
deep_breakpoint,
|
|
29
|
+
PodTerminatedError,
|
|
30
|
+
StartupError,
|
|
31
|
+
WorkerMembershipChanged,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
from .resources import images
|
|
35
|
+
|
|
36
|
+
# Alias to expose as kt.images
|
|
37
|
+
images = images
|
|
38
|
+
|
|
39
|
+
# Registry of all kubetorch exceptions for serialization/deserialization
|
|
40
|
+
EXCEPTION_REGISTRY = {
|
|
41
|
+
"ImagePullError": ImagePullError,
|
|
42
|
+
"PodContainerError": PodContainerError,
|
|
43
|
+
"ResourceNotAvailableError": ResourceNotAvailableError,
|
|
44
|
+
"ServiceHealthError": ServiceHealthError,
|
|
45
|
+
"ServiceTimeoutError": ServiceTimeoutError,
|
|
46
|
+
"StartupError": StartupError,
|
|
47
|
+
"PodTerminatedError": PodTerminatedError,
|
|
48
|
+
"QueueUnschedulableError": QueueUnschedulableError,
|
|
49
|
+
"KnativeServiceConflictError": KnativeServiceConflictError,
|
|
50
|
+
"RsyncError": RsyncError,
|
|
51
|
+
"VersionMismatchError": VersionMismatchError,
|
|
52
|
+
"SecretNotFound": SecretNotFound,
|
|
53
|
+
"WorkerMembershipChanged": WorkerMembershipChanged,
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
# Make exceptions appear to be from the main package (e.g. kubetorch.ImagePullError)
|
|
57
|
+
for exception in EXCEPTION_REGISTRY.values():
|
|
58
|
+
exception.__module__ = "kubetorch"
|
|
59
|
+
|
|
60
|
+
__version__ = "0.2.1"
|