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.
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: zipher-sdk
3
- Version: 0.1.2
4
- Summary: Python SDK for accessing the Zipher API
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
- UNKNOWN
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.2',
8
- description='Python SDK for accessing the Zipher API',
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
+
@@ -0,0 +1,8 @@
1
+ from typing import Optional
2
+ from pydantic import BaseModel
3
+
4
+
5
+ class ConfFetcherRequest(BaseModel):
6
+ job_id: str
7
+ customer_id: str
8
+ merge_with: Optional[str] = None
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: zipher-sdk
3
- Version: 0.1.2
4
- Summary: Python SDK for accessing the Zipher API
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
- UNKNOWN
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
@@ -1,2 +0,0 @@
1
- # zipher-sdk
2
- Zipher's python sdk provides programmatic access to Zipher's api
@@ -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