zipher-sdk 0.1.2__tar.gz → 0.1.4__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.
- {zipher-sdk-0.1.2 → zipher-sdk-0.1.4}/PKG-INFO +3 -3
- zipher-sdk-0.1.4/README.md +58 -0
- {zipher-sdk-0.1.2 → zipher-sdk-0.1.4}/setup.py +3 -2
- zipher-sdk-0.1.4/tests/__init__.py +0 -0
- zipher-sdk-0.1.4/tests/test_client.py +39 -0
- zipher-sdk-0.1.4/zipher/client.py +48 -0
- zipher-sdk-0.1.4/zipher/models.py +8 -0
- {zipher-sdk-0.1.2 → zipher-sdk-0.1.4}/zipher_sdk.egg-info/PKG-INFO +3 -3
- {zipher-sdk-0.1.2 → zipher-sdk-0.1.4}/zipher_sdk.egg-info/SOURCES.txt +3 -1
- {zipher-sdk-0.1.2 → zipher-sdk-0.1.4}/zipher_sdk.egg-info/top_level.txt +1 -0
- zipher-sdk-0.1.2/README.md +0 -2
- zipher-sdk-0.1.2/zipher/client.py +0 -40
- zipher-sdk-0.1.2/zipher/cluster_spec_merger.py +0 -64
- {zipher-sdk-0.1.2 → zipher-sdk-0.1.4}/LICENSE +0 -0
- {zipher-sdk-0.1.2 → zipher-sdk-0.1.4}/setup.cfg +0 -0
- {zipher-sdk-0.1.2 → zipher-sdk-0.1.4}/zipher/__init__.py +0 -0
- {zipher-sdk-0.1.2 → zipher-sdk-0.1.4}/zipher/conf.py +0 -0
- {zipher-sdk-0.1.2 → zipher-sdk-0.1.4}/zipher_sdk.egg-info/dependency_links.txt +0 -0
- {zipher-sdk-0.1.2 → zipher-sdk-0.1.4}/zipher_sdk.egg-info/requires.txt +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: zipher-sdk
|
|
3
|
-
Version: 0.1.
|
|
4
|
-
Summary: Python SDK for
|
|
3
|
+
Version: 0.1.4
|
|
4
|
+
Summary: Python SDK for programmatic access to the Zipher API
|
|
5
5
|
Home-page: UNKNOWN
|
|
6
6
|
Author: Zipher Inc.
|
|
7
7
|
Author-email: yoav@zipher.cloud
|
|
@@ -12,5 +12,5 @@ Classifier: License :: OSI Approved :: MIT License
|
|
|
12
12
|
Classifier: Operating System :: OS Independent
|
|
13
13
|
License-File: LICENSE
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
Python SDK for programmatic access to the Zipher API
|
|
16
16
|
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
|
|
2
|
+
# Zipher SDK
|
|
3
|
+
|
|
4
|
+
The Zipher SDK is a Python library for interacting with Zipher's APIs.
|
|
5
|
+
|
|
6
|
+
## Installation
|
|
7
|
+
|
|
8
|
+
You can install the Zipher SDK using pip:
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
pip install zipher-sdk
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Usage
|
|
15
|
+
|
|
16
|
+
Here are some basic examples of how you can use the Zipher SDK to optimize your databricks clusters using Zipher's ML-powered optimization engine:
|
|
17
|
+
|
|
18
|
+
### Update Existing Configuration
|
|
19
|
+
|
|
20
|
+
You can update an existing configuration by initializing a zipher Client and sending a JSON payload to the `update_existing_conf` function. Here's how you can do it:
|
|
21
|
+
|
|
22
|
+
```python
|
|
23
|
+
from zipher import Client
|
|
24
|
+
|
|
25
|
+
client = Client(customer_id="my_customer_id") # assuming the zipher API key is stored in ZIPHER_API_KEY environment variable
|
|
26
|
+
|
|
27
|
+
# Your existing cluster config:
|
|
28
|
+
config_payload = {
|
|
29
|
+
"new_cluster": {
|
|
30
|
+
"autoscale": {
|
|
31
|
+
"min_workers": 1,
|
|
32
|
+
"max_workers": 30
|
|
33
|
+
},
|
|
34
|
+
"cluster_name": "my-cluster",
|
|
35
|
+
"spark_version": "10.4.x-scala2.12",
|
|
36
|
+
"spark_conf": {
|
|
37
|
+
"spark.driver.maxResultSize": "4g"
|
|
38
|
+
},
|
|
39
|
+
"aws_attributes": {
|
|
40
|
+
"first_on_demand": 0,
|
|
41
|
+
"availability": "SPOT",
|
|
42
|
+
"zone_id": "auto",
|
|
43
|
+
"spot_bid_price_percent": 100,
|
|
44
|
+
"ebs_volume_count": 0
|
|
45
|
+
},
|
|
46
|
+
"node_type_id": "rd-fleet.2xlarge",
|
|
47
|
+
"driver_node_type_id": "rd-fleet.xlarge",
|
|
48
|
+
"spark_env_vars": {},
|
|
49
|
+
"enable_elastic_disk": "false"
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
# Update configuration
|
|
54
|
+
optimized_cluster = client.update_existing_conf(job_id="my-job-id", existing_conf=config_payload)
|
|
55
|
+
|
|
56
|
+
# Continue with sending the optimized configuration to Databricks via the Databricks python SDK, Airflow operator, etc.
|
|
57
|
+
|
|
58
|
+
```
|
|
@@ -4,8 +4,9 @@ from setuptools import setup, find_packages
|
|
|
4
4
|
|
|
5
5
|
setup(
|
|
6
6
|
name='zipher-sdk',
|
|
7
|
-
version='0.1.
|
|
8
|
-
description='Python SDK for
|
|
7
|
+
version='0.1.4',
|
|
8
|
+
description='Python SDK for programmatic access to the Zipher API',
|
|
9
|
+
long_description='Python SDK for programmatic access to the Zipher API',
|
|
9
10
|
author='Zipher Inc.',
|
|
10
11
|
author_email='yoav@zipher.cloud',
|
|
11
12
|
packages=find_packages(),
|
|
File without changes
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
from unittest.mock import patch, MagicMock
|
|
3
|
+
from zipher.client import Client
|
|
4
|
+
from zipher.models import ConfFetcherRequest
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class TestClient(unittest.TestCase):
|
|
8
|
+
|
|
9
|
+
def setUp(self):
|
|
10
|
+
self.customer_id = "test_id"
|
|
11
|
+
self.api_key = "test_api_key"
|
|
12
|
+
self.client = Client(customer_id=self.customer_id, zipher_api_key=self.api_key)
|
|
13
|
+
|
|
14
|
+
def test_initialization(self):
|
|
15
|
+
self.assertEqual(self.client.customer_id, self.customer_id)
|
|
16
|
+
self.assertEqual(self.client.zipher_api_key, self.api_key)
|
|
17
|
+
# Test initialization with API key as env var
|
|
18
|
+
with patch('os.getenv', return_value='env_api_key'):
|
|
19
|
+
client = Client(customer_id=self.customer_id)
|
|
20
|
+
self.assertEqual(client.zipher_api_key, 'env_api_key')
|
|
21
|
+
|
|
22
|
+
@patch('requests.get')
|
|
23
|
+
def test_get_optimized_config_success(self, mock_get):
|
|
24
|
+
mock_get.return_value.ok = True
|
|
25
|
+
mock_get.return_value.json = MagicMock(return_value={'body': '{"key": "value"}'})
|
|
26
|
+
|
|
27
|
+
response = self.client.get_optimized_config(job_id="1234")
|
|
28
|
+
self.assertEqual(response, {"key": "value"})
|
|
29
|
+
|
|
30
|
+
@patch('requests.get')
|
|
31
|
+
def test_get_optimized_config_failure(self, mock_get):
|
|
32
|
+
mock_get.return_value.ok = False
|
|
33
|
+
mock_get.return_value.raise_for_status = MagicMock(side_effect=ConnectionError("API Failure"))
|
|
34
|
+
with self.assertRaises(ConnectionError):
|
|
35
|
+
self.client.get_optimized_config(job_id="1234")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
if __name__ == '__main__':
|
|
39
|
+
unittest.main()
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import json
|
|
3
|
+
import requests
|
|
4
|
+
from zipher.conf import Conf
|
|
5
|
+
from typing import Dict
|
|
6
|
+
from databricks.sdk.service.compute import ClusterSpec
|
|
7
|
+
from zipher.models import ConfFetcherRequest
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Client:
|
|
11
|
+
def __init__(self, customer_id: str, zipher_api_key: str = ''):
|
|
12
|
+
self.zipher_api_key = zipher_api_key or os.getenv(Conf.zipher_api_key_env_var)
|
|
13
|
+
self.customer_id = customer_id
|
|
14
|
+
|
|
15
|
+
def _call_conf_fetcher_api(self, params: ConfFetcherRequest):
|
|
16
|
+
headers = {
|
|
17
|
+
'x-api-key': self.zipher_api_key
|
|
18
|
+
}
|
|
19
|
+
response = requests.get(
|
|
20
|
+
url=Conf.zipher_config_fetcher_api_endpoint,
|
|
21
|
+
headers=headers,
|
|
22
|
+
params=params.model_dump()
|
|
23
|
+
)
|
|
24
|
+
if response.ok:
|
|
25
|
+
return json.loads(response.json()['body'])
|
|
26
|
+
else:
|
|
27
|
+
response.raise_for_status()
|
|
28
|
+
|
|
29
|
+
def get_optimized_config(self, job_id: str) -> Dict:
|
|
30
|
+
params = ConfFetcherRequest(customer_id=self.customer_id, job_id=job_id)
|
|
31
|
+
return self._call_conf_fetcher_api(params)
|
|
32
|
+
|
|
33
|
+
def get_optimized_config_as_dbx_cluster_spec(self, job_id: str) -> ClusterSpec:
|
|
34
|
+
return ClusterSpec.from_dict(self.get_optimized_config(job_id))
|
|
35
|
+
|
|
36
|
+
def update_existing_conf(self, job_id: str, existing_conf: Dict) -> Dict:
|
|
37
|
+
if 'new_cluster' in existing_conf:
|
|
38
|
+
existing_conf = existing_conf['new_cluster']
|
|
39
|
+
params = ConfFetcherRequest(customer_id=self.customer_id, job_id=job_id, merge_with=json.dumps(existing_conf))
|
|
40
|
+
return self._call_conf_fetcher_api(params)
|
|
41
|
+
|
|
42
|
+
# optimized_spec = self.get_optimized_config_as_dbx_cluster_spec(job_id=job_id)
|
|
43
|
+
# if 'new_cluster' in existing_conf:
|
|
44
|
+
# existing_conf = existing_conf['new_cluster']
|
|
45
|
+
# existing_spec = ClusterSpec.from_dict(existing_conf)
|
|
46
|
+
# merged_spec = self.cluster_spec_merger.merge(orig_spec=existing_spec, optimized_spec=optimized_spec)
|
|
47
|
+
# merged_dict = merged_spec.as_dict()
|
|
48
|
+
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: zipher-sdk
|
|
3
|
-
Version: 0.1.
|
|
4
|
-
Summary: Python SDK for
|
|
3
|
+
Version: 0.1.4
|
|
4
|
+
Summary: Python SDK for programmatic access to the Zipher API
|
|
5
5
|
Home-page: UNKNOWN
|
|
6
6
|
Author: Zipher Inc.
|
|
7
7
|
Author-email: yoav@zipher.cloud
|
|
@@ -12,5 +12,5 @@ Classifier: License :: OSI Approved :: MIT License
|
|
|
12
12
|
Classifier: Operating System :: OS Independent
|
|
13
13
|
License-File: LICENSE
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
Python SDK for programmatic access to the Zipher API
|
|
16
16
|
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
LICENSE
|
|
2
2
|
README.md
|
|
3
3
|
setup.py
|
|
4
|
+
tests/__init__.py
|
|
5
|
+
tests/test_client.py
|
|
4
6
|
zipher/__init__.py
|
|
5
7
|
zipher/client.py
|
|
6
|
-
zipher/cluster_spec_merger.py
|
|
7
8
|
zipher/conf.py
|
|
9
|
+
zipher/models.py
|
|
8
10
|
zipher_sdk.egg-info/PKG-INFO
|
|
9
11
|
zipher_sdk.egg-info/SOURCES.txt
|
|
10
12
|
zipher_sdk.egg-info/dependency_links.txt
|
zipher-sdk-0.1.2/README.md
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import json
|
|
3
|
-
import requests
|
|
4
|
-
from zipher.conf import Conf
|
|
5
|
-
from zipher.cluster_spec_merger import ClusterSpecMerger
|
|
6
|
-
from typing import Dict
|
|
7
|
-
from databricks.sdk.service.compute import ClusterSpec
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class Client:
|
|
11
|
-
def __init__(self, customer_id: str, zipher_api_key: str = ''):
|
|
12
|
-
self.zipher_api_key = zipher_api_key or os.getenv(Conf.zipher_api_key_env_var)
|
|
13
|
-
self.customer_id = customer_id
|
|
14
|
-
self.cluster_spec_merger = ClusterSpecMerger()
|
|
15
|
-
|
|
16
|
-
def get_optimized_config(self, job_id: str) -> Dict:
|
|
17
|
-
headers = {
|
|
18
|
-
'x-api-key': self.zipher_api_key
|
|
19
|
-
}
|
|
20
|
-
params = {
|
|
21
|
-
'customer_id': self.customer_id,
|
|
22
|
-
'job_id': job_id
|
|
23
|
-
}
|
|
24
|
-
response = requests.get(url=Conf.zipher_config_fetcher_api_endpoint, headers=headers, params=params)
|
|
25
|
-
if response.ok:
|
|
26
|
-
return json.loads(response.json()['body'])
|
|
27
|
-
else:
|
|
28
|
-
response.raise_for_status()
|
|
29
|
-
|
|
30
|
-
def get_optimized_config_as_dbx_cluster_spec(self, job_id: str) -> ClusterSpec:
|
|
31
|
-
return ClusterSpec.from_dict(self.get_optimized_config(job_id))
|
|
32
|
-
|
|
33
|
-
def update_existing_conf(self, job_id: str, existing_conf: Dict) -> Dict:
|
|
34
|
-
optimized_spec = self.get_optimized_config_as_dbx_cluster_spec(job_id=job_id)
|
|
35
|
-
if 'new_cluster' in existing_conf:
|
|
36
|
-
existing_conf = existing_conf['new_cluster']
|
|
37
|
-
existing_spec = ClusterSpec.from_dict(existing_conf)
|
|
38
|
-
merged_spec = self.cluster_spec_merger.merge(orig_spec=existing_spec, optimized_spec=optimized_spec)
|
|
39
|
-
return merged_spec.as_dict()
|
|
40
|
-
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
from databricks.sdk.service.compute import ClusterSpec, AwsAttributes
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
class ClusterSpecMerger:
|
|
5
|
-
def __init__(self):
|
|
6
|
-
self.orig_spec: ClusterSpec = None
|
|
7
|
-
self.optimized_spec: ClusterSpec = None
|
|
8
|
-
|
|
9
|
-
def merge(self, orig_spec: ClusterSpec, optimized_spec: ClusterSpec) -> ClusterSpec:
|
|
10
|
-
self.orig_spec = orig_spec
|
|
11
|
-
self.optimized_spec = optimized_spec
|
|
12
|
-
|
|
13
|
-
self._merge_autoscaling_config()
|
|
14
|
-
self._merge_spark_cong_config()
|
|
15
|
-
self._merge_aws_attrs()
|
|
16
|
-
self._merge_node_types()
|
|
17
|
-
self._merge_spark_env_vars()
|
|
18
|
-
|
|
19
|
-
return self.orig_spec
|
|
20
|
-
|
|
21
|
-
def _merge_autoscaling_config(self):
|
|
22
|
-
if self.orig_spec.autoscale is None:
|
|
23
|
-
self.orig_spec.autoscale = self.optimized_spec.autoscale
|
|
24
|
-
else:
|
|
25
|
-
if self.optimized_spec.autoscale is not None:
|
|
26
|
-
self.orig_spec.autoscale.min_workers = self.optimized_spec.autoscale.min_workers
|
|
27
|
-
self.orig_spec.autoscale.max_workers = self.optimized_spec.autoscale.max_workers
|
|
28
|
-
|
|
29
|
-
def _merge_spark_cong_config(self):
|
|
30
|
-
self.orig_spec.spark_conf = self.orig_spec.spark_conf or {}
|
|
31
|
-
if self.optimized_spec.spark_conf:
|
|
32
|
-
self.orig_spec.spark_conf.update(self.optimized_spec.spark_conf)
|
|
33
|
-
|
|
34
|
-
def _merge_aws_attrs(self):
|
|
35
|
-
self.orig_spec.aws_attributes = self.orig_spec.aws_attributes or AwsAttributes()
|
|
36
|
-
if self.optimized_spec.aws_attributes:
|
|
37
|
-
self.orig_spec.aws_attributes.ebs_volume_count = (self.optimized_spec.aws_attributes.ebs_volume_count or
|
|
38
|
-
self.orig_spec.aws_attributes.ebs_volume_count)
|
|
39
|
-
self.orig_spec.aws_attributes.ebs_volume_size = (self.optimized_spec.aws_attributes.ebs_volume_size or
|
|
40
|
-
self.orig_spec.aws_attributes.ebs_volume_size)
|
|
41
|
-
self.orig_spec.aws_attributes.ebs_volume_throughput = (
|
|
42
|
-
self.optimized_spec.aws_attributes.ebs_volume_throughput or
|
|
43
|
-
self.orig_spec.aws_attributes.ebs_volume_throughput)
|
|
44
|
-
self.orig_spec.aws_attributes.ebs_volume_iops = (
|
|
45
|
-
self.optimized_spec.aws_attributes.ebs_volume_iops or
|
|
46
|
-
self.orig_spec.aws_attributes.ebs_volume_iops)
|
|
47
|
-
self.orig_spec.aws_attributes.ebs_volume_type = (
|
|
48
|
-
self.optimized_spec.aws_attributes.ebs_volume_type or
|
|
49
|
-
self.orig_spec.aws_attributes.ebs_volume_type)
|
|
50
|
-
self.orig_spec.aws_attributes.zone_id = (
|
|
51
|
-
self.optimized_spec.aws_attributes.zone_id or
|
|
52
|
-
self.orig_spec.aws_attributes.zone_id)
|
|
53
|
-
self.orig_spec.aws_attributes.first_on_demand = (
|
|
54
|
-
self.optimized_spec.aws_attributes.first_on_demand or
|
|
55
|
-
self.orig_spec.aws_attributes.first_on_demand)
|
|
56
|
-
|
|
57
|
-
def _merge_node_types(self):
|
|
58
|
-
self.orig_spec.node_type_id = self.optimized_spec.node_type_id or self.orig_spec.node_type_id
|
|
59
|
-
self.orig_spec.driver_node_type_id = self.optimized_spec.driver_node_type_id or self.orig_spec.driver_node_type_id
|
|
60
|
-
|
|
61
|
-
def _merge_spark_env_vars(self):
|
|
62
|
-
self.orig_spec.spark_env_vars = self.orig_spec.spark_env_vars or {}
|
|
63
|
-
if self.optimized_spec.spark_env_vars:
|
|
64
|
-
self.orig_spec.spark_env_vars.update(self.optimized_spec.spark_env_vars)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|