omniql 0.5.0__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.
- omniql-0.5.0/PKG-INFO +30 -0
- omniql-0.5.0/README.md +20 -0
- omniql-0.5.0/omniql/__init__.py +273 -0
- omniql-0.5.0/omniql/libomniql.dylib +0 -0
- omniql-0.5.0/omniql/libomniql.so +0 -0
- omniql-0.5.0/omniql/omniql.dll +0 -0
- omniql-0.5.0/omniql.egg-info/PKG-INFO +30 -0
- omniql-0.5.0/omniql.egg-info/SOURCES.txt +15 -0
- omniql-0.5.0/omniql.egg-info/dependency_links.txt +1 -0
- omniql-0.5.0/omniql.egg-info/requires.txt +1 -0
- omniql-0.5.0/omniql.egg-info/top_level.txt +1 -0
- omniql-0.5.0/pyproject.toml +24 -0
- omniql-0.5.0/setup.cfg +4 -0
omniql-0.5.0/PKG-INFO
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: omniql
|
|
3
|
+
Version: 0.5.0
|
|
4
|
+
Summary: A standardized, multi-database query language (OQL) for Python.
|
|
5
|
+
Author-email: Uttam Mahata <uttam-mahata-cs@outlook.com>
|
|
6
|
+
License: Apache License 2.0
|
|
7
|
+
Requires-Python: >=3.8
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Requires-Dist: cffi>=1.0.0
|
|
10
|
+
|
|
11
|
+
# OmniQL for Python
|
|
12
|
+
|
|
13
|
+
A standardized, multi-database query language (OQL) for Python.
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pip install omniql
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
```python
|
|
24
|
+
import asyncio
|
|
25
|
+
from omniql import OmniEngine, Query
|
|
26
|
+
|
|
27
|
+
async def main():
|
|
28
|
+
engine = OmniEngine()
|
|
29
|
+
# ...
|
|
30
|
+
```
|
omniql-0.5.0/README.md
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# OmniQL for Python
|
|
2
|
+
|
|
3
|
+
A standardized, multi-database query language (OQL) for Python.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install omniql
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
import asyncio
|
|
15
|
+
from omniql import OmniEngine, Query
|
|
16
|
+
|
|
17
|
+
async def main():
|
|
18
|
+
engine = OmniEngine()
|
|
19
|
+
# ...
|
|
20
|
+
```
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
"""
|
|
2
|
+
OmniQL Python Binding
|
|
3
|
+
=====================
|
|
4
|
+
|
|
5
|
+
A native extension built with CFFI that wraps the OmniQL shared library
|
|
6
|
+
(``libomniql.so`` / ``omniql.dll``) and exposes an asyncio-compatible API.
|
|
7
|
+
|
|
8
|
+
Build the native library first::
|
|
9
|
+
|
|
10
|
+
go build -buildmode=c-shared -o libomniql.so ../../pkg/ffi
|
|
11
|
+
|
|
12
|
+
Then install the Python package::
|
|
13
|
+
|
|
14
|
+
pip install .
|
|
15
|
+
|
|
16
|
+
Example usage::
|
|
17
|
+
|
|
18
|
+
import asyncio
|
|
19
|
+
from omniql import OmniEngine, Query
|
|
20
|
+
|
|
21
|
+
async def main():
|
|
22
|
+
engine = OmniEngine()
|
|
23
|
+
|
|
24
|
+
# 1. Register a driver and bind a target to it.
|
|
25
|
+
driver_name = await engine.register_sqlite_driver(":memory:")
|
|
26
|
+
await engine.route("analytics_data", driver_name)
|
|
27
|
+
|
|
28
|
+
# 2. Execute a query.
|
|
29
|
+
result = await engine.execute(
|
|
30
|
+
Query(
|
|
31
|
+
target="analytics_data",
|
|
32
|
+
action="FIND",
|
|
33
|
+
filter={
|
|
34
|
+
"category": {"$in": ["electronics", "books"]},
|
|
35
|
+
"price": {"$lt": 500},
|
|
36
|
+
},
|
|
37
|
+
options={"limit": 20},
|
|
38
|
+
)
|
|
39
|
+
)
|
|
40
|
+
print(result.data)
|
|
41
|
+
|
|
42
|
+
asyncio.run(main())
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
from __future__ import annotations
|
|
46
|
+
|
|
47
|
+
import asyncio
|
|
48
|
+
import json
|
|
49
|
+
import os
|
|
50
|
+
import threading
|
|
51
|
+
from dataclasses import dataclass, field, asdict
|
|
52
|
+
from typing import Any, Dict, List, Optional
|
|
53
|
+
|
|
54
|
+
import cffi # type: ignore
|
|
55
|
+
|
|
56
|
+
# ---------------------------------------------------------------------------
|
|
57
|
+
# Load the native library via CFFI
|
|
58
|
+
# ---------------------------------------------------------------------------
|
|
59
|
+
|
|
60
|
+
_ffi = cffi.FFI()
|
|
61
|
+
_ffi.cdef(
|
|
62
|
+
"""
|
|
63
|
+
int OmniQL_NewEngine(void);
|
|
64
|
+
void OmniQL_FreeEngine(int handle);
|
|
65
|
+
char * OmniQL_Execute(int handle, const char *queryJson);
|
|
66
|
+
char * OmniQL_RegisterSchema(int handle, const char *schemaJson);
|
|
67
|
+
char * OmniQL_Route(int handle, const char *target, const char *driverName);
|
|
68
|
+
char * OmniQL_RegisterSQLiteDriver(int handle, const char *dsn);
|
|
69
|
+
char * OmniQL_RegisterPostgresDriver(int handle, const char *connStr);
|
|
70
|
+
char * OmniQL_RegisterMongoDriver(int handle, const char *uri, const char *dbName);
|
|
71
|
+
void OmniQL_Free(char *ptr);
|
|
72
|
+
"""
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
_LIB_SEARCH_PATHS = [
|
|
76
|
+
os.path.join(os.path.dirname(__file__), "libomniql.so"),
|
|
77
|
+
os.path.join(os.path.dirname(__file__), "omniql.dll"),
|
|
78
|
+
os.path.join(os.path.dirname(__file__), "libomniql.dylib"),
|
|
79
|
+
"libomniql.so",
|
|
80
|
+
"omniql.dll",
|
|
81
|
+
"libomniql.dylib",
|
|
82
|
+
]
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _load_lib():
|
|
86
|
+
for path in _LIB_SEARCH_PATHS:
|
|
87
|
+
if os.path.exists(path):
|
|
88
|
+
return _ffi.dlopen(path)
|
|
89
|
+
raise OSError(
|
|
90
|
+
"OmniQL native library not found. "
|
|
91
|
+
"Build it with: go build -buildmode=c-shared -o libomniql.so ../../pkg/ffi"
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
_lib = _load_lib()
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
# ---------------------------------------------------------------------------
|
|
99
|
+
# Python data classes
|
|
100
|
+
# ---------------------------------------------------------------------------
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@dataclass
|
|
104
|
+
class QueryOptions:
|
|
105
|
+
limit: int = 0
|
|
106
|
+
skip: int = 0
|
|
107
|
+
sort: Dict[str, int] = field(default_factory=dict)
|
|
108
|
+
fields: Dict[str, Any] = field(default_factory=dict)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
@dataclass
|
|
112
|
+
class Query:
|
|
113
|
+
"""An OQL query object."""
|
|
114
|
+
|
|
115
|
+
target: str
|
|
116
|
+
action: str = "FIND"
|
|
117
|
+
filter: Dict[str, Any] = field(default_factory=dict)
|
|
118
|
+
document: Dict[str, Any] = field(default_factory=dict)
|
|
119
|
+
options: QueryOptions = field(default_factory=QueryOptions)
|
|
120
|
+
|
|
121
|
+
def to_dict(self) -> dict:
|
|
122
|
+
return asdict(self)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
@dataclass
|
|
126
|
+
class OmniMeta:
|
|
127
|
+
total: int = 0
|
|
128
|
+
returned: int = 0
|
|
129
|
+
driver: str = ""
|
|
130
|
+
target: str = ""
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
@dataclass
|
|
134
|
+
class OmniError:
|
|
135
|
+
code: str = ""
|
|
136
|
+
message: str = ""
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
@dataclass
|
|
140
|
+
class OmniResult:
|
|
141
|
+
"""The standardised OmniJSON response."""
|
|
142
|
+
|
|
143
|
+
data: List[Dict[str, Any]] = field(default_factory=list)
|
|
144
|
+
meta: OmniMeta = field(default_factory=OmniMeta)
|
|
145
|
+
error: Optional[OmniError] = None
|
|
146
|
+
|
|
147
|
+
@classmethod
|
|
148
|
+
def from_dict(cls, d: dict) -> "OmniResult":
|
|
149
|
+
meta = OmniMeta(**d.get("meta", {}))
|
|
150
|
+
err_data = d.get("error")
|
|
151
|
+
err = OmniError(**err_data) if err_data else None
|
|
152
|
+
return cls(data=d.get("data", []), meta=meta, error=err)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
# ---------------------------------------------------------------------------
|
|
156
|
+
# OmniEngine
|
|
157
|
+
# ---------------------------------------------------------------------------
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
class OmniEngine:
|
|
161
|
+
"""High-level wrapper around the native OmniQL Core Engine.
|
|
162
|
+
|
|
163
|
+
The engine is thread-safe and supports asyncio via
|
|
164
|
+
:meth:`execute` (async) or :meth:`execute_sync` (synchronous).
|
|
165
|
+
"""
|
|
166
|
+
|
|
167
|
+
def __init__(self) -> None:
|
|
168
|
+
self._handle: int = _lib.OmniQL_NewEngine()
|
|
169
|
+
self._lock = threading.Lock()
|
|
170
|
+
|
|
171
|
+
def __del__(self) -> None:
|
|
172
|
+
try:
|
|
173
|
+
_lib.OmniQL_FreeEngine(self._handle)
|
|
174
|
+
except Exception:
|
|
175
|
+
pass
|
|
176
|
+
|
|
177
|
+
# ------------------------------------------------------------------
|
|
178
|
+
# Synchronous API
|
|
179
|
+
# ------------------------------------------------------------------
|
|
180
|
+
|
|
181
|
+
def execute_sync(self, query: Query) -> OmniResult:
|
|
182
|
+
"""Execute a query synchronously and return the result."""
|
|
183
|
+
query_json = json.dumps(query.to_dict()).encode("utf-8")
|
|
184
|
+
with self._lock:
|
|
185
|
+
raw = _lib.OmniQL_Execute(self._handle, query_json)
|
|
186
|
+
try:
|
|
187
|
+
response = json.loads(_ffi.string(raw).decode("utf-8"))
|
|
188
|
+
finally:
|
|
189
|
+
_lib.OmniQL_Free(raw)
|
|
190
|
+
return OmniResult.from_dict(response)
|
|
191
|
+
|
|
192
|
+
def register_schema_sync(self, schema: dict) -> None:
|
|
193
|
+
"""Register a collection schema synchronously."""
|
|
194
|
+
schema_json = json.dumps(schema).encode("utf-8")
|
|
195
|
+
with self._lock:
|
|
196
|
+
raw = _lib.OmniQL_RegisterSchema(self._handle, schema_json)
|
|
197
|
+
_lib.OmniQL_Free(raw)
|
|
198
|
+
|
|
199
|
+
def route_sync(self, target: str, driver_name: str) -> None:
|
|
200
|
+
"""Bind a target collection/table to a driver name synchronously."""
|
|
201
|
+
with self._lock:
|
|
202
|
+
raw = _lib.OmniQL_Route(self._handle, target.encode(), driver_name.encode())
|
|
203
|
+
_lib.OmniQL_Free(raw)
|
|
204
|
+
|
|
205
|
+
def register_sqlite_driver_sync(self, dsn: str) -> str:
|
|
206
|
+
"""Register a SQLite driver synchronously. Returns the driver name."""
|
|
207
|
+
with self._lock:
|
|
208
|
+
raw = _lib.OmniQL_RegisterSQLiteDriver(self._handle, dsn.encode())
|
|
209
|
+
try:
|
|
210
|
+
result = json.loads(_ffi.string(raw).decode())
|
|
211
|
+
return result.get("driver", "sqlite")
|
|
212
|
+
finally:
|
|
213
|
+
_lib.OmniQL_Free(raw)
|
|
214
|
+
|
|
215
|
+
def register_postgres_driver_sync(self, conn_str: str) -> str:
|
|
216
|
+
"""Register a PostgreSQL driver synchronously. Returns the driver name."""
|
|
217
|
+
with self._lock:
|
|
218
|
+
raw = _lib.OmniQL_RegisterPostgresDriver(self._handle, conn_str.encode())
|
|
219
|
+
try:
|
|
220
|
+
result = json.loads(_ffi.string(raw).decode())
|
|
221
|
+
return result.get("driver", "postgres")
|
|
222
|
+
finally:
|
|
223
|
+
_lib.OmniQL_Free(raw)
|
|
224
|
+
|
|
225
|
+
def register_mongo_driver_sync(self, uri: str, db_name: str) -> str:
|
|
226
|
+
"""Register a MongoDB driver synchronously. Returns the driver name."""
|
|
227
|
+
with self._lock:
|
|
228
|
+
raw = _lib.OmniQL_RegisterMongoDriver(self._handle, uri.encode(), db_name.encode())
|
|
229
|
+
try:
|
|
230
|
+
result = json.loads(_ffi.string(raw).decode())
|
|
231
|
+
return result.get("driver", "mongo")
|
|
232
|
+
finally:
|
|
233
|
+
_lib.OmniQL_Free(raw)
|
|
234
|
+
|
|
235
|
+
# ------------------------------------------------------------------
|
|
236
|
+
# Asyncio API
|
|
237
|
+
# ------------------------------------------------------------------
|
|
238
|
+
|
|
239
|
+
def _get_loop(self) -> asyncio.AbstractEventLoop:
|
|
240
|
+
try:
|
|
241
|
+
return asyncio.get_running_loop()
|
|
242
|
+
except RuntimeError:
|
|
243
|
+
return asyncio.get_event_loop()
|
|
244
|
+
|
|
245
|
+
async def execute(self, query: Query) -> OmniResult:
|
|
246
|
+
"""Execute a query asynchronously (runs in a thread-pool executor)."""
|
|
247
|
+
loop = self._get_loop()
|
|
248
|
+
return await loop.run_in_executor(None, self.execute_sync, query)
|
|
249
|
+
|
|
250
|
+
async def register_schema(self, schema: dict) -> None:
|
|
251
|
+
"""Register a schema asynchronously."""
|
|
252
|
+
loop = self._get_loop()
|
|
253
|
+
await loop.run_in_executor(None, self.register_schema_sync, schema)
|
|
254
|
+
|
|
255
|
+
async def route(self, target: str, driver_name: str) -> None:
|
|
256
|
+
"""Bind a target collection/table to a driver name asynchronously."""
|
|
257
|
+
loop = self._get_loop()
|
|
258
|
+
await loop.run_in_executor(None, self.route_sync, target, driver_name)
|
|
259
|
+
|
|
260
|
+
async def register_sqlite_driver(self, dsn: str) -> str:
|
|
261
|
+
"""Register a SQLite driver asynchronously. Returns the driver name."""
|
|
262
|
+
loop = self._get_loop()
|
|
263
|
+
return await loop.run_in_executor(None, self.register_sqlite_driver_sync, dsn)
|
|
264
|
+
|
|
265
|
+
async def register_postgres_driver(self, conn_str: str) -> str:
|
|
266
|
+
"""Register a PostgreSQL driver asynchronously. Returns the driver name."""
|
|
267
|
+
loop = self._get_loop()
|
|
268
|
+
return await loop.run_in_executor(None, self.register_postgres_driver_sync, conn_str)
|
|
269
|
+
|
|
270
|
+
async def register_mongo_driver(self, uri: str, db_name: str) -> str:
|
|
271
|
+
"""Register a MongoDB driver asynchronously. Returns the driver name."""
|
|
272
|
+
loop = self._get_loop()
|
|
273
|
+
return await loop.run_in_executor(None, self.register_mongo_driver_sync, uri, db_name)
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: omniql
|
|
3
|
+
Version: 0.5.0
|
|
4
|
+
Summary: A standardized, multi-database query language (OQL) for Python.
|
|
5
|
+
Author-email: Uttam Mahata <uttam-mahata-cs@outlook.com>
|
|
6
|
+
License: Apache License 2.0
|
|
7
|
+
Requires-Python: >=3.8
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Requires-Dist: cffi>=1.0.0
|
|
10
|
+
|
|
11
|
+
# OmniQL for Python
|
|
12
|
+
|
|
13
|
+
A standardized, multi-database query language (OQL) for Python.
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pip install omniql
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
```python
|
|
24
|
+
import asyncio
|
|
25
|
+
from omniql import OmniEngine, Query
|
|
26
|
+
|
|
27
|
+
async def main():
|
|
28
|
+
engine = OmniEngine()
|
|
29
|
+
# ...
|
|
30
|
+
```
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
./omniql/__init__.py
|
|
4
|
+
./omniql/libomniql.dylib
|
|
5
|
+
./omniql/libomniql.so
|
|
6
|
+
./omniql/omniql.dll
|
|
7
|
+
omniql/__init__.py
|
|
8
|
+
omniql/libomniql.dylib
|
|
9
|
+
omniql/libomniql.so
|
|
10
|
+
omniql/omniql.dll
|
|
11
|
+
omniql.egg-info/PKG-INFO
|
|
12
|
+
omniql.egg-info/SOURCES.txt
|
|
13
|
+
omniql.egg-info/dependency_links.txt
|
|
14
|
+
omniql.egg-info/requires.txt
|
|
15
|
+
omniql.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
cffi>=1.0.0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
omniql
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "cffi>=1.0.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "omniql"
|
|
7
|
+
version = "0.5.0"
|
|
8
|
+
description = "A standardized, multi-database query language (OQL) for Python."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.8"
|
|
11
|
+
license = {text = "Apache License 2.0"}
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "Uttam Mahata", email = "uttam-mahata-cs@outlook.com"},
|
|
14
|
+
]
|
|
15
|
+
dependencies = [
|
|
16
|
+
"cffi>=1.0.0",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
[tool.setuptools]
|
|
20
|
+
packages = ["omniql"]
|
|
21
|
+
package-dir = {"" = "."}
|
|
22
|
+
|
|
23
|
+
[tool.setuptools.package-data]
|
|
24
|
+
omniql = ["*.so", "*.dll", "*.dylib"]
|
omniql-0.5.0/setup.cfg
ADDED