surrealdb-orm 0.1.4__py3-none-any.whl → 0.5.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.
Files changed (50) hide show
  1. surreal_orm/__init__.py +72 -3
  2. surreal_orm/aggregations.py +164 -0
  3. surreal_orm/auth/__init__.py +15 -0
  4. surreal_orm/auth/access.py +167 -0
  5. surreal_orm/auth/mixins.py +302 -0
  6. surreal_orm/cli/__init__.py +15 -0
  7. surreal_orm/cli/commands.py +369 -0
  8. surreal_orm/connection_manager.py +58 -18
  9. surreal_orm/fields/__init__.py +36 -0
  10. surreal_orm/fields/encrypted.py +166 -0
  11. surreal_orm/fields/relation.py +465 -0
  12. surreal_orm/migrations/__init__.py +51 -0
  13. surreal_orm/migrations/executor.py +380 -0
  14. surreal_orm/migrations/generator.py +272 -0
  15. surreal_orm/migrations/introspector.py +305 -0
  16. surreal_orm/migrations/migration.py +188 -0
  17. surreal_orm/migrations/operations.py +531 -0
  18. surreal_orm/migrations/state.py +406 -0
  19. surreal_orm/model_base.py +530 -44
  20. surreal_orm/query_set.py +609 -33
  21. surreal_orm/relations.py +645 -0
  22. surreal_orm/surreal_function.py +95 -0
  23. surreal_orm/surreal_ql.py +113 -0
  24. surreal_orm/types.py +86 -0
  25. surreal_sdk/README.md +79 -0
  26. surreal_sdk/__init__.py +151 -0
  27. surreal_sdk/connection/__init__.py +17 -0
  28. surreal_sdk/connection/base.py +516 -0
  29. surreal_sdk/connection/http.py +421 -0
  30. surreal_sdk/connection/pool.py +244 -0
  31. surreal_sdk/connection/websocket.py +519 -0
  32. surreal_sdk/exceptions.py +71 -0
  33. surreal_sdk/functions.py +607 -0
  34. surreal_sdk/protocol/__init__.py +13 -0
  35. surreal_sdk/protocol/rpc.py +218 -0
  36. surreal_sdk/py.typed +0 -0
  37. surreal_sdk/pyproject.toml +49 -0
  38. surreal_sdk/streaming/__init__.py +31 -0
  39. surreal_sdk/streaming/change_feed.py +278 -0
  40. surreal_sdk/streaming/live_query.py +265 -0
  41. surreal_sdk/streaming/live_select.py +369 -0
  42. surreal_sdk/transaction.py +386 -0
  43. surreal_sdk/types.py +346 -0
  44. surrealdb_orm-0.5.0.dist-info/METADATA +465 -0
  45. surrealdb_orm-0.5.0.dist-info/RECORD +52 -0
  46. {surrealdb_orm-0.1.4.dist-info → surrealdb_orm-0.5.0.dist-info}/WHEEL +1 -1
  47. surrealdb_orm-0.5.0.dist-info/entry_points.txt +2 -0
  48. {surrealdb_orm-0.1.4.dist-info → surrealdb_orm-0.5.0.dist-info}/licenses/LICENSE +1 -1
  49. surrealdb_orm-0.1.4.dist-info/METADATA +0 -184
  50. surrealdb_orm-0.1.4.dist-info/RECORD +0 -12
@@ -0,0 +1,95 @@
1
+ from enum import StrEnum
2
+
3
+
4
+ class SurealFunction(StrEnum): ...
5
+
6
+
7
+ # XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
8
+ class SurrealArrayFunction(SurealFunction):
9
+ APPEND = "array::append"
10
+
11
+
12
+ # https://surrealdb.com/docs/surrealql/functions/database/time
13
+ class SurrealTimeFunction(SurealFunction):
14
+ CEIL = "time::ceil"
15
+ DAY = "time::day"
16
+ FLOOR = "time::floor"
17
+ FORMAT = "time::format"
18
+ GROUP = "time::group"
19
+ HOUR = "time::hour"
20
+ MAX = "time::max"
21
+ MICROS = "time::micros"
22
+ MILLIS = "time::millis"
23
+ MIN = "time::min"
24
+ MINUTE = "time::minute"
25
+ MONTH = "time::month"
26
+ NANOS = "time::nanos"
27
+ NOW = "time::now"
28
+ ROUND = "time::round"
29
+ SECOND = "time::second"
30
+ TIMEZONE = "time::timezone"
31
+ UNIX = "time::unix"
32
+ WEEK = "time::week"
33
+ YDAY = "time::yday"
34
+ YEAR = "time::year"
35
+ IS_LEAP_YEAR = "time::is::leap_year"
36
+ FROM_MICROS = "time::from::micros"
37
+ FROM_MILLIS = "time::from::millis"
38
+ FROM_NANOS = "time::from::nanos"
39
+ FROM_SECONDS = "time::from::seconds"
40
+ FROM_UNIX = "time::from::unix"
41
+ FROM_ULID = "time::from::ulid"
42
+ FROM_UUID = "time::from::uuid"
43
+
44
+
45
+ # https://surrealdb.com/docs/surrealql/functions/database/math
46
+ class SurrealMathFunction(SurealFunction):
47
+ ABS = "math::abs"
48
+ ACOS = "math::acos"
49
+ ACOT = "math::acot"
50
+ ASIN = "math::asin"
51
+ ATAN = "math::atan"
52
+ BOTTOM = "math::bottom"
53
+ CEIL = "math::ceil"
54
+ CLAAMP = "math::clamp"
55
+ COS = "math::cos"
56
+ COT = "math::cot"
57
+ COUNT = "count" # https://surrealdb.com/docs/surrealql/functions/database/count
58
+ DEG2RAD = "math::deg2rad"
59
+ E = "math::e"
60
+ FIXED = "math::fixed"
61
+ FLOOR = "math::floor"
62
+ FRAC_1_PI = "math::frac_1_pi"
63
+ FRAC_1_SQRT_2 = "math::frac_1_sqrt_2"
64
+ FRAC_2_PI = "math::frac_2_pi"
65
+ FRAC_2_SQRT_PI = "math::frac_2_sqrt_pi"
66
+ FRAC_PI_2 = "math::frac_pi_2"
67
+ FRAC_PI_3 = "math::frac_pi_3"
68
+ FRAC_PI_4 = "math::frac_pi_4"
69
+ FRAC_PI_6 = "math::frac_pi_6"
70
+ FRAC_PI_8 = "math::frac_pi_8"
71
+ INF = "math::inf"
72
+ INTERQUARTILE = "math::interquartile"
73
+ LERP = "math::lerp"
74
+ LERPANGLE = "math::lerpangle"
75
+ LN = "math::ln"
76
+ LN_10 = "math::ln_10"
77
+ LN_2 = "math::ln_2"
78
+ LOG = "math::log"
79
+ LOG10 = "math::log10"
80
+ LOG10_2 = "math::log10_2"
81
+ LOG10_E = "math::log10_e"
82
+ LOG2 = "math::log2"
83
+ LOG2_10 = "math::log2_20"
84
+ LOG2_E = "math::log2_e"
85
+ ###
86
+ PI = "math::pi"
87
+ POW = "math::pow"
88
+ ROUND = "math::round"
89
+ SIGN = "math::sign"
90
+ SIN = "math::sin"
91
+ SINH = "math::sinh"
92
+ SQRT = "math::sqrt"
93
+ TAN = "math::tan"
94
+ TANH = "math::tanh"
95
+ TRUNC = "math::trunc"
@@ -0,0 +1,113 @@
1
+ from typing import Any, Self
2
+ from .connection_manager import SurrealDBConnectionManager
3
+ from .utils import remove_quotes_for_variables
4
+ from surreal_sdk import HTTPConnection
5
+
6
+ import logging
7
+
8
+ logger = logging.getLogger(__name__)
9
+
10
+
11
+ class SurrealQL:
12
+ def __init__(self) -> None:
13
+ self._variables: dict[str, Any] = {}
14
+ self.related_models: list[Any] = []
15
+ self._query: str = ""
16
+
17
+ """
18
+ Class for interacting with SurrealDB using SQL-like commands.
19
+ """
20
+
21
+ def select(self, fields: str | list[str] | None) -> Self:
22
+ if fields is None:
23
+ self._query += "SELECT "
24
+ elif isinstance(fields, list):
25
+ fields = ", ".join(fields)
26
+ self._query += f"SELECT {fields}"
27
+ else:
28
+ self._query += f"SELECT {fields}"
29
+
30
+ return self
31
+
32
+ def related(self, record_table: str) -> Self:
33
+ """
34
+ Placeholder method for defining related models.
35
+ """
36
+ self._query += f"RELATE {record_table}"
37
+
38
+ return self
39
+
40
+ def to_related(self, table_or_record_table: str) -> Self:
41
+ """
42
+ Placeholder method for defining the target table for a query.
43
+ """
44
+ self._query += f"->{table_or_record_table}"
45
+
46
+ return self
47
+
48
+ def from_related(self, table_or_record_table: str) -> Self:
49
+ """
50
+ Placeholder method for defining the source table for a query.
51
+ """
52
+ self._query += f"<-{table_or_record_table}"
53
+
54
+ return self
55
+
56
+ def from_tables(self, tables: str | list[str]) -> Self:
57
+ """
58
+ Placeholder method for defining the source table for a query.
59
+ """
60
+ if isinstance(tables, list):
61
+ tables = ", ".join(tables)
62
+ self._query += f"FROM {tables}"
63
+
64
+ return self
65
+
66
+ async def _execute_query(self, query: str) -> list[Any]:
67
+ """
68
+ Execute the given SQL query using the SurrealDB client.
69
+
70
+ This internal method handles the execution of the compiled SQL query and returns the raw results
71
+ from the database.
72
+
73
+ Args:
74
+ query (str): The SQL query string to execute.
75
+
76
+ Returns:
77
+ list[Any]: A list of query result objects.
78
+
79
+ Raises:
80
+ SurrealDbError: If there is an issue executing the query.
81
+
82
+ Example:
83
+ ```python
84
+ results = await self._execute_query("SELECT * FROM users;")
85
+ ```
86
+ """
87
+ client = await SurrealDBConnectionManager.get_client()
88
+ return await self._run_query_on_client(client, query)
89
+
90
+ async def _run_query_on_client(self, client: HTTPConnection, query: str) -> list[Any]:
91
+ """
92
+ Run the SQL query on the provided SurrealDB client.
93
+
94
+ This internal method sends the query to the SurrealDB client along with any predefined variables
95
+ and returns the raw query responses.
96
+
97
+ Args:
98
+ client (HTTPConnection): The active SurrealDB client instance.
99
+ query (str): The SQL query string to execute.
100
+
101
+ Returns:
102
+ list[Any]: A list of query result objects.
103
+
104
+ Raises:
105
+ SurrealDbError: If there is an issue executing the query.
106
+
107
+ Example:
108
+ ```python
109
+ results = await self._run_query_on_client(client, "SELECT * FROM users;")
110
+ ```
111
+ """
112
+ result = await client.query(remove_quotes_for_variables(query), self._variables)
113
+ return result.all_records
surreal_orm/types.py ADDED
@@ -0,0 +1,86 @@
1
+ """
2
+ Type definitions for SurrealDB ORM.
3
+
4
+ This module contains enums and type definitions used throughout the ORM
5
+ for table types, schema modes, and field types.
6
+ """
7
+
8
+ from enum import StrEnum
9
+
10
+
11
+ class TableType(StrEnum):
12
+ """
13
+ Table type classification for migration behavior and connection preferences.
14
+
15
+ Each table type has specific characteristics:
16
+ - NORMAL: Standard table with default behavior
17
+ - USER: Authentication table with enforced SCHEMAFULL, required password field
18
+ - STREAM: Real-time optimized table with CHANGEFEED enabled, WebSocket preferred
19
+ - HASH: Lookup/cache table, SCHEMALESS by default
20
+ """
21
+
22
+ NORMAL = "normal"
23
+ USER = "user"
24
+ STREAM = "stream"
25
+ HASH = "hash"
26
+
27
+
28
+ class SchemaMode(StrEnum):
29
+ """
30
+ Schema enforcement mode for SurrealDB tables.
31
+
32
+ - SCHEMAFULL: Strict schema enforcement, only defined fields allowed
33
+ - SCHEMALESS: Flexible schema, any fields accepted
34
+ """
35
+
36
+ SCHEMAFULL = "SCHEMAFULL"
37
+ SCHEMALESS = "SCHEMALESS"
38
+
39
+
40
+ class FieldType(StrEnum):
41
+ """
42
+ SurrealDB field types for schema definitions.
43
+
44
+ Maps to SurrealDB's native type system.
45
+ """
46
+
47
+ STRING = "string"
48
+ INT = "int"
49
+ FLOAT = "float"
50
+ BOOL = "bool"
51
+ DATETIME = "datetime"
52
+ DURATION = "duration"
53
+ DECIMAL = "decimal"
54
+ ARRAY = "array"
55
+ OBJECT = "object"
56
+ RECORD = "record"
57
+ GEOMETRY = "geometry"
58
+ ANY = "any"
59
+ OPTION = "option"
60
+ BYTES = "bytes"
61
+ UUID = "uuid"
62
+
63
+
64
+ class EncryptionAlgorithm(StrEnum):
65
+ """
66
+ Supported encryption algorithms for password hashing.
67
+
68
+ All algorithms use SurrealDB's built-in crypto functions.
69
+ """
70
+
71
+ ARGON2 = "argon2"
72
+ BCRYPT = "bcrypt"
73
+ PBKDF2 = "pbkdf2"
74
+ SCRYPT = "scrypt"
75
+
76
+
77
+ # Type mapping from Python types to SurrealDB types
78
+ PYTHON_TO_SURREAL_TYPE: dict[type, FieldType] = {
79
+ str: FieldType.STRING,
80
+ int: FieldType.INT,
81
+ float: FieldType.FLOAT,
82
+ bool: FieldType.BOOL,
83
+ list: FieldType.ARRAY,
84
+ dict: FieldType.OBJECT,
85
+ bytes: FieldType.BYTES,
86
+ }
surreal_sdk/README.md ADDED
@@ -0,0 +1,79 @@
1
+ # Surreal SDK
2
+
3
+ A custom Python SDK for SurrealDB with HTTP and WebSocket support.
4
+
5
+ **No dependency on the official `surrealdb` package** - this is a standalone implementation.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ pip install surreal-sdk
11
+ ```
12
+
13
+ ## Quick Start
14
+
15
+ ### HTTP Connection (Stateless)
16
+
17
+ ```python
18
+ from surreal_sdk import SurrealDB
19
+
20
+ async with SurrealDB.http("http://localhost:8000", "namespace", "database") as db:
21
+ await db.signin("root", "root")
22
+
23
+ # Query
24
+ result = await db.query("SELECT * FROM users WHERE age > $min_age", {"min_age": 18})
25
+ print(result.all_records)
26
+
27
+ # CRUD
28
+ user = await db.create("users", {"name": "Alice", "age": 30})
29
+ await db.update("users:alice", {"age": 31})
30
+ await db.delete("users:alice")
31
+ ```
32
+
33
+ ### WebSocket Connection (Stateful, Real-time)
34
+
35
+ ```python
36
+ from surreal_sdk import SurrealDB
37
+
38
+ async with SurrealDB.ws("ws://localhost:8000", "namespace", "database") as db:
39
+ await db.signin("root", "root")
40
+
41
+ # Live Query
42
+ async def on_change(data):
43
+ print(f"Change detected: {data}")
44
+
45
+ live_id = await db.live("users", on_change)
46
+ # ... do work ...
47
+ await db.kill(live_id)
48
+ ```
49
+
50
+ ### Connection Pool
51
+
52
+ ```python
53
+ from surreal_sdk import SurrealDB
54
+
55
+ async with SurrealDB.pool("http://localhost:8000", "ns", "db", size=10) as pool:
56
+ await pool.set_credentials("root", "root")
57
+
58
+ async with pool.acquire() as conn:
59
+ result = await conn.query("SELECT * FROM users")
60
+ ```
61
+
62
+ ## Features
63
+
64
+ - **HTTPConnection** - Stateless, ideal for microservices and serverless
65
+ - **WebSocketConnection** - Stateful, for real-time features and Live Queries
66
+ - **ConnectionPool** - Connection pooling for high-throughput scenarios
67
+ - **Typed Responses** - `QueryResponse`, `RecordResponse`, `RecordsResponse`, etc.
68
+ - **Live Queries** - Real-time subscriptions (WebSocket only)
69
+ - **Change Feeds** - CDC pattern for stateless architectures (HTTP)
70
+
71
+ ## Requirements
72
+
73
+ - Python 3.12+
74
+ - httpx
75
+ - aiohttp
76
+
77
+ ## License
78
+
79
+ MIT
@@ -0,0 +1,151 @@
1
+ """
2
+ SurrealDB SDK - A custom Python SDK for SurrealDB.
3
+
4
+ This SDK provides direct connection to SurrealDB via HTTP and WebSocket
5
+ without depending on the official surrealdb package.
6
+
7
+ Supports:
8
+ - HTTP connections (stateless, ideal for microservices)
9
+ - WebSocket connections (stateful, for real-time features)
10
+ - Live Queries (WebSocket only)
11
+ - Live Select Streams (async iterator pattern)
12
+ - Change Feeds streaming (HTTP, stateless)
13
+ - Typed function calls
14
+ """
15
+
16
+ from typing import Any
17
+
18
+ from .connection.base import BaseSurrealConnection
19
+ from .connection.http import HTTPConnection
20
+ from .connection.websocket import WebSocketConnection
21
+ from .connection.pool import ConnectionPool
22
+ from .streaming.change_feed import ChangeFeedStream
23
+ from .streaming.live_query import LiveQuery, LiveQueryManager, LiveNotification
24
+ from .streaming.live_select import (
25
+ LiveSelectStream,
26
+ LiveSelectManager,
27
+ LiveChange,
28
+ LiveAction,
29
+ LiveSubscriptionParams,
30
+ )
31
+ from .protocol.rpc import RPCRequest, RPCResponse, RPCError
32
+ from .types import (
33
+ ResponseStatus,
34
+ QueryResult,
35
+ QueryResponse,
36
+ RecordResponse,
37
+ RecordsResponse,
38
+ AuthResponse,
39
+ InfoResponse,
40
+ LiveQueryId,
41
+ DeleteResponse,
42
+ )
43
+ from .exceptions import (
44
+ SurrealDBError,
45
+ ConnectionError,
46
+ AuthenticationError,
47
+ QueryError,
48
+ TimeoutError,
49
+ TransactionError,
50
+ )
51
+ from .transaction import (
52
+ BaseTransaction,
53
+ HTTPTransaction,
54
+ WebSocketTransaction,
55
+ TransactionStatement,
56
+ )
57
+ from .functions import (
58
+ FunctionCall,
59
+ FunctionNamespace,
60
+ MathFunctions,
61
+ TimeFunctions,
62
+ ArrayFunctions,
63
+ StringFunctions,
64
+ CryptoFunctions,
65
+ )
66
+
67
+ __version__ = "0.5.0"
68
+ __all__ = [
69
+ # Connections
70
+ "BaseSurrealConnection",
71
+ "HTTPConnection",
72
+ "WebSocketConnection",
73
+ "ConnectionPool",
74
+ # Streaming - Live Query (callback-based)
75
+ "ChangeFeedStream",
76
+ "LiveQuery",
77
+ "LiveQueryManager",
78
+ "LiveNotification",
79
+ # Streaming - Live Select (async iterator)
80
+ "LiveSelectStream",
81
+ "LiveSelectManager",
82
+ "LiveChange",
83
+ "LiveAction",
84
+ "LiveSubscriptionParams",
85
+ # Protocol
86
+ "RPCRequest",
87
+ "RPCResponse",
88
+ "RPCError",
89
+ # Response Types
90
+ "ResponseStatus",
91
+ "QueryResult",
92
+ "QueryResponse",
93
+ "RecordResponse",
94
+ "RecordsResponse",
95
+ "AuthResponse",
96
+ "InfoResponse",
97
+ "LiveQueryId",
98
+ "DeleteResponse",
99
+ # Transactions
100
+ "BaseTransaction",
101
+ "HTTPTransaction",
102
+ "WebSocketTransaction",
103
+ "TransactionStatement",
104
+ # Functions
105
+ "FunctionCall",
106
+ "FunctionNamespace",
107
+ "MathFunctions",
108
+ "TimeFunctions",
109
+ "ArrayFunctions",
110
+ "StringFunctions",
111
+ "CryptoFunctions",
112
+ # Exceptions
113
+ "SurrealDBError",
114
+ "ConnectionError",
115
+ "AuthenticationError",
116
+ "QueryError",
117
+ "TimeoutError",
118
+ "TransactionError",
119
+ ]
120
+
121
+
122
+ class SurrealDB:
123
+ """
124
+ Factory class for creating SurrealDB connections.
125
+
126
+ Usage:
127
+ # HTTP connection (stateless)
128
+ async with SurrealDB.http("http://localhost:8000", "ns", "db") as db:
129
+ await db.signin("root", "root")
130
+ result = await db.query("SELECT * FROM users")
131
+
132
+ # WebSocket connection (stateful)
133
+ async with SurrealDB.ws("ws://localhost:8000", "ns", "db") as db:
134
+ await db.signin("root", "root")
135
+ await db.live("users", callback=on_change)
136
+ """
137
+
138
+ @staticmethod
139
+ def http(url: str, namespace: str, database: str, **kwargs: Any) -> HTTPConnection:
140
+ """Create an HTTP connection (stateless)."""
141
+ return HTTPConnection(url, namespace, database, **kwargs)
142
+
143
+ @staticmethod
144
+ def ws(url: str, namespace: str, database: str, **kwargs: Any) -> WebSocketConnection:
145
+ """Create a WebSocket connection (stateful)."""
146
+ return WebSocketConnection(url, namespace, database, **kwargs)
147
+
148
+ @staticmethod
149
+ def pool(url: str, namespace: str, database: str, size: int = 10, **kwargs: Any) -> ConnectionPool:
150
+ """Create a connection pool."""
151
+ return ConnectionPool(url, namespace, database, size=size, **kwargs)
@@ -0,0 +1,17 @@
1
+ """
2
+ SurrealDB SDK Connection Module.
3
+
4
+ Provides HTTP and WebSocket connection implementations.
5
+ """
6
+
7
+ from .base import BaseSurrealConnection
8
+ from .http import HTTPConnection
9
+ from .websocket import WebSocketConnection
10
+ from .pool import ConnectionPool
11
+
12
+ __all__ = [
13
+ "BaseSurrealConnection",
14
+ "HTTPConnection",
15
+ "WebSocketConnection",
16
+ "ConnectionPool",
17
+ ]