erioon 0.1.2__py3-none-any.whl → 0.1.4__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.
- erioon-0.1.4.dist-info/METADATA +30 -0
- erioon-0.1.4.dist-info/RECORD +5 -0
- erioon-0.1.4.dist-info/top_level.txt +1 -0
- erioon/auth.py +0 -20
- erioon/client.py +0 -150
- erioon/collection.py +0 -298
- erioon/create.py +0 -265
- erioon/database.py +0 -67
- erioon/delete.py +0 -257
- erioon/functions.py +0 -350
- erioon/ping.py +0 -37
- erioon/read.py +0 -241
- erioon/update.py +0 -123
- erioon-0.1.2.dist-info/METADATA +0 -26
- erioon-0.1.2.dist-info/RECORD +0 -15
- erioon-0.1.2.dist-info/top_level.txt +0 -1
- {erioon-0.1.2.dist-info → erioon-0.1.4.dist-info}/LICENSE +0 -0
- {erioon-0.1.2.dist-info → erioon-0.1.4.dist-info}/WHEEL +0 -0
@@ -0,0 +1,30 @@
|
|
1
|
+
Metadata-Version: 2.2
|
2
|
+
Name: erioon
|
3
|
+
Version: 0.1.4
|
4
|
+
Summary: Erioon Python SDK for seamless interaction with Erioon data services
|
5
|
+
Author: Zyber Pireci
|
6
|
+
Author-email: zyber.pireci@erioon.com
|
7
|
+
License: MIT
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
10
|
+
Classifier: Operating System :: OS Independent
|
11
|
+
Classifier: Topic :: Software Development :: Libraries
|
12
|
+
Classifier: Intended Audience :: Developers
|
13
|
+
Requires-Python: >=3.6
|
14
|
+
Description-Content-Type: text/plain
|
15
|
+
License-File: LICENSE
|
16
|
+
Requires-Dist: requests>=2.25.1
|
17
|
+
Requires-Dist: pyodbc
|
18
|
+
Requires-Dist: azure-storage-blob
|
19
|
+
Requires-Dist: msgpack
|
20
|
+
Dynamic: author
|
21
|
+
Dynamic: author-email
|
22
|
+
Dynamic: classifier
|
23
|
+
Dynamic: description
|
24
|
+
Dynamic: description-content-type
|
25
|
+
Dynamic: license
|
26
|
+
Dynamic: requires-dist
|
27
|
+
Dynamic: requires-python
|
28
|
+
Dynamic: summary
|
29
|
+
|
30
|
+
The Erioon SDK for Python provides a robust interface to interact with Erioon resources such as collections, databases, and clusters. It supports CRUD operations, querying, and connection management with ease, enabling developers to integrate Erioon data services into their applications efficiently.
|
@@ -0,0 +1,5 @@
|
|
1
|
+
erioon-0.1.4.dist-info/LICENSE,sha256=xwnq3DNlZpQyteOK9HvtHRhMdYviXTTaCDljEodFRnQ,569
|
2
|
+
erioon-0.1.4.dist-info/METADATA,sha256=hOjsdNViFMKZGlj6vtQR9hxPbZtfoyFMOtFAVX_Bz6E,1165
|
3
|
+
erioon-0.1.4.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
4
|
+
erioon-0.1.4.dist-info/top_level.txt,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
5
|
+
erioon-0.1.4.dist-info/RECORD,,
|
@@ -0,0 +1 @@
|
|
1
|
+
|
erioon/auth.py
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
from erioon.client import ErioonClient
|
2
|
-
|
3
|
-
def Auth(credential_string):
|
4
|
-
"""
|
5
|
-
Authenticates a user using a colon-separated email:password string.
|
6
|
-
|
7
|
-
Parameters:
|
8
|
-
- credential_string (str): A string in the format "email:password"
|
9
|
-
|
10
|
-
Returns:
|
11
|
-
- ErioonClient instance: An instance representing the authenticated user.
|
12
|
-
If authentication fails, the instance will contain the error message.
|
13
|
-
|
14
|
-
Example usage:
|
15
|
-
>>> from erioon.auth import Auth
|
16
|
-
>>> client = Auth("<API_KEY>:<EMAIL>:<PASSWORD>")
|
17
|
-
>>> print(client) # prints user_id if successful or error message if not
|
18
|
-
"""
|
19
|
-
api, email, password = credential_string.split(":")
|
20
|
-
return ErioonClient(api ,email, password)
|
erioon/client.py
DELETED
@@ -1,150 +0,0 @@
|
|
1
|
-
import os
|
2
|
-
import json
|
3
|
-
import requests
|
4
|
-
from datetime import datetime, timezone
|
5
|
-
from erioon.database import Database
|
6
|
-
|
7
|
-
class ErioonClient:
|
8
|
-
def __init__(self, api, email, password, base_url="https://sdk.erioon.com"):
|
9
|
-
self.api = api
|
10
|
-
self.email = email
|
11
|
-
self.password = password
|
12
|
-
self.base_url = base_url
|
13
|
-
self.user_id = None
|
14
|
-
self.token_path = os.path.expanduser(f"~/.erioon_token_{self._safe_filename(email)}")
|
15
|
-
self.login_metadata = None
|
16
|
-
|
17
|
-
try:
|
18
|
-
self.login_metadata = self._load_or_login()
|
19
|
-
self._update_metadata_fields()
|
20
|
-
except Exception as e:
|
21
|
-
print(f"[ErioonClient] Initialization error: {e}")
|
22
|
-
|
23
|
-
def _safe_filename(self, text):
|
24
|
-
return "".join(c if c.isalnum() else "_" for c in text)
|
25
|
-
|
26
|
-
def _do_login_and_cache(self):
|
27
|
-
metadata = self._login()
|
28
|
-
with open(self.token_path, "w") as f:
|
29
|
-
json.dump(metadata, f)
|
30
|
-
return metadata
|
31
|
-
|
32
|
-
def _load_or_login(self):
|
33
|
-
if os.path.exists(self.token_path):
|
34
|
-
with open(self.token_path, "r") as f:
|
35
|
-
metadata = json.load(f)
|
36
|
-
if self._is_sas_expired(metadata):
|
37
|
-
return self._do_login_and_cache()
|
38
|
-
return metadata
|
39
|
-
else:
|
40
|
-
return self._do_login_and_cache()
|
41
|
-
|
42
|
-
def _login(self):
|
43
|
-
url = f"{self.base_url}/login_with_credentials"
|
44
|
-
payload = {"api_key": self.api, "email": self.email, "password": self.password}
|
45
|
-
headers = {"Content-Type": "application/json"}
|
46
|
-
|
47
|
-
response = requests.post(url, json=payload, headers=headers)
|
48
|
-
if response.status_code == 200:
|
49
|
-
data = response.json()
|
50
|
-
self.login_metadata = data
|
51
|
-
self._update_metadata_fields()
|
52
|
-
return data
|
53
|
-
else:
|
54
|
-
try:
|
55
|
-
msg = response.json().get("error", "Login failed")
|
56
|
-
except Exception:
|
57
|
-
msg = response.text
|
58
|
-
print(f"[ErioonClient] Login failed: {msg}")
|
59
|
-
raise RuntimeError(msg)
|
60
|
-
|
61
|
-
def _update_metadata_fields(self):
|
62
|
-
if self.login_metadata:
|
63
|
-
self.user_id = self.login_metadata.get("_id")
|
64
|
-
self.cluster = self.login_metadata.get("cluster")
|
65
|
-
self.database = self.login_metadata.get("database")
|
66
|
-
self.sas_tokens = self.login_metadata.get("sas_tokens", {})
|
67
|
-
|
68
|
-
def _clear_cached_token(self):
|
69
|
-
if os.path.exists(self.token_path):
|
70
|
-
os.remove(self.token_path)
|
71
|
-
self.user_id = None
|
72
|
-
self.login_metadata = None
|
73
|
-
|
74
|
-
def _is_sas_expired(self, metadata):
|
75
|
-
expiry_str = metadata.get("sas_token_expiry") or metadata.get("expiry")
|
76
|
-
if not expiry_str:
|
77
|
-
return True
|
78
|
-
try:
|
79
|
-
expiry_dt = datetime.fromisoformat(expiry_str.replace("Z", "+00:00"))
|
80
|
-
now = datetime.now(timezone.utc)
|
81
|
-
return now >= expiry_dt
|
82
|
-
except Exception:
|
83
|
-
return True
|
84
|
-
|
85
|
-
def __getitem__(self, db_id):
|
86
|
-
if not self.user_id:
|
87
|
-
print(f"[ErioonClient] Not authenticated. Cannot access database {db_id}.")
|
88
|
-
raise ValueError("Client not authenticated.")
|
89
|
-
|
90
|
-
try:
|
91
|
-
return self._get_database_info(db_id)
|
92
|
-
except Exception as e:
|
93
|
-
print(f"[ErioonClient] Access failed for DB {db_id}, retrying login... ({e})")
|
94
|
-
self._clear_cached_token()
|
95
|
-
|
96
|
-
try:
|
97
|
-
self.login_metadata = self._do_login_and_cache()
|
98
|
-
self._update_metadata_fields()
|
99
|
-
except Exception as login_error:
|
100
|
-
print(f"[ErioonClient] Re-login failed: {login_error}")
|
101
|
-
raise RuntimeError(login_error)
|
102
|
-
|
103
|
-
try:
|
104
|
-
return self._get_database_info(db_id)
|
105
|
-
except Exception as second_error:
|
106
|
-
print(f"[ErioonClient] DB fetch after re-auth failed: {second_error}")
|
107
|
-
raise RuntimeError(second_error)
|
108
|
-
|
109
|
-
def _get_database_info(self, db_id):
|
110
|
-
payload = {"user_id": self.user_id, "db_id": db_id}
|
111
|
-
headers = {"Content-Type": "application/json"}
|
112
|
-
response = requests.post(f"{self.base_url}/db_info", json=payload, headers=headers)
|
113
|
-
|
114
|
-
if response.status_code == 200:
|
115
|
-
db_info = response.json()
|
116
|
-
sas_info = self.sas_tokens.get(db_id)
|
117
|
-
if not sas_info:
|
118
|
-
raise Exception(f"No SAS token info for database id {db_id}")
|
119
|
-
|
120
|
-
container_url = sas_info.get("container_url")
|
121
|
-
sas_token = sas_info.get("sas_token")
|
122
|
-
|
123
|
-
if not container_url or not sas_token:
|
124
|
-
raise Exception("Missing SAS URL components")
|
125
|
-
|
126
|
-
if not sas_token.startswith("?"):
|
127
|
-
sas_token = "?" + sas_token
|
128
|
-
|
129
|
-
sas_url = container_url.split("?")[0] + sas_token
|
130
|
-
|
131
|
-
return Database(
|
132
|
-
user_id=self.user_id,
|
133
|
-
metadata=db_info,
|
134
|
-
database=self.database,
|
135
|
-
cluster=self.cluster,
|
136
|
-
sas_url=sas_url
|
137
|
-
)
|
138
|
-
else:
|
139
|
-
try:
|
140
|
-
error_json = response.json()
|
141
|
-
error_msg = error_json.get("error", response.text)
|
142
|
-
except Exception:
|
143
|
-
error_msg = response.text
|
144
|
-
raise Exception(f"Failed to fetch database info: {error_msg}")
|
145
|
-
|
146
|
-
def __str__(self):
|
147
|
-
return self.user_id if self.user_id else "[ErioonClient] Unauthenticated"
|
148
|
-
|
149
|
-
def __repr__(self):
|
150
|
-
return f"<ErioonClient user_id={self.user_id}>" if self.user_id else "<ErioonClient unauthenticated>"
|
erioon/collection.py
DELETED
@@ -1,298 +0,0 @@
|
|
1
|
-
import json
|
2
|
-
from urllib.parse import urlparse
|
3
|
-
from erioon.read import handle_get_all, handle_get_data, handle_classify_vector
|
4
|
-
from erioon.create import handle_insert_one, handle_insert_many, handle_vector, handle_insert_many_vectors
|
5
|
-
from erioon.delete import handle_delete_one, handle_delete_many
|
6
|
-
from erioon.update import handle_update_query
|
7
|
-
from erioon.ping import handle_connection_ping
|
8
|
-
|
9
|
-
class Collection:
|
10
|
-
def __init__(
|
11
|
-
self,
|
12
|
-
user_id,
|
13
|
-
db_id,
|
14
|
-
coll_id,
|
15
|
-
metadata,
|
16
|
-
database,
|
17
|
-
cluster,
|
18
|
-
sas_url,
|
19
|
-
):
|
20
|
-
|
21
|
-
"""
|
22
|
-
Initialize a Collection object that wraps Erioon collection access.
|
23
|
-
|
24
|
-
Args:
|
25
|
-
user_id (str): The authenticated user's ID.
|
26
|
-
db_id (str): The database ID.
|
27
|
-
coll_id (str): The collection ID.
|
28
|
-
metadata (dict): Metadata info about this collection (e.g., schema, indexing, etc.).
|
29
|
-
database (str): Name or ID of the database.
|
30
|
-
cluster (str): Cluster name or ID hosting the database.
|
31
|
-
sas_url (str): Full SAS URL used to access the storage container.
|
32
|
-
"""
|
33
|
-
|
34
|
-
self.user_id = user_id
|
35
|
-
self.db_id = db_id
|
36
|
-
self.coll_id = coll_id
|
37
|
-
self.metadata = metadata
|
38
|
-
self.database = database
|
39
|
-
self.cluster = cluster
|
40
|
-
|
41
|
-
parsed_url = urlparse(sas_url.rstrip("/"))
|
42
|
-
container_name = parsed_url.path.lstrip("/").split("/")[0]
|
43
|
-
account_url = f"{parsed_url.scheme}://{parsed_url.netloc}"
|
44
|
-
sas_token = parsed_url.query
|
45
|
-
self.container_url = f"{account_url}/{container_name}?{sas_token}"
|
46
|
-
|
47
|
-
def _print_loading(self):
|
48
|
-
"""Prints a loading message (likely for UX in CLI or SDK usage)."""
|
49
|
-
print("Erioon is loading...")
|
50
|
-
|
51
|
-
def _is_read_only(self):
|
52
|
-
"""Check if the current database is marked as read-only."""
|
53
|
-
return self.database == "read"
|
54
|
-
|
55
|
-
def _read_only_response(self):
|
56
|
-
"""Standardized error response for blocked write operations."""
|
57
|
-
return "This user is not allowed to perform write operations.", 403
|
58
|
-
|
59
|
-
def get_all(self, limit=1000000):
|
60
|
-
"""
|
61
|
-
Fetch all records from the collection (up to a limit).
|
62
|
-
|
63
|
-
Args:
|
64
|
-
limit (int): Max number of records to fetch.
|
65
|
-
Returns:
|
66
|
-
list: Collection of records.
|
67
|
-
"""
|
68
|
-
self._print_loading()
|
69
|
-
result, status_code = handle_get_all(
|
70
|
-
user_id=self.user_id,
|
71
|
-
db_id=self.db_id,
|
72
|
-
coll_id=self.coll_id,
|
73
|
-
limit=limit,
|
74
|
-
container_url=self.container_url,
|
75
|
-
)
|
76
|
-
return result
|
77
|
-
|
78
|
-
def get_specific(self, filters: dict | None = None, limit: int = 1000):
|
79
|
-
"""
|
80
|
-
Fetch records that match specific key-value filters.
|
81
|
-
|
82
|
-
Args:
|
83
|
-
filters (dict): Dictionary of exact match filters.
|
84
|
-
limit (int): Max number of matching records to return.
|
85
|
-
|
86
|
-
Returns:
|
87
|
-
list: Filtered records from the collection.
|
88
|
-
"""
|
89
|
-
if limit > 500_000:
|
90
|
-
raise ValueError("Limit of 500,000 exceeded")
|
91
|
-
self._print_loading()
|
92
|
-
|
93
|
-
if filters is None:
|
94
|
-
filters = {}
|
95
|
-
|
96
|
-
search_criteria = [{k: v} for k, v in filters.items()]
|
97
|
-
print(search_criteria)
|
98
|
-
|
99
|
-
result, status_code = handle_get_data(
|
100
|
-
user_id=self.user_id,
|
101
|
-
db_id=self.db_id,
|
102
|
-
coll_id=self.coll_id,
|
103
|
-
search_criteria=search_criteria,
|
104
|
-
limit=limit,
|
105
|
-
container_url=self.container_url,
|
106
|
-
)
|
107
|
-
return result
|
108
|
-
|
109
|
-
def insert_one(self, record):
|
110
|
-
"""
|
111
|
-
Insert a single record into the collection.
|
112
|
-
|
113
|
-
Args:
|
114
|
-
record (dict): Record to insert.
|
115
|
-
|
116
|
-
Returns:
|
117
|
-
tuple: (response message, HTTP status code)
|
118
|
-
"""
|
119
|
-
if self._is_read_only():
|
120
|
-
return self._read_only_response()
|
121
|
-
return handle_insert_one(
|
122
|
-
user_id_cont=self.user_id,
|
123
|
-
database=self.db_id,
|
124
|
-
collection=self.coll_id,
|
125
|
-
record=record,
|
126
|
-
container_url=self.container_url,
|
127
|
-
)
|
128
|
-
|
129
|
-
def insert_many(self, data):
|
130
|
-
"""
|
131
|
-
Insert multiple records into the collection.
|
132
|
-
|
133
|
-
Args:
|
134
|
-
data (list of dicts): Multiple records to insert.
|
135
|
-
|
136
|
-
Returns:
|
137
|
-
tuple: (response message, HTTP status code)
|
138
|
-
"""
|
139
|
-
if self._is_read_only():
|
140
|
-
return self._read_only_response()
|
141
|
-
return handle_insert_many(
|
142
|
-
user_id_cont=self.user_id,
|
143
|
-
database=self.db_id,
|
144
|
-
collection=self.coll_id,
|
145
|
-
data=data,
|
146
|
-
container_url=self.container_url,
|
147
|
-
)
|
148
|
-
|
149
|
-
def delete_one(self, record_to_delete):
|
150
|
-
"""
|
151
|
-
Delete a single record based on its _id or nested key.
|
152
|
-
|
153
|
-
Args:
|
154
|
-
record_to_delete (dict): Identification of the record.
|
155
|
-
|
156
|
-
Returns:
|
157
|
-
tuple: (response message, HTTP status code)
|
158
|
-
"""
|
159
|
-
if self._is_read_only():
|
160
|
-
return self._read_only_response()
|
161
|
-
return handle_delete_one(
|
162
|
-
user_id=self.user_id,
|
163
|
-
db_id=self.db_id,
|
164
|
-
coll_id=self.coll_id,
|
165
|
-
data_to_delete=record_to_delete,
|
166
|
-
container_url=self.container_url,
|
167
|
-
)
|
168
|
-
|
169
|
-
def delete_many(self, records_to_delete_list, batch_size=10):
|
170
|
-
"""
|
171
|
-
Delete multiple records in batches.
|
172
|
-
|
173
|
-
Args:
|
174
|
-
records_to_delete_list (list): List of record identifiers.
|
175
|
-
batch_size (int): How many to delete at once (for efficiency).
|
176
|
-
|
177
|
-
Returns:
|
178
|
-
tuple: (response message, HTTP status code)
|
179
|
-
"""
|
180
|
-
if self._is_read_only():
|
181
|
-
return self._read_only_response()
|
182
|
-
return handle_delete_many(
|
183
|
-
user_id=self.user_id,
|
184
|
-
db_id=self.db_id,
|
185
|
-
coll_id=self.coll_id,
|
186
|
-
data_to_delete_list=records_to_delete_list,
|
187
|
-
batch_size=batch_size,
|
188
|
-
container_url=self.container_url,
|
189
|
-
)
|
190
|
-
|
191
|
-
def update_query(self, filter_query: dict, update_query: dict):
|
192
|
-
"""
|
193
|
-
Update a record in-place by filtering and applying update logic.
|
194
|
-
|
195
|
-
Args:
|
196
|
-
filter_query (dict): Dict describing what record(s) to match.
|
197
|
-
update_query (dict): Dict describing update operators ($set, $push, $remove).
|
198
|
-
|
199
|
-
Returns:
|
200
|
-
tuple: (response message, HTTP status code)
|
201
|
-
"""
|
202
|
-
if self._is_read_only():
|
203
|
-
return self._read_only_response()
|
204
|
-
return handle_update_query(
|
205
|
-
user_id=self.user_id,
|
206
|
-
db_id=self.db_id,
|
207
|
-
coll_id=self.coll_id,
|
208
|
-
filter_query=filter_query,
|
209
|
-
update_query=update_query,
|
210
|
-
container_url=self.container_url,
|
211
|
-
)
|
212
|
-
|
213
|
-
def ping(self):
|
214
|
-
"""
|
215
|
-
Health check / ping to verify collection accessibility.
|
216
|
-
|
217
|
-
Returns:
|
218
|
-
tuple: (response message, HTTP status code)
|
219
|
-
"""
|
220
|
-
return handle_connection_ping(
|
221
|
-
user_id=self.user_id,
|
222
|
-
db_id=self.db_id,
|
223
|
-
coll_id=self.coll_id,
|
224
|
-
container_url=self.container_url,
|
225
|
-
)
|
226
|
-
|
227
|
-
def insert_one_vector(self, vector_data, metadata):
|
228
|
-
"""
|
229
|
-
Insert a single record into the collection.
|
230
|
-
|
231
|
-
Args:
|
232
|
-
record (dict): Record to insert.
|
233
|
-
|
234
|
-
Returns:
|
235
|
-
tuple: (response message, HTTP status code)
|
236
|
-
"""
|
237
|
-
if self._is_read_only():
|
238
|
-
return self._read_only_response()
|
239
|
-
self._print_loading()
|
240
|
-
return handle_vector(
|
241
|
-
user_id_cont=self.user_id,
|
242
|
-
database=self.db_id,
|
243
|
-
collection=self.coll_id,
|
244
|
-
vector=vector_data,
|
245
|
-
metadata=metadata,
|
246
|
-
container_url=self.container_url,
|
247
|
-
)
|
248
|
-
|
249
|
-
def insert_many_vectors(self, records):
|
250
|
-
"""
|
251
|
-
Insert multiple vector records into the collection.
|
252
|
-
|
253
|
-
Args:
|
254
|
-
records (list): List of dicts, each with keys 'vector', 'metadata', and optional '_id'.
|
255
|
-
|
256
|
-
Returns:
|
257
|
-
tuple: (response message, HTTP status code)
|
258
|
-
"""
|
259
|
-
if self._is_read_only():
|
260
|
-
return self._read_only_response()
|
261
|
-
self._print_loading()
|
262
|
-
return handle_insert_many_vectors(
|
263
|
-
user_id_cont=self.user_id,
|
264
|
-
database=self.db_id,
|
265
|
-
collection=self.coll_id,
|
266
|
-
records=records,
|
267
|
-
container_url=self.container_url,
|
268
|
-
)
|
269
|
-
|
270
|
-
def classify_vector(self, k=3):
|
271
|
-
"""
|
272
|
-
Retrieve all vector records from the collection and classify them using k-NN.
|
273
|
-
|
274
|
-
Args:
|
275
|
-
k (int): Number of neighbors to use for classification.
|
276
|
-
|
277
|
-
Returns:
|
278
|
-
tuple: (response message, HTTP status code)
|
279
|
-
"""
|
280
|
-
if self._is_read_only():
|
281
|
-
return self._read_only_response()
|
282
|
-
self._print_loading()
|
283
|
-
return handle_classify_vector(
|
284
|
-
user_id=self.user_id,
|
285
|
-
db_id=self.db_id,
|
286
|
-
coll_id=self.coll_id,
|
287
|
-
container_url=self.container_url,
|
288
|
-
k=k
|
289
|
-
)
|
290
|
-
|
291
|
-
|
292
|
-
def __str__(self):
|
293
|
-
"""Pretty print the collection metadata."""
|
294
|
-
return json.dumps(self.metadata, indent=4)
|
295
|
-
|
296
|
-
def __repr__(self):
|
297
|
-
"""Simplified representation for debugging or introspection."""
|
298
|
-
return f"<Collection coll_id={self.coll_id}>"
|