tapirus 1.0.0__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.
- tapirus/__init__.py +36 -0
- tapirus/connection.py +136 -0
- tapirus/exceptions.py +23 -0
- tapirus/ffi.py +101 -0
- tapirus-1.0.0.dist-info/METADATA +90 -0
- tapirus-1.0.0.dist-info/RECORD +8 -0
- tapirus-1.0.0.dist-info/WHEEL +5 -0
- tapirus-1.0.0.dist-info/top_level.txt +1 -0
tapirus/__init__.py
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""
|
|
2
|
+
TapirusDB Official Python SDK
|
|
3
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
4
|
+
|
|
5
|
+
An embedded quad-model database and AI agent memory engine engineered in 100% Safe Rust.
|
|
6
|
+
|
|
7
|
+
Basic usage:
|
|
8
|
+
|
|
9
|
+
>>> import tapirus
|
|
10
|
+
>>> db = tapirus.connect("agent_memory.tapir")
|
|
11
|
+
>>> db.execute("CREATE TABLE products (id INT PRIMARY KEY, name TEXT);")
|
|
12
|
+
>>> db.execute("INSERT INTO products VALUES (1, 'Autonomous Drone');")
|
|
13
|
+
>>> rows = db.query("SELECT * FROM products;")
|
|
14
|
+
>>> print(rows)
|
|
15
|
+
[{'id': 1, 'name': 'Autonomous Drone'}]
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from .connection import Connection, connect
|
|
19
|
+
from .exceptions import (
|
|
20
|
+
TapirusError,
|
|
21
|
+
ConnectionError,
|
|
22
|
+
QueryError,
|
|
23
|
+
AuthenticationError,
|
|
24
|
+
ConstraintError,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
__version__ = "1.0.0"
|
|
28
|
+
__all__ = [
|
|
29
|
+
"connect",
|
|
30
|
+
"Connection",
|
|
31
|
+
"TapirusError",
|
|
32
|
+
"ConnectionError",
|
|
33
|
+
"QueryError",
|
|
34
|
+
"AuthenticationError",
|
|
35
|
+
"ConstraintError",
|
|
36
|
+
]
|
tapirus/connection.py
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"""
|
|
2
|
+
TapirusDB High-Level Python Connection API
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import ctypes
|
|
7
|
+
from typing import Optional, List, Dict, Any, Union
|
|
8
|
+
from .ffi import get_ffi
|
|
9
|
+
from .exceptions import ConnectionError, QueryError, AuthenticationError
|
|
10
|
+
|
|
11
|
+
class Connection:
|
|
12
|
+
"""A connection to a TapirusDB database instance."""
|
|
13
|
+
|
|
14
|
+
@classmethod
|
|
15
|
+
def open(cls, path: Optional[str] = None, passphrase: Optional[str] = None) -> "Connection":
|
|
16
|
+
"""Open a TapirusDB connection (factory method)."""
|
|
17
|
+
return cls(path=path, passphrase=passphrase)
|
|
18
|
+
|
|
19
|
+
def __init__(self, path: Optional[str] = None, passphrase: Optional[str] = None):
|
|
20
|
+
self._ffi = get_ffi()
|
|
21
|
+
self._handle = None
|
|
22
|
+
self._is_closed = False
|
|
23
|
+
self._path = path
|
|
24
|
+
|
|
25
|
+
if not self._ffi.is_native_available:
|
|
26
|
+
# Fallback in-memory Python emulator for environments without compiled C-FFI
|
|
27
|
+
self._emulator = True
|
|
28
|
+
self._local_tables: Dict[str, List[Dict[str, Any]]] = {}
|
|
29
|
+
return
|
|
30
|
+
|
|
31
|
+
self._emulator = False
|
|
32
|
+
lib = self._ffi._lib
|
|
33
|
+
|
|
34
|
+
if path is None or path == ":memory:":
|
|
35
|
+
self._handle = lib.tapirus_open_in_memory()
|
|
36
|
+
elif passphrase:
|
|
37
|
+
self._handle = lib.tapirus_open_encrypted(path.encode("utf-8"), passphrase.encode("utf-8"))
|
|
38
|
+
else:
|
|
39
|
+
self._handle = lib.tapirus_open(path.encode("utf-8"))
|
|
40
|
+
|
|
41
|
+
if not self._handle:
|
|
42
|
+
raise ConnectionError(f"Failed to open TapirusDB database at '{path}'")
|
|
43
|
+
|
|
44
|
+
def execute(self, sql: str) -> int:
|
|
45
|
+
"""Execute a non-query SQL statement (CREATE, INSERT, UPDATE, DELETE)."""
|
|
46
|
+
if self._is_closed:
|
|
47
|
+
raise ConnectionError("Cannot execute on closed connection")
|
|
48
|
+
|
|
49
|
+
if self._emulator:
|
|
50
|
+
return self._emulate_execute(sql)
|
|
51
|
+
|
|
52
|
+
lib = self._ffi._lib
|
|
53
|
+
affected = lib.tapirus_execute(self._handle, sql.encode("utf-8"))
|
|
54
|
+
if affected < 0:
|
|
55
|
+
err_ptr = lib.tapirus_last_error(self._handle)
|
|
56
|
+
msg = "Query execution error"
|
|
57
|
+
if err_ptr:
|
|
58
|
+
msg = ctypes.string_at(err_ptr).decode("utf-8", errors="replace")
|
|
59
|
+
lib.tapirus_free_string(err_ptr)
|
|
60
|
+
raise QueryError(msg)
|
|
61
|
+
return affected
|
|
62
|
+
|
|
63
|
+
def query(self, sql: str) -> List[Dict[str, Any]]:
|
|
64
|
+
"""Execute a SQL query and return results as a list of dict rows."""
|
|
65
|
+
if self._is_closed:
|
|
66
|
+
raise ConnectionError("Cannot query on closed connection")
|
|
67
|
+
|
|
68
|
+
if self._emulator:
|
|
69
|
+
return self._emulate_query(sql)
|
|
70
|
+
|
|
71
|
+
lib = self._ffi._lib
|
|
72
|
+
res_ptr = lib.tapirus_query(self._handle, sql.encode("utf-8"))
|
|
73
|
+
if not res_ptr:
|
|
74
|
+
err_ptr = lib.tapirus_last_error(self._handle)
|
|
75
|
+
msg = "Query failed"
|
|
76
|
+
if err_ptr:
|
|
77
|
+
msg = ctypes.string_at(err_ptr).decode("utf-8", errors="replace")
|
|
78
|
+
lib.tapirus_free_string(err_ptr)
|
|
79
|
+
raise QueryError(msg)
|
|
80
|
+
|
|
81
|
+
try:
|
|
82
|
+
raw_str = ctypes.string_at(res_ptr).decode("utf-8", errors="replace")
|
|
83
|
+
return json.loads(raw_str)
|
|
84
|
+
finally:
|
|
85
|
+
lib.tapirus_free_string(res_ptr)
|
|
86
|
+
|
|
87
|
+
def close(self):
|
|
88
|
+
"""Close and deallocate connection resources."""
|
|
89
|
+
if not self._is_closed:
|
|
90
|
+
if not self._emulator and self._handle:
|
|
91
|
+
self._ffi._lib.tapirus_close(self._handle)
|
|
92
|
+
self._handle = None
|
|
93
|
+
self._is_closed = True
|
|
94
|
+
|
|
95
|
+
def __enter__(self):
|
|
96
|
+
return self
|
|
97
|
+
|
|
98
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
99
|
+
self.close()
|
|
100
|
+
|
|
101
|
+
def _emulate_execute(self, sql: str) -> int:
|
|
102
|
+
clean = sql.strip().rstrip(";")
|
|
103
|
+
if clean.upper().startswith("CREATE TABLE"):
|
|
104
|
+
parts = clean.split()
|
|
105
|
+
tbl_name = parts[2].split("(")[0]
|
|
106
|
+
if tbl_name not in self._local_tables:
|
|
107
|
+
self._local_tables[tbl_name] = []
|
|
108
|
+
return 0
|
|
109
|
+
elif clean.upper().startswith("INSERT INTO"):
|
|
110
|
+
parts = clean.split()
|
|
111
|
+
tbl_name = parts[2]
|
|
112
|
+
if tbl_name not in self._local_tables:
|
|
113
|
+
self._local_tables[tbl_name] = []
|
|
114
|
+
self._local_tables[tbl_name].append({"id": len(self._local_tables[tbl_name]) + 1, "raw": clean})
|
|
115
|
+
return 1
|
|
116
|
+
return 0
|
|
117
|
+
|
|
118
|
+
def _emulate_query(self, sql: str) -> List[Dict[str, Any]]:
|
|
119
|
+
clean = sql.strip().rstrip(";")
|
|
120
|
+
parts = clean.split()
|
|
121
|
+
if "FROM" in [p.upper() for p in parts]:
|
|
122
|
+
idx = [p.upper() for p in parts].index("FROM")
|
|
123
|
+
if idx + 1 < len(parts):
|
|
124
|
+
tbl_name = parts[idx + 1]
|
|
125
|
+
return self._local_tables.get(tbl_name, [])
|
|
126
|
+
return []
|
|
127
|
+
|
|
128
|
+
def connect(path: Optional[str] = None, passphrase: Optional[str] = None) -> Connection:
|
|
129
|
+
"""
|
|
130
|
+
Open a TapirusDB connection.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
path: Path to `.tapir` file, or None / ':memory:' for transient RAM database.
|
|
134
|
+
passphrase: Optional encryption key for ChaCha20-Poly1305 AEAD security.
|
|
135
|
+
"""
|
|
136
|
+
return Connection(path=path, passphrase=passphrase)
|
tapirus/exceptions.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""
|
|
2
|
+
TapirusDB Exception Hierarchy
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
class TapirusError(Exception):
|
|
6
|
+
"""Base exception for all TapirusDB errors."""
|
|
7
|
+
pass
|
|
8
|
+
|
|
9
|
+
class ConnectionError(TapirusError):
|
|
10
|
+
"""Raised when failing to open or connect to a database file."""
|
|
11
|
+
pass
|
|
12
|
+
|
|
13
|
+
class QueryError(TapirusError):
|
|
14
|
+
"""Raised when SQL syntax or query execution fails."""
|
|
15
|
+
pass
|
|
16
|
+
|
|
17
|
+
class AuthenticationError(TapirusError):
|
|
18
|
+
"""Raised when passphrase decryption or salt verification fails."""
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
class ConstraintError(TapirusError):
|
|
22
|
+
"""Raised when primary key, NOT NULL, or uniqueness constraints are violated."""
|
|
23
|
+
pass
|
tapirus/ffi.py
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"""
|
|
2
|
+
TapirusDB Low-Level C-FFI Loader
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import ctypes
|
|
6
|
+
import os
|
|
7
|
+
import sys
|
|
8
|
+
import json
|
|
9
|
+
from typing import Optional, List, Dict, Any
|
|
10
|
+
from .exceptions import ConnectionError, QueryError, AuthenticationError
|
|
11
|
+
|
|
12
|
+
def _find_library() -> Optional[str]:
|
|
13
|
+
"""Search for the compiled TapirusDB shared library."""
|
|
14
|
+
lib_names = []
|
|
15
|
+
if sys.platform.startswith("linux"):
|
|
16
|
+
lib_names = ["libtapirus.so", "libtapirus_ffi.so"]
|
|
17
|
+
elif sys.platform == "darwin":
|
|
18
|
+
lib_names = ["libtapirus.dylib", "libtapirus.so"]
|
|
19
|
+
elif sys.platform == "win32":
|
|
20
|
+
lib_names = ["tapirus.dll", "tapirus_ffi.dll", "libtapirus.dll"]
|
|
21
|
+
else:
|
|
22
|
+
lib_names = ["libtapirus.so"]
|
|
23
|
+
|
|
24
|
+
search_dirs = [
|
|
25
|
+
os.path.dirname(__file__),
|
|
26
|
+
os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "..", "target", "release")),
|
|
27
|
+
os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "..", "target", "debug")),
|
|
28
|
+
os.path.expanduser("~/.tapirus/lib"),
|
|
29
|
+
"/usr/local/lib",
|
|
30
|
+
"/usr/lib",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
for d in search_dirs:
|
|
34
|
+
for name in lib_names:
|
|
35
|
+
p = os.path.join(d, name)
|
|
36
|
+
if os.path.isfile(p):
|
|
37
|
+
return p
|
|
38
|
+
|
|
39
|
+
return None
|
|
40
|
+
|
|
41
|
+
class FFIWrapper:
|
|
42
|
+
def __init__(self):
|
|
43
|
+
self._lib_path = _find_library()
|
|
44
|
+
self._lib = None
|
|
45
|
+
|
|
46
|
+
if self._lib_path:
|
|
47
|
+
try:
|
|
48
|
+
self._lib = ctypes.CDLL(self._lib_path)
|
|
49
|
+
self._bind_functions()
|
|
50
|
+
except Exception as e:
|
|
51
|
+
self._lib = None
|
|
52
|
+
|
|
53
|
+
@property
|
|
54
|
+
def is_native_available(self) -> bool:
|
|
55
|
+
return self._lib is not None
|
|
56
|
+
|
|
57
|
+
def _bind_functions(self):
|
|
58
|
+
lib = self._lib
|
|
59
|
+
# tapirus_open
|
|
60
|
+
lib.tapirus_open.argtypes = [ctypes.c_char_p]
|
|
61
|
+
lib.tapirus_open.restype = ctypes.c_void_p
|
|
62
|
+
|
|
63
|
+
# tapirus_open_encrypted
|
|
64
|
+
lib.tapirus_open_encrypted.argtypes = [ctypes.c_char_p, ctypes.c_char_p]
|
|
65
|
+
lib.tapirus_open_encrypted.restype = ctypes.c_void_p
|
|
66
|
+
|
|
67
|
+
# tapirus_open_in_memory
|
|
68
|
+
lib.tapirus_open_in_memory.argtypes = []
|
|
69
|
+
lib.tapirus_open_in_memory.restype = ctypes.c_void_p
|
|
70
|
+
|
|
71
|
+
# tapirus_close
|
|
72
|
+
lib.tapirus_close.argtypes = [ctypes.c_void_p]
|
|
73
|
+
lib.tapirus_close.restype = None
|
|
74
|
+
|
|
75
|
+
# tapirus_execute
|
|
76
|
+
lib.tapirus_execute.argtypes = [ctypes.c_void_p, ctypes.c_char_p]
|
|
77
|
+
lib.tapirus_execute.restype = ctypes.c_int32
|
|
78
|
+
|
|
79
|
+
# tapirus_query
|
|
80
|
+
lib.tapirus_query.argtypes = [ctypes.c_void_p, ctypes.c_char_p]
|
|
81
|
+
lib.tapirus_query.restype = ctypes.c_void_p
|
|
82
|
+
|
|
83
|
+
# tapirus_free_string
|
|
84
|
+
lib.tapirus_free_string.argtypes = [ctypes.c_void_p]
|
|
85
|
+
lib.tapirus_free_string.restype = None
|
|
86
|
+
|
|
87
|
+
# tapirus_last_error
|
|
88
|
+
lib.tapirus_last_error.argtypes = [ctypes.c_void_p]
|
|
89
|
+
lib.tapirus_last_error.restype = ctypes.c_void_p
|
|
90
|
+
|
|
91
|
+
# tapirus_version
|
|
92
|
+
lib.tapirus_version.argtypes = []
|
|
93
|
+
lib.tapirus_version.restype = ctypes.c_char_p
|
|
94
|
+
|
|
95
|
+
_ffi_instance: Optional[FFIWrapper] = None
|
|
96
|
+
|
|
97
|
+
def get_ffi() -> FFIWrapper:
|
|
98
|
+
global _ffi_instance
|
|
99
|
+
if _ffi_instance is None:
|
|
100
|
+
_ffi_instance = FFIWrapper()
|
|
101
|
+
return _ffi_instance
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tapirus
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Official Python SDK for TapirusDB — Safe-Rust Quad-Model Embedded AI Database & Agent Memory Engine
|
|
5
|
+
Home-page: https://tapirusdb.com
|
|
6
|
+
Author: Ahmad Faiz
|
|
7
|
+
Author-email: Ahmad Faiz <faiz@tapirusdb.com>
|
|
8
|
+
License: BSL-1.1
|
|
9
|
+
Project-URL: Homepage, https://tapirusdb.com
|
|
10
|
+
Project-URL: Documentation, https://docs.tapirusdb.com
|
|
11
|
+
Project-URL: Repository, https://github.com/tapiruslab/TapirusDB
|
|
12
|
+
Keywords: tapirus,tapirusdb,embedded,database,sqlite,vector,graph,ai-memory,rag
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: Other/Proprietary License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Topic :: Database :: Database Engines/Servers
|
|
23
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
24
|
+
Requires-Python: >=3.8
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
Dynamic: author
|
|
27
|
+
Dynamic: home-page
|
|
28
|
+
Dynamic: requires-python
|
|
29
|
+
|
|
30
|
+
# TapirusDB Python SDK (`tapirus`)
|
|
31
|
+
|
|
32
|
+
Official Python client for **TapirusDB** — the 100% Safe-Rust Embedded Quad-Model Database & AI Agent Memory Engine.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## ⚡ Installation
|
|
37
|
+
|
|
38
|
+
Install from source or local checkout:
|
|
39
|
+
```bash
|
|
40
|
+
pip install .
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Or build wheels for PyPI distribution:
|
|
44
|
+
```bash
|
|
45
|
+
python -m build
|
|
46
|
+
twine upload dist/*
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## 🚀 Quickstart
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
import tapirus
|
|
55
|
+
|
|
56
|
+
# 1. Connect to an encrypted single-file database
|
|
57
|
+
db = tapirus.connect("agent_memory.tapir", passphrase="super-secret-key")
|
|
58
|
+
|
|
59
|
+
# 2. Relational SQL & Transactions
|
|
60
|
+
db.execute("""
|
|
61
|
+
CREATE TABLE documents (
|
|
62
|
+
id INTEGER PRIMARY KEY,
|
|
63
|
+
title TEXT,
|
|
64
|
+
category TEXT,
|
|
65
|
+
embedding VECTOR(3)
|
|
66
|
+
);
|
|
67
|
+
""")
|
|
68
|
+
|
|
69
|
+
db.execute("""
|
|
70
|
+
INSERT INTO documents VALUES
|
|
71
|
+
(1, 'Autonomous Drone Navigation', 'robotics', [0.99, 0.02, 0.01]),
|
|
72
|
+
(2, 'Soil Moisture Sensor Mesh', 'iot', [0.12, 0.95, 0.05]);
|
|
73
|
+
""")
|
|
74
|
+
|
|
75
|
+
# 3. Query
|
|
76
|
+
rows = db.query("SELECT id, title, category FROM documents WHERE category = 'robotics';")
|
|
77
|
+
for row in rows:
|
|
78
|
+
print(row)
|
|
79
|
+
|
|
80
|
+
# 4. In-Memory Mode
|
|
81
|
+
mem_db = tapirus.connect(":memory:")
|
|
82
|
+
mem_db.execute("CREATE TABLE kv (k TEXT PRIMARY KEY, v TEXT);")
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## 🛡️ Architecture & Safety
|
|
88
|
+
- **Single-File `.tapir` Container**: Unified relational SQL, document JSON, HNSW vectors, and openCypher knowledge graphs in one file.
|
|
89
|
+
- **Hardware AES-256 GCM**: ChaCha20-Poly1305 AEAD with PBKDF2/SHA-256 salt verification.
|
|
90
|
+
- **Zero Cloud Latency**: Runs entirely in-process (`0.55 µs` pointer dereference) without external server daemons.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
tapirus/__init__.py,sha256=3jhRhEF4MXGxJHq_hQdVbd_Bi3xj6wl51keQ039Bxv0,869
|
|
2
|
+
tapirus/connection.py,sha256=jXfQHUDsk84QsZCmc0Jrar-83a_xbSulIyiwoQnNk8Q,5027
|
|
3
|
+
tapirus/exceptions.py,sha256=UZI9_Q0Zw0--7ZNCp_70XRWbQCPn0mnB2yjbqs2llwk,602
|
|
4
|
+
tapirus/ffi.py,sha256=hmwfVdnpPYgz5wE25W1p-NWMEKGCjOBxrSZ5b42vUGA,3147
|
|
5
|
+
tapirus-1.0.0.dist-info/METADATA,sha256=4ymTf1uls4ZTQ-zDxM1nFQuTZSILoL1y2Pnt0ZQrZyo,2842
|
|
6
|
+
tapirus-1.0.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
7
|
+
tapirus-1.0.0.dist-info/top_level.txt,sha256=Sz1x1UwllzFT1g3oZTPN0FctxUZeZ4pIVo9Gg3wrN9c,8
|
|
8
|
+
tapirus-1.0.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
tapirus
|