surreal-orm-lite 0.19.0__tar.gz → 0.19.2__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.
Files changed (26) hide show
  1. {surreal_orm_lite-0.19.0 → surreal_orm_lite-0.19.2}/CHANGELOG.md +57 -0
  2. {surreal_orm_lite-0.19.0 → surreal_orm_lite-0.19.2}/PKG-INFO +80 -75
  3. {surreal_orm_lite-0.19.0 → surreal_orm_lite-0.19.2}/README.md +79 -74
  4. {surreal_orm_lite-0.19.0 → surreal_orm_lite-0.19.2}/pyproject.toml +1 -1
  5. {surreal_orm_lite-0.19.0 → surreal_orm_lite-0.19.2}/src/surreal_orm_lite/__init__.py +1 -1
  6. {surreal_orm_lite-0.19.0 → surreal_orm_lite-0.19.2}/src/surreal_orm_lite/connection_manager.py +9 -6
  7. {surreal_orm_lite-0.19.0 → surreal_orm_lite-0.19.2}/.gitignore +0 -0
  8. {surreal_orm_lite-0.19.0 → surreal_orm_lite-0.19.2}/LICENSE +0 -0
  9. {surreal_orm_lite-0.19.0 → surreal_orm_lite-0.19.2}/Makefile +0 -0
  10. {surreal_orm_lite-0.19.0 → surreal_orm_lite-0.19.2}/src/surreal_orm_lite/_sdk.py +0 -0
  11. {surreal_orm_lite-0.19.0 → surreal_orm_lite-0.19.2}/src/surreal_orm_lite/aggregations.py +0 -0
  12. {surreal_orm_lite-0.19.0 → surreal_orm_lite-0.19.2}/src/surreal_orm_lite/auth.py +0 -0
  13. {surreal_orm_lite-0.19.0 → surreal_orm_lite-0.19.2}/src/surreal_orm_lite/concurrency.py +0 -0
  14. {surreal_orm_lite-0.19.0 → surreal_orm_lite-0.19.2}/src/surreal_orm_lite/constants.py +0 -0
  15. {surreal_orm_lite-0.19.0 → surreal_orm_lite-0.19.2}/src/surreal_orm_lite/enum.py +0 -0
  16. {surreal_orm_lite-0.19.0 → surreal_orm_lite-0.19.2}/src/surreal_orm_lite/exceptions.py +0 -0
  17. {surreal_orm_lite-0.19.0 → surreal_orm_lite-0.19.2}/src/surreal_orm_lite/functions.py +0 -0
  18. {surreal_orm_lite-0.19.0 → surreal_orm_lite-0.19.2}/src/surreal_orm_lite/live.py +0 -0
  19. {surreal_orm_lite-0.19.0 → surreal_orm_lite-0.19.2}/src/surreal_orm_lite/model_auth.py +0 -0
  20. {surreal_orm_lite-0.19.0 → surreal_orm_lite-0.19.2}/src/surreal_orm_lite/model_base.py +0 -0
  21. {surreal_orm_lite-0.19.0 → surreal_orm_lite-0.19.2}/src/surreal_orm_lite/py.typed +0 -0
  22. {surreal_orm_lite-0.19.0 → surreal_orm_lite-0.19.2}/src/surreal_orm_lite/q.py +0 -0
  23. {surreal_orm_lite-0.19.0 → surreal_orm_lite-0.19.2}/src/surreal_orm_lite/query_set.py +0 -0
  24. {surreal_orm_lite-0.19.0 → surreal_orm_lite-0.19.2}/src/surreal_orm_lite/signals.py +0 -0
  25. {surreal_orm_lite-0.19.0 → surreal_orm_lite-0.19.2}/src/surreal_orm_lite/transaction.py +0 -0
  26. {surreal_orm_lite-0.19.0 → surreal_orm_lite-0.19.2}/src/surreal_orm_lite/utils.py +0 -0
@@ -5,6 +5,63 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.19.2] - 2026-10-01
9
+
10
+ Security release for the locked dependencies. No library code changes; the published package
11
+ behaves exactly as 0.19.1.
12
+
13
+ ### Security
14
+
15
+ - **urllib3 2.7.0 → 2.8.0** (GHSA-8988-9cw3-xx77, GHSA-vxq7-64xx-v4gw, GHSA-gh4c-6fx4-qh6g).
16
+ urllib3 is reached at runtime through the SDK (`surrealdb` → `requests` → `urllib3`) and by
17
+ the dev-only `docker` package.
18
+ - **tornado 6.5.8 → 6.5.9** (GHSA-c2m8-h5v5-343r, GHSA-chx6-46f5-w4vp, GHSA-3hv7-mjh2-fv65).
19
+ Dev only: pulled in by `ipykernel` and `jupyter-client` for the example notebooks.
20
+
21
+ ### Notes
22
+
23
+ - **This updates `uv.lock`, which protects CI and contributors, not your installation.** The ORM
24
+ declares no bound on urllib3 (it never imports it; the SDK's `requests` does), so your
25
+ environment keeps whatever urllib3 it resolved. If it is older than 2.8.0, upgrade it
26
+ (`pip install -U "urllib3>=2.8.0"` or `uv lock --upgrade-package urllib3`).
27
+
28
+ ## [0.19.1] - 2026-10-01
29
+
30
+ Tested against the new SurrealDB releases on both supported lines: **2.7.0** and **3.3.0**. No
31
+ library code changes; the published package behaves exactly as 0.19.0.
32
+
33
+ ### Changed
34
+
35
+ - **SurrealDB test pins moved**: the 2.x line from 2.6.5 to **2.7.0**, the 3.x line from 3.2.4
36
+ to **3.3.0**. The CI matrix, the README badge, compatibility table and behaviour table, and the
37
+ ROADMAP follow the pins through `scripts/sync_surrealdb_pins.py`. 2.6.5 and 3.2.4 stay listed
38
+ as compatible, tested up to v0.19.0.
39
+
40
+ ### Notes
41
+
42
+ - **2.7.0 is indistinguishable from 2.6.5 for the ORM.** The full suite passes with the same
43
+ 3.x-only skips, and every divergence in the behaviour table was re-measured on it: no native
44
+ transaction RPC, `WITH REFRESH` rejected, a live query on a missing table accepted, nothing
45
+ sent after `kill()`, no `session` key in a live notification.
46
+ - **3.3.0 changes one server behaviour.** Signing in as a system user while a record session is
47
+ open used to swap only the permissions, leaving `$auth`, `$access` and `$session.rd` on the
48
+ record so `info()` kept reporting it. 3.3.0 clears all three and `info()` returns `None`.
49
+ Nothing in the ORM depends on the old behaviour, and `invalidate()` remains the portable way
50
+ to log a record user out. The test that pinned the old behaviour now pins each line's, keyed
51
+ on the server's reported version so a further change goes red; it was checked on 3.3.0,
52
+ 3.2.4, 3.1.5 and 2.7.0. Every other 3.x divergence holds unchanged on 3.3.0.
53
+
54
+ ### Fixed
55
+
56
+ - **The SurrealDB version monitor files its failure report again** (#188). `gh issue create`
57
+ refuses the whole call when one label does not exist, and `needs-investigation` never did, so
58
+ the 3.3.0 failure above was never reported. Labels are now created first, with an unlabelled
59
+ issue as fallback.
60
+
61
+ ### Dependencies
62
+
63
+ - ruff 0.16.7 → 0.16.9 (dev only, #190).
64
+
8
65
  ## [0.19.0] - 2026-09-30
9
66
 
10
67
  Real-time, phase one: subscribe to a table and receive a notification whenever a record is
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: surreal-orm-lite
3
- Version: 0.19.0
3
+ Version: 0.19.2
4
4
  Summary: Lightweight Django-style ORM for SurrealDB using the official Python SDK. Async support with Pydantic validation.
5
5
  Project-URL: Homepage, https://github.com/EulogySnowfall/SurrealDB-ORM-lite
6
6
  Project-URL: Documentation, https://github.com/EulogySnowfall/SurrealDB-ORM-lite
@@ -51,7 +51,7 @@ Description-Content-Type: text/markdown
51
51
  # Surreal ORM Lite
52
52
 
53
53
  ![Python](https://img.shields.io/badge/python-3.11%2B-blue)
54
- ![SurrealDB](https://img.shields.io/badge/SurrealDB-2.6.5%20%7C%203.2.4-purple)
54
+ ![SurrealDB](https://img.shields.io/badge/SurrealDB-2.7.0%20%7C%203.3.0-purple)
55
55
  ![SDK](https://img.shields.io/badge/SDK-Official%202.0-green)
56
56
  ![License](https://img.shields.io/badge/license-MIT-blue)
57
57
  [![codecov](https://codecov.io/gh/EulogySnowfall/SurrealDB-ORM-lite/graph/badge.svg)](https://codecov.io/gh/EulogySnowfall/SurrealDB-ORM-lite)
@@ -74,11 +74,11 @@ This ORM is designed to:
74
74
  | Dependency | Version |
75
75
  | ------------ | --------------------------------- |
76
76
  | Python | 3.11+ |
77
- | SurrealDB | 2.6.x or 3.2.x |
77
+ | SurrealDB | 2.6.x / 2.7.x or 3.2.x / 3.3.x |
78
78
  | Official SDK | surrealdb[pydantic]>=2.0.0,<3.0.0 |
79
79
  | Pydantic | >=2.13.4 |
80
80
 
81
- > **Note**: As of v0.7.0, Surreal ORM Lite targets the SurrealDB Python SDK 2.x (`surrealdb[pydantic]>=2.0.0,<3.0.0`), which supports the SurrealDB 3.x protocol. It is tested against SurrealDB **v2.6.5** and **v3.2.4**. SurrealDB 3.1.x is no longer a supported line as of v0.14.0 — the suite is still run against 3.1.5 as a backward-compatibility check, but a regression there does not block a release.
81
+ > **Note**: As of v0.7.0, Surreal ORM Lite targets the SurrealDB Python SDK 2.x (`surrealdb[pydantic]>=2.0.0,<3.0.0`), which supports the SurrealDB 3.x protocol. It is tested against SurrealDB **v2.7.0** and **v3.3.0**; up to v0.19.0 the pins were v2.6.5 and v3.2.4. The 2.x line behaves identically on 2.7.0, and 3.3.0 differs from 3.2.4 in one server behaviour, listed in the behaviour table. SurrealDB 3.1.x is no longer a supported line as of v0.14.0 — the suite is still run against 3.1.5 as a backward-compatibility check, but a regression there does not block a release.
82
82
 
83
83
  ---
84
84
 
@@ -633,7 +633,7 @@ await user.merge(plan="pro", server_values={"updated_at": SurrealFunc.call(Surre
633
633
 
634
634
  `SurrealFunc.call(fn, *args)` builds `fn(arg, …)` from a function name — a plain string or a
635
635
  member of the shipped enums, which give you autocompletion over a **curated catalog whose every
636
- member is tested against SurrealDB 2.6.5 and 3.2.4**:
636
+ member is tested against SurrealDB 2.7.0 and 3.3.0**:
637
637
 
638
638
  | Enum | Covers |
639
639
  | ----------------------- | ---------------------------------------------------------------- |
@@ -1143,7 +1143,7 @@ next `refresh()`. Keep the default whenever you need to know the write landed.
1143
1143
 
1144
1144
  A live query is a server-side subscription: SurrealDB pushes a notification every time a
1145
1145
  record in the watched table is created, updated or deleted. It needs a **WebSocket**
1146
- connection, and it works on both SurrealDB 2.6.x and 3.2.x.
1146
+ connection, and it works on both SurrealDB 2.x (2.6 / 2.7) and 3.x (3.2 / 3.3).
1147
1147
 
1148
1148
  The simplest form is `watch()`, an async context manager that kills the subscription when the
1149
1149
  block exits:
@@ -1329,77 +1329,82 @@ Long-lived single-loop applications are unaffected: one loop, one connection, as
1329
1329
 
1330
1330
  As of v0.7.0, Surreal ORM Lite uses `surrealdb[pydantic]>=2.0.0,<3.0.0` (SurrealDB 3.x protocol) and is tested against both major SurrealDB release lines.
1331
1331
 
1332
- **Compatibility advantage over the full ORM**: ORM-lite runs on **both** SurrealDB **2.6.x and 3.2.x**, while the full [SurrealDB-ORM](https://github.com/EulogySnowfall/SurrealDB-ORM/) (custom SDK) targets **3.x only**. Lite stays usable on existing 2.6.x deployments without forcing a server upgrade.
1332
+ **Compatibility advantage over the full ORM**: ORM-lite runs on **both** SurrealDB **2.x (2.6 / 2.7) and 3.x (3.2 / 3.3)**, while the full [SurrealDB-ORM](https://github.com/EulogySnowfall/SurrealDB-ORM/) (custom SDK) targets **3.x only**. Lite stays usable on existing 2.6.x deployments without forcing a server upgrade.
1333
1333
 
1334
- | SurrealDB Version | SDK Version | Status |
1335
- | ----------------- | ----------- | ----------------------- |
1336
- | 3.2.4 | 2.0 | ✅ Tested |
1337
- | 2.6.5 | 2.0 | ✅ Tested |
1338
- | 3.2.x / 2.6.x | 2.0 | ✅ Compatible |
1339
- | 3.1.5 | 2.0 | ⚠️ Backward-compat only |
1340
- | < 2.6 or > 3.2 | — | ⚠️ Not guaranteed |
1334
+ | SurrealDB Version | SDK Version | Status |
1335
+ | ----------------------------- | ----------- | ------------------------------------ |
1336
+ | 3.3.0 | 2.0 | ✅ Tested |
1337
+ | 2.7.0 | 2.0 | ✅ Tested |
1338
+ | 3.2.4 | 2.0 | ✅ Compatible (tested up to v0.19.0) |
1339
+ | 2.6.5 | 2.0 | ✅ Compatible (tested up to v0.19.0) |
1340
+ | 3.3.x / 3.2.x / 2.7.x / 2.6.x | 2.0 | ✅ Compatible |
1341
+ | 3.1.5 | 2.0 | ⚠️ Backward-compat only |
1342
+ | < 2.6 or > 3.3 | — | ⚠️ Not guaranteed |
1341
1343
 
1342
- ### ORM behaviour: SurrealDB 2.6.x vs 3.x
1344
+ ### ORM behaviour: SurrealDB 2.x vs 3.x
1343
1345
 
1344
1346
  Surreal ORM Lite runs on both lines; some capabilities differ because they rely on server
1345
- features introduced in SurrealDB 3.x. On 2.6.x the ORM degrades gracefully. Capabilities not
1346
- listed behave the same on both lines.
1347
-
1348
- | ORM capability | SurrealDB 2.6.x | SurrealDB 3.2.x | Since |
1349
- | -------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ | ------- |
1350
- | Transaction strategy auto-selected by `transaction()` | buffered batch (`BEGIN…COMMIT`) | native interactive on WebSocket | v0.9.0 |
1351
- | Reads inside a transaction (`objects(tx=)`) | raise (buffered cannot read) | see uncommitted writes | v0.9.0 |
1352
- | `save(tx=)` with an auto-generated id | raises — explicit id required | supported | v0.9.0 |
1353
- | `refresh(tx=)` inside a transaction | raises | works | v0.9.0 |
1354
- | `bulk_update` / `bulk_delete` / `QuerySet.patch` row count inside a tx | returns `0` (not knowable pre-commit) | real count | v0.9.0 |
1355
- | "Already exists" error on create | normalised to `SurrealDbError` | normalised to `SurrealDbError` | v0.7.0 |
1356
- | Cleanup on a missing target (`delete_table`, `remove_relation`) | native no-op | ORM makes it a silent no-op | v0.7.0 |
1357
- | Aggregation over an empty set (`NaN` / `±inf`) | returns `0.0` / `None` | ORM normalises to `0.0` / `None` | v0.7.0 |
1358
- | Namespace/db selection (`use()` ordering) | lenient (auto-creates) | strict — ORM signs in before `use()` | v0.7.0 |
1359
- | `upsert()` / `update_or_create()` / `get_or_create()` | same on both lines | same on both lines | v0.10.0 |
1360
- | `patch()` / `atomic_append` / `atomic_set_add` / `atomic_remove` / `atomic_increment` | same on both lines (portable `array::*` fns chosen over divergent `+=`/`-=`) | same on both lines | v0.11.0 |
1361
- | `retry_on_conflict` / `SurrealDbConflictError` (retryable conflict) | same type + decorator; conflicts rarer (engine serialises more) | same type + decorator; conflicts are the normal optimistic-MVCC failure | v0.12.0 |
1362
- | `SurrealFunc` / `server_values=` / `extra_vars=` on `save`/`merge` | same on both lines (compiled to portable `CREATE`/`UPDATE … SET`) | same on both lines | v0.13.0 |
1363
- | Shipped function-name enums (`SurrealTimeFunction`, `SurrealCryptoFunction`, …) | every catalogued member verified on 2.6.5 | every catalogued member verified on 3.2.4 | v0.13.0 |
1364
- | `server_values` inside a transaction — when the instance sees the computed value | only after commit (buffered; `refresh()` to read it) | immediately (interactive returns the row) | v0.13.0 |
1365
- | `merge(server_values=)` on a missing record / never-created table | server returns no rows → ORM raises `SurrealDbError` | server raises `NotFound` for a missing table → ORM raises the same error | v0.13.0 |
1366
- | Computed fields (`Computed[...]` → `DEFINE FIELD … VALUE`) | same on both lines (DDL, recompute triggers, precedence over client data) | same on both lines | v0.14.0 |
1367
- | DDL run inside a transaction, then rolled back | definition rolled back with the transaction | same on both lines | v0.14.0 |
1368
- | Invalid computed-field expression — raw SDK exception | `InternalError` | `ValidationError` | v0.14.0 |
1369
- | Invalid computed-field expression — through the ORM | `SurrealDbError` (normalised) | `SurrealDbError` (normalised) | v0.14.0 |
1370
- | Issue #156 correctness fixes (`Var`/`$$`, `first()`, `*_or_create` strictness, `created` on upsert, quoted ids, one-hop `get_related`) | same on both lines (each fix reproduced and verified on 2.6.5) | same on both lines (verified on 3.2.4) | v0.14.3 |
1371
- | Record-id lookups (`filter(id=…)`, `id__in`, `get(…)`, `*_or_create(id=…)`) coerced to `RecordID` | same on both lines (int/str typing verified on 2.6.5) | same on both lines (verified on 3.2.4) | v0.14.4 |
1372
- | Per-event-loop client cache (`get_client`, `close_connection`, `close_all_connections`) | same on both lines (loop binding is an asyncio/SDK property, not a server one) | same on both lines (verified on 3.2.4) | v0.14.5 |
1373
- | `call_function()` — the call itself (`args`, `params`, `return_type`, nested `fn::a::b`) | same on both lines (bare call form chosen so it is portable) | same on both lines (verified on 3.2.4) | v0.15.0 |
1374
- | `call_function(tx=)` — return value | `None` (buffered: queued until commit) | the function's value (interactive returns it immediately) | v0.15.0 |
1375
- | `call_function(tx=, return_type=)` | raises `ValueError` (no value to coerce yet) | coerces the returned value | v0.15.0 |
1376
- | Missing function called inside a transaction | surfaces at COMMIT as `SurrealDbError` (buffered: the call is only queued) | raises `SurrealDbNotFoundError` at call time | v0.15.0 |
1377
- | Declared parameter name that collides with a reserved word, as echoed by `INFO FOR DB` | quoted: `` $`by` `` — parser accepts it | bare: `$by` | v0.15.0 |
1378
- | Auth methods (`signin`, `signup`, `authenticate`, `invalidate`, `info`) | same on both lines (native SDK primitives, verified on 2.6.5) | same on both lines (verified on 3.2.4) | v0.16.0 |
1379
- | Auth failure — raw SDK exception for a wrong password | `InternalError` | `NotFoundError` | v0.16.0 |
1380
- | Auth failure — through the ORM | `SurrealDbAuthenticationError` (normalised) | `SurrealDbAuthenticationError` (normalised) | v0.16.0 |
1381
- | `DEFINE ACCESS … WITH REFRESH` and `AuthTokens.refresh` | not supported — the clause does not parse; `refresh` is always `None` | supported; `refresh` populated | v0.16.0 |
1382
- | `signin(access=…, refresh=…)` renewal | raises `SurrealDbAuthenticationError` (no such access method is definable) | returns a fresh, rotated token pair; the spent one is rejected | v0.16.0 |
1383
- | Session token replayed on reconnect (`get_client`, `reconnect`) | same on both lines | same on both lines | v0.16.0 |
1384
- | `info()` when the record's table denies it `select` on itself | returns `None` (no error) | returns `None` (no error) | v0.16.0 |
1385
- | Signing in as a system user while a record session is open | permissions change, `$auth` still points at the record — only `invalidate()` clears it | same on both lines | v0.16.0 |
1386
- | Duplicate signin identifier in the record table | signin fails (`No record was returned`) | signin succeeds, picking one record | v0.16.0 |
1387
- | Model auth (`signup`/`signin`/`authenticate`, `access_ddl`/`define_access`) | same on both lines (verified on 2.6.5) | same on both lines (verified on 3.2.4) | v0.17.0 |
1388
- | Model auth session isolation (each call on its own ephemeral connection) | same on both lines (an SDK/connection property, not a server one) | same on both lines (verified on 3.2.4) | v0.17.0 |
1389
- | Hydrated instance's password field after `signup`/`signin` | holds the stored **hash**, never the plaintext | same on both lines | v0.17.0 |
1390
- | Model config `with_refresh=True` → `define_access()` | raises `SurrealDbError` (clause does not parse; message names the 3.x requirement) | applies, and `AuthResult.tokens.refresh` is populated | v0.17.0 |
1391
- | `Model.refresh(token)` renewal | unavailable — no refresh token can exist | returns a fresh, rotated pair; the spent token is rejected | v0.17.0 |
1392
- | Field aliases (`Field(alias=…)`) across writes, hydration and every QuerySet clause | same on both lines (pure Pydantic + client-side name translation) | same on both lines (verified on 3.2.4) | v0.18.0 |
1393
- | `server_fields` — excluded from creates, kept on replaces, hydrated back | same on both lines; a `DEFAULT` is a create-time default on 2.6.5 | same on both lines; a `DEFAULT` is a create-time default on 3.2.4 | v0.18.0 |
1394
- | A server column omitted from a REPLACE (`UPDATE`/`UPSERT … CONTENT`) | an optional column is deleted; a required one raises `Found NONE for field …` | same on both lines — which is why the ORM keeps it in replace payloads | v0.18.0 |
1395
- | `merge(refresh=False)` — skipped resync, `RETURN NONE`, forfeited missing-record check | same on both lines | same on both lines (verified on 3.2.4) | v0.18.0 |
1396
- | `merge()` on a table that was never created | server returns no rows → ORM raises `SurrealDbError` ("no record found") | server raises `NotFoundError` → normalised to the same `SurrealDbError` | v0.18.0 |
1397
- | Live queries (`live()`, `subscribe_live()`, `kill()`) — the subscription itself | supported over WebSocket (verified on 2.6.5) | supported over WebSocket (verified on 3.2.4) | v0.19.0 |
1398
- | `live()` on a table that does not exist | succeeds; the stream simply never fires | server raises `NotFound` → ORM raises `SurrealDbNotFoundError` | v0.19.0 |
1399
- | Server notification after `kill()` | none is sent | a final envelope whose action is `KILLED` | v0.19.0 |
1400
- | End of a `subscribe_live()` stream after `kill()` | ORM pushes its own sentinel so the `async for` ends | ends on the sentinel or the `KILLED` envelope, whichever lands first | v0.19.0 |
1401
- | `session` key in a live notification envelope | absent | present (the SDK session uuid) | v0.19.0 |
1402
- | Live queries over HTTP | refused by the ORM with a message naming the WebSocket requirement | same on both lines | v0.19.0 |
1347
+ features introduced in SurrealDB 3.x. On 2.x the ORM degrades gracefully. Capabilities not
1348
+ listed behave the same on both lines. The 2.x column was measured on 2.6.5 and holds unchanged
1349
+ on 2.7.0. The 3.x column was measured on 3.2.4 and holds on 3.3.0, except where a row says
1350
+ otherwise. The full suite and every divergence below were re-verified on both new versions.
1351
+
1352
+ | ORM capability | SurrealDB 2.6.x / 2.7.x | SurrealDB 3.2.x / 3.3.x | Since |
1353
+ | -------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | ------- |
1354
+ | Transaction strategy auto-selected by `transaction()` | buffered batch (`BEGIN…COMMIT`) | native interactive on WebSocket | v0.9.0 |
1355
+ | Reads inside a transaction (`objects(tx=)`) | raise (buffered cannot read) | see uncommitted writes | v0.9.0 |
1356
+ | `save(tx=)` with an auto-generated id | raises — explicit id required | supported | v0.9.0 |
1357
+ | `refresh(tx=)` inside a transaction | raises | works | v0.9.0 |
1358
+ | `bulk_update` / `bulk_delete` / `QuerySet.patch` row count inside a tx | returns `0` (not knowable pre-commit) | real count | v0.9.0 |
1359
+ | "Already exists" error on create | normalised to `SurrealDbError` | normalised to `SurrealDbError` | v0.7.0 |
1360
+ | Cleanup on a missing target (`delete_table`, `remove_relation`) | native no-op | ORM makes it a silent no-op | v0.7.0 |
1361
+ | Aggregation over an empty set (`NaN` / `±inf`) | returns `0.0` / `None` | ORM normalises to `0.0` / `None` | v0.7.0 |
1362
+ | Namespace/db selection (`use()` ordering) | lenient (auto-creates) | strict — ORM signs in before `use()` | v0.7.0 |
1363
+ | `upsert()` / `update_or_create()` / `get_or_create()` | same on both lines | same on both lines | v0.10.0 |
1364
+ | `patch()` / `atomic_append` / `atomic_set_add` / `atomic_remove` / `atomic_increment` | same on both lines (portable `array::*` fns chosen over divergent `+=`/`-=`) | same on both lines | v0.11.0 |
1365
+ | `retry_on_conflict` / `SurrealDbConflictError` (retryable conflict) | same type + decorator; conflicts rarer (engine serialises more) | same type + decorator; conflicts are the normal optimistic-MVCC failure | v0.12.0 |
1366
+ | `SurrealFunc` / `server_values=` / `extra_vars=` on `save`/`merge` | same on both lines (compiled to portable `CREATE`/`UPDATE … SET`) | same on both lines | v0.13.0 |
1367
+ | Shipped function-name enums (`SurrealTimeFunction`, `SurrealCryptoFunction`, …) | every catalogued member verified on 2.7.0 | every catalogued member verified on 3.3.0 | v0.13.0 |
1368
+ | `server_values` inside a transaction — when the instance sees the computed value | only after commit (buffered; `refresh()` to read it) | immediately (interactive returns the row) | v0.13.0 |
1369
+ | `merge(server_values=)` on a missing record / never-created table | server returns no rows → ORM raises `SurrealDbError` | server raises `NotFound` for a missing table → ORM raises the same error | v0.13.0 |
1370
+ | Computed fields (`Computed[...]` → `DEFINE FIELD … VALUE`) | same on both lines (DDL, recompute triggers, precedence over client data) | same on both lines | v0.14.0 |
1371
+ | DDL run inside a transaction, then rolled back | definition rolled back with the transaction | same on both lines | v0.14.0 |
1372
+ | Invalid computed-field expression — raw SDK exception | `InternalError` | `ValidationError` | v0.14.0 |
1373
+ | Invalid computed-field expression — through the ORM | `SurrealDbError` (normalised) | `SurrealDbError` (normalised) | v0.14.0 |
1374
+ | Issue #156 correctness fixes (`Var`/`$$`, `first()`, `*_or_create` strictness, `created` on upsert, quoted ids, one-hop `get_related`) | same on both lines (each fix reproduced and verified on 2.6.5) | same on both lines (verified on 3.2.4) | v0.14.3 |
1375
+ | Record-id lookups (`filter(id=…)`, `id__in`, `get(…)`, `*_or_create(id=…)`) coerced to `RecordID` | same on both lines (int/str typing verified on 2.6.5) | same on both lines (verified on 3.2.4) | v0.14.4 |
1376
+ | Per-event-loop client cache (`get_client`, `close_connection`, `close_all_connections`) | same on both lines (loop binding is an asyncio/SDK property, not a server one) | same on both lines (verified on 3.2.4) | v0.14.5 |
1377
+ | `call_function()` — the call itself (`args`, `params`, `return_type`, nested `fn::a::b`) | same on both lines (bare call form chosen so it is portable) | same on both lines (verified on 3.2.4) | v0.15.0 |
1378
+ | `call_function(tx=)` — return value | `None` (buffered: queued until commit) | the function's value (interactive returns it immediately) | v0.15.0 |
1379
+ | `call_function(tx=, return_type=)` | raises `ValueError` (no value to coerce yet) | coerces the returned value | v0.15.0 |
1380
+ | Missing function called inside a transaction | surfaces at COMMIT as `SurrealDbError` (buffered: the call is only queued) | raises `SurrealDbNotFoundError` at call time | v0.15.0 |
1381
+ | Declared parameter name that collides with a reserved word, as echoed by `INFO FOR DB` | quoted: `` $`by` `` — parser accepts it | bare: `$by` | v0.15.0 |
1382
+ | Auth methods (`signin`, `signup`, `authenticate`, `invalidate`, `info`) | same on both lines (native SDK primitives, verified on 2.6.5) | same on both lines (verified on 3.2.4) | v0.16.0 |
1383
+ | Auth failure — raw SDK exception for a wrong password | `InternalError` | `NotFoundError` | v0.16.0 |
1384
+ | Auth failure — through the ORM | `SurrealDbAuthenticationError` (normalised) | `SurrealDbAuthenticationError` (normalised) | v0.16.0 |
1385
+ | `DEFINE ACCESS … WITH REFRESH` and `AuthTokens.refresh` | not supported — the clause does not parse; `refresh` is always `None` | supported; `refresh` populated | v0.16.0 |
1386
+ | `signin(access=…, refresh=…)` renewal | raises `SurrealDbAuthenticationError` (no such access method is definable) | returns a fresh, rotated token pair; the spent one is rejected | v0.16.0 |
1387
+ | Session token replayed on reconnect (`get_client`, `reconnect`) | same on both lines | same on both lines | v0.16.0 |
1388
+ | `info()` when the record's table denies it `select` on itself | returns `None` (no error) | returns `None` (no error) | v0.16.0 |
1389
+ | Signing in as a system user while a record session is open | permissions change, `$auth` still points at the record — only `invalidate()` clears it | 3.2.x: same as 2.x. **3.3.0**: the identity is cleared and `info()` returns `None`; `invalidate()` stays the portable log-out | v0.16.0 |
1390
+ | Duplicate signin identifier in the record table | signin fails (`No record was returned`) | signin succeeds, picking one record | v0.16.0 |
1391
+ | Model auth (`signup`/`signin`/`authenticate`, `access_ddl`/`define_access`) | same on both lines (verified on 2.6.5) | same on both lines (verified on 3.2.4) | v0.17.0 |
1392
+ | Model auth session isolation (each call on its own ephemeral connection) | same on both lines (an SDK/connection property, not a server one) | same on both lines (verified on 3.2.4) | v0.17.0 |
1393
+ | Hydrated instance's password field after `signup`/`signin` | holds the stored **hash**, never the plaintext | same on both lines | v0.17.0 |
1394
+ | Model config `with_refresh=True` → `define_access()` | raises `SurrealDbError` (clause does not parse; message names the 3.x requirement) | applies, and `AuthResult.tokens.refresh` is populated | v0.17.0 |
1395
+ | `Model.refresh(token)` renewal | unavailable — no refresh token can exist | returns a fresh, rotated pair; the spent token is rejected | v0.17.0 |
1396
+ | Field aliases (`Field(alias=…)`) across writes, hydration and every QuerySet clause | same on both lines (pure Pydantic + client-side name translation) | same on both lines (verified on 3.2.4) | v0.18.0 |
1397
+ | `server_fields` — excluded from creates, kept on replaces, hydrated back | same on both lines; a `DEFAULT` is a create-time default on 2.6.5 | same on both lines; a `DEFAULT` is a create-time default on 3.2.4 | v0.18.0 |
1398
+ | A server column omitted from a REPLACE (`UPDATE`/`UPSERT … CONTENT`) | an optional column is deleted; a required one raises `Found NONE for field …` | same on both lines — which is why the ORM keeps it in replace payloads | v0.18.0 |
1399
+ | `merge(refresh=False)` — skipped resync, `RETURN NONE`, forfeited missing-record check | same on both lines | same on both lines (verified on 3.2.4) | v0.18.0 |
1400
+ | `merge()` on a table that was never created | server returns no rows → ORM raises `SurrealDbError` ("no record found") | server raises `NotFoundError` → normalised to the same `SurrealDbError` | v0.18.0 |
1401
+ | Live queries (`live()`, `subscribe_live()`, `kill()`) — the subscription itself | supported over WebSocket (verified on 2.6.5) | supported over WebSocket (verified on 3.2.4) | v0.19.0 |
1402
+ | `live()` on a table that does not exist | succeeds; the stream simply never fires | server raises `NotFound` → ORM raises `SurrealDbNotFoundError` | v0.19.0 |
1403
+ | Server notification after `kill()` | none is sent | a final envelope whose action is `KILLED` | v0.19.0 |
1404
+ | End of a `subscribe_live()` stream after `kill()` | ORM pushes its own sentinel so the `async for` ends | ends on the sentinel or the `KILLED` envelope, whichever lands first | v0.19.0 |
1405
+ | `session` key in a live notification envelope | absent | present (the SDK session uuid) | v0.19.0 |
1406
+ | Live queries over HTTP | refused by the ORM with a message naming the WebSocket requirement | same on both lines | v0.19.0 |
1407
+ | Full suite and every divergence in this table, re-run on SurrealDB 2.7.0 and 3.3.0 | 2.7.0 identical to 2.6.5 (same passes, same 3.x-only skips) | 3.3.0 identical to 3.2.4, except the system-user signin row | v0.19.1 |
1403
1408
 
1404
1409
  > **Note on record IDs**: A record loaded from the database has its `id` field set to a native `surrealdb.RecordID` object, not a plain string. Use `model.get_raw_id()` to obtain the bare identifier string (e.g. `"alice"`), or compare directly with `model.id == RecordID("User", "alice")`. In-memory instances you construct yourself retain whatever value you assign.
1405
1410
 
@@ -1459,7 +1464,7 @@ SDK) and **server support**. Everything below is on the lite roadmap via the off
1459
1464
 
1460
1465
  | Feature | ORM-lite (official SDK) | ORM (custom SDK) |
1461
1466
  | ----------------------------- | ----------------------- | ---------------- |
1462
- | Supported SurrealDB | **2.6.x + 3.2.x** | 3.x only |
1467
+ | Supported SurrealDB | **2.6/2.7 + 3.2/3.3** | 3.x only |
1463
1468
  | CRUD & QuerySet | ✅ | ✅ |
1464
1469
  | Aggregations & GROUP BY | ✅ | ✅ |
1465
1470
  | Model Signals | ✅ | ✅ |
@@ -1499,7 +1504,7 @@ SDK) and **server support**. Everything below is on the lite roadmap via the off
1499
1504
  | Custom SDK / CBOR Protocol | ❌ never | ✅ |
1500
1505
 
1501
1506
  **Choose ORM-lite** if you want the official SDK, minimal dependencies, support for SurrealDB
1502
- 2.6.x **and** 3.2.x, and a full feature roadmap built entirely on the official SDK.
1507
+ 2.x (2.6 / 2.7) **and** 3.x (3.2 / 3.3), and a full feature roadmap built entirely on the official SDK.
1503
1508
 
1504
1509
  **Choose ORM** if you need the custom-SDK internals (CBOR protocol, native connection pool)
1505
1510
  or those features available today rather than on the roadmap.
@@ -1,7 +1,7 @@
1
1
  # Surreal ORM Lite
2
2
 
3
3
  ![Python](https://img.shields.io/badge/python-3.11%2B-blue)
4
- ![SurrealDB](https://img.shields.io/badge/SurrealDB-2.6.5%20%7C%203.2.4-purple)
4
+ ![SurrealDB](https://img.shields.io/badge/SurrealDB-2.7.0%20%7C%203.3.0-purple)
5
5
  ![SDK](https://img.shields.io/badge/SDK-Official%202.0-green)
6
6
  ![License](https://img.shields.io/badge/license-MIT-blue)
7
7
  [![codecov](https://codecov.io/gh/EulogySnowfall/SurrealDB-ORM-lite/graph/badge.svg)](https://codecov.io/gh/EulogySnowfall/SurrealDB-ORM-lite)
@@ -24,11 +24,11 @@ This ORM is designed to:
24
24
  | Dependency | Version |
25
25
  | ------------ | --------------------------------- |
26
26
  | Python | 3.11+ |
27
- | SurrealDB | 2.6.x or 3.2.x |
27
+ | SurrealDB | 2.6.x / 2.7.x or 3.2.x / 3.3.x |
28
28
  | Official SDK | surrealdb[pydantic]>=2.0.0,<3.0.0 |
29
29
  | Pydantic | >=2.13.4 |
30
30
 
31
- > **Note**: As of v0.7.0, Surreal ORM Lite targets the SurrealDB Python SDK 2.x (`surrealdb[pydantic]>=2.0.0,<3.0.0`), which supports the SurrealDB 3.x protocol. It is tested against SurrealDB **v2.6.5** and **v3.2.4**. SurrealDB 3.1.x is no longer a supported line as of v0.14.0 — the suite is still run against 3.1.5 as a backward-compatibility check, but a regression there does not block a release.
31
+ > **Note**: As of v0.7.0, Surreal ORM Lite targets the SurrealDB Python SDK 2.x (`surrealdb[pydantic]>=2.0.0,<3.0.0`), which supports the SurrealDB 3.x protocol. It is tested against SurrealDB **v2.7.0** and **v3.3.0**; up to v0.19.0 the pins were v2.6.5 and v3.2.4. The 2.x line behaves identically on 2.7.0, and 3.3.0 differs from 3.2.4 in one server behaviour, listed in the behaviour table. SurrealDB 3.1.x is no longer a supported line as of v0.14.0 — the suite is still run against 3.1.5 as a backward-compatibility check, but a regression there does not block a release.
32
32
 
33
33
  ---
34
34
 
@@ -583,7 +583,7 @@ await user.merge(plan="pro", server_values={"updated_at": SurrealFunc.call(Surre
583
583
 
584
584
  `SurrealFunc.call(fn, *args)` builds `fn(arg, …)` from a function name — a plain string or a
585
585
  member of the shipped enums, which give you autocompletion over a **curated catalog whose every
586
- member is tested against SurrealDB 2.6.5 and 3.2.4**:
586
+ member is tested against SurrealDB 2.7.0 and 3.3.0**:
587
587
 
588
588
  | Enum | Covers |
589
589
  | ----------------------- | ---------------------------------------------------------------- |
@@ -1093,7 +1093,7 @@ next `refresh()`. Keep the default whenever you need to know the write landed.
1093
1093
 
1094
1094
  A live query is a server-side subscription: SurrealDB pushes a notification every time a
1095
1095
  record in the watched table is created, updated or deleted. It needs a **WebSocket**
1096
- connection, and it works on both SurrealDB 2.6.x and 3.2.x.
1096
+ connection, and it works on both SurrealDB 2.x (2.6 / 2.7) and 3.x (3.2 / 3.3).
1097
1097
 
1098
1098
  The simplest form is `watch()`, an async context manager that kills the subscription when the
1099
1099
  block exits:
@@ -1279,77 +1279,82 @@ Long-lived single-loop applications are unaffected: one loop, one connection, as
1279
1279
 
1280
1280
  As of v0.7.0, Surreal ORM Lite uses `surrealdb[pydantic]>=2.0.0,<3.0.0` (SurrealDB 3.x protocol) and is tested against both major SurrealDB release lines.
1281
1281
 
1282
- **Compatibility advantage over the full ORM**: ORM-lite runs on **both** SurrealDB **2.6.x and 3.2.x**, while the full [SurrealDB-ORM](https://github.com/EulogySnowfall/SurrealDB-ORM/) (custom SDK) targets **3.x only**. Lite stays usable on existing 2.6.x deployments without forcing a server upgrade.
1282
+ **Compatibility advantage over the full ORM**: ORM-lite runs on **both** SurrealDB **2.x (2.6 / 2.7) and 3.x (3.2 / 3.3)**, while the full [SurrealDB-ORM](https://github.com/EulogySnowfall/SurrealDB-ORM/) (custom SDK) targets **3.x only**. Lite stays usable on existing 2.6.x deployments without forcing a server upgrade.
1283
1283
 
1284
- | SurrealDB Version | SDK Version | Status |
1285
- | ----------------- | ----------- | ----------------------- |
1286
- | 3.2.4 | 2.0 | ✅ Tested |
1287
- | 2.6.5 | 2.0 | ✅ Tested |
1288
- | 3.2.x / 2.6.x | 2.0 | ✅ Compatible |
1289
- | 3.1.5 | 2.0 | ⚠️ Backward-compat only |
1290
- | < 2.6 or > 3.2 | — | ⚠️ Not guaranteed |
1284
+ | SurrealDB Version | SDK Version | Status |
1285
+ | ----------------------------- | ----------- | ------------------------------------ |
1286
+ | 3.3.0 | 2.0 | ✅ Tested |
1287
+ | 2.7.0 | 2.0 | ✅ Tested |
1288
+ | 3.2.4 | 2.0 | ✅ Compatible (tested up to v0.19.0) |
1289
+ | 2.6.5 | 2.0 | ✅ Compatible (tested up to v0.19.0) |
1290
+ | 3.3.x / 3.2.x / 2.7.x / 2.6.x | 2.0 | ✅ Compatible |
1291
+ | 3.1.5 | 2.0 | ⚠️ Backward-compat only |
1292
+ | < 2.6 or > 3.3 | — | ⚠️ Not guaranteed |
1291
1293
 
1292
- ### ORM behaviour: SurrealDB 2.6.x vs 3.x
1294
+ ### ORM behaviour: SurrealDB 2.x vs 3.x
1293
1295
 
1294
1296
  Surreal ORM Lite runs on both lines; some capabilities differ because they rely on server
1295
- features introduced in SurrealDB 3.x. On 2.6.x the ORM degrades gracefully. Capabilities not
1296
- listed behave the same on both lines.
1297
-
1298
- | ORM capability | SurrealDB 2.6.x | SurrealDB 3.2.x | Since |
1299
- | -------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ | ------- |
1300
- | Transaction strategy auto-selected by `transaction()` | buffered batch (`BEGIN…COMMIT`) | native interactive on WebSocket | v0.9.0 |
1301
- | Reads inside a transaction (`objects(tx=)`) | raise (buffered cannot read) | see uncommitted writes | v0.9.0 |
1302
- | `save(tx=)` with an auto-generated id | raises — explicit id required | supported | v0.9.0 |
1303
- | `refresh(tx=)` inside a transaction | raises | works | v0.9.0 |
1304
- | `bulk_update` / `bulk_delete` / `QuerySet.patch` row count inside a tx | returns `0` (not knowable pre-commit) | real count | v0.9.0 |
1305
- | "Already exists" error on create | normalised to `SurrealDbError` | normalised to `SurrealDbError` | v0.7.0 |
1306
- | Cleanup on a missing target (`delete_table`, `remove_relation`) | native no-op | ORM makes it a silent no-op | v0.7.0 |
1307
- | Aggregation over an empty set (`NaN` / `±inf`) | returns `0.0` / `None` | ORM normalises to `0.0` / `None` | v0.7.0 |
1308
- | Namespace/db selection (`use()` ordering) | lenient (auto-creates) | strict — ORM signs in before `use()` | v0.7.0 |
1309
- | `upsert()` / `update_or_create()` / `get_or_create()` | same on both lines | same on both lines | v0.10.0 |
1310
- | `patch()` / `atomic_append` / `atomic_set_add` / `atomic_remove` / `atomic_increment` | same on both lines (portable `array::*` fns chosen over divergent `+=`/`-=`) | same on both lines | v0.11.0 |
1311
- | `retry_on_conflict` / `SurrealDbConflictError` (retryable conflict) | same type + decorator; conflicts rarer (engine serialises more) | same type + decorator; conflicts are the normal optimistic-MVCC failure | v0.12.0 |
1312
- | `SurrealFunc` / `server_values=` / `extra_vars=` on `save`/`merge` | same on both lines (compiled to portable `CREATE`/`UPDATE … SET`) | same on both lines | v0.13.0 |
1313
- | Shipped function-name enums (`SurrealTimeFunction`, `SurrealCryptoFunction`, …) | every catalogued member verified on 2.6.5 | every catalogued member verified on 3.2.4 | v0.13.0 |
1314
- | `server_values` inside a transaction — when the instance sees the computed value | only after commit (buffered; `refresh()` to read it) | immediately (interactive returns the row) | v0.13.0 |
1315
- | `merge(server_values=)` on a missing record / never-created table | server returns no rows → ORM raises `SurrealDbError` | server raises `NotFound` for a missing table → ORM raises the same error | v0.13.0 |
1316
- | Computed fields (`Computed[...]` → `DEFINE FIELD … VALUE`) | same on both lines (DDL, recompute triggers, precedence over client data) | same on both lines | v0.14.0 |
1317
- | DDL run inside a transaction, then rolled back | definition rolled back with the transaction | same on both lines | v0.14.0 |
1318
- | Invalid computed-field expression — raw SDK exception | `InternalError` | `ValidationError` | v0.14.0 |
1319
- | Invalid computed-field expression — through the ORM | `SurrealDbError` (normalised) | `SurrealDbError` (normalised) | v0.14.0 |
1320
- | Issue #156 correctness fixes (`Var`/`$$`, `first()`, `*_or_create` strictness, `created` on upsert, quoted ids, one-hop `get_related`) | same on both lines (each fix reproduced and verified on 2.6.5) | same on both lines (verified on 3.2.4) | v0.14.3 |
1321
- | Record-id lookups (`filter(id=…)`, `id__in`, `get(…)`, `*_or_create(id=…)`) coerced to `RecordID` | same on both lines (int/str typing verified on 2.6.5) | same on both lines (verified on 3.2.4) | v0.14.4 |
1322
- | Per-event-loop client cache (`get_client`, `close_connection`, `close_all_connections`) | same on both lines (loop binding is an asyncio/SDK property, not a server one) | same on both lines (verified on 3.2.4) | v0.14.5 |
1323
- | `call_function()` — the call itself (`args`, `params`, `return_type`, nested `fn::a::b`) | same on both lines (bare call form chosen so it is portable) | same on both lines (verified on 3.2.4) | v0.15.0 |
1324
- | `call_function(tx=)` — return value | `None` (buffered: queued until commit) | the function's value (interactive returns it immediately) | v0.15.0 |
1325
- | `call_function(tx=, return_type=)` | raises `ValueError` (no value to coerce yet) | coerces the returned value | v0.15.0 |
1326
- | Missing function called inside a transaction | surfaces at COMMIT as `SurrealDbError` (buffered: the call is only queued) | raises `SurrealDbNotFoundError` at call time | v0.15.0 |
1327
- | Declared parameter name that collides with a reserved word, as echoed by `INFO FOR DB` | quoted: `` $`by` `` — parser accepts it | bare: `$by` | v0.15.0 |
1328
- | Auth methods (`signin`, `signup`, `authenticate`, `invalidate`, `info`) | same on both lines (native SDK primitives, verified on 2.6.5) | same on both lines (verified on 3.2.4) | v0.16.0 |
1329
- | Auth failure — raw SDK exception for a wrong password | `InternalError` | `NotFoundError` | v0.16.0 |
1330
- | Auth failure — through the ORM | `SurrealDbAuthenticationError` (normalised) | `SurrealDbAuthenticationError` (normalised) | v0.16.0 |
1331
- | `DEFINE ACCESS … WITH REFRESH` and `AuthTokens.refresh` | not supported — the clause does not parse; `refresh` is always `None` | supported; `refresh` populated | v0.16.0 |
1332
- | `signin(access=…, refresh=…)` renewal | raises `SurrealDbAuthenticationError` (no such access method is definable) | returns a fresh, rotated token pair; the spent one is rejected | v0.16.0 |
1333
- | Session token replayed on reconnect (`get_client`, `reconnect`) | same on both lines | same on both lines | v0.16.0 |
1334
- | `info()` when the record's table denies it `select` on itself | returns `None` (no error) | returns `None` (no error) | v0.16.0 |
1335
- | Signing in as a system user while a record session is open | permissions change, `$auth` still points at the record — only `invalidate()` clears it | same on both lines | v0.16.0 |
1336
- | Duplicate signin identifier in the record table | signin fails (`No record was returned`) | signin succeeds, picking one record | v0.16.0 |
1337
- | Model auth (`signup`/`signin`/`authenticate`, `access_ddl`/`define_access`) | same on both lines (verified on 2.6.5) | same on both lines (verified on 3.2.4) | v0.17.0 |
1338
- | Model auth session isolation (each call on its own ephemeral connection) | same on both lines (an SDK/connection property, not a server one) | same on both lines (verified on 3.2.4) | v0.17.0 |
1339
- | Hydrated instance's password field after `signup`/`signin` | holds the stored **hash**, never the plaintext | same on both lines | v0.17.0 |
1340
- | Model config `with_refresh=True` → `define_access()` | raises `SurrealDbError` (clause does not parse; message names the 3.x requirement) | applies, and `AuthResult.tokens.refresh` is populated | v0.17.0 |
1341
- | `Model.refresh(token)` renewal | unavailable — no refresh token can exist | returns a fresh, rotated pair; the spent token is rejected | v0.17.0 |
1342
- | Field aliases (`Field(alias=…)`) across writes, hydration and every QuerySet clause | same on both lines (pure Pydantic + client-side name translation) | same on both lines (verified on 3.2.4) | v0.18.0 |
1343
- | `server_fields` — excluded from creates, kept on replaces, hydrated back | same on both lines; a `DEFAULT` is a create-time default on 2.6.5 | same on both lines; a `DEFAULT` is a create-time default on 3.2.4 | v0.18.0 |
1344
- | A server column omitted from a REPLACE (`UPDATE`/`UPSERT … CONTENT`) | an optional column is deleted; a required one raises `Found NONE for field …` | same on both lines — which is why the ORM keeps it in replace payloads | v0.18.0 |
1345
- | `merge(refresh=False)` — skipped resync, `RETURN NONE`, forfeited missing-record check | same on both lines | same on both lines (verified on 3.2.4) | v0.18.0 |
1346
- | `merge()` on a table that was never created | server returns no rows → ORM raises `SurrealDbError` ("no record found") | server raises `NotFoundError` → normalised to the same `SurrealDbError` | v0.18.0 |
1347
- | Live queries (`live()`, `subscribe_live()`, `kill()`) — the subscription itself | supported over WebSocket (verified on 2.6.5) | supported over WebSocket (verified on 3.2.4) | v0.19.0 |
1348
- | `live()` on a table that does not exist | succeeds; the stream simply never fires | server raises `NotFound` → ORM raises `SurrealDbNotFoundError` | v0.19.0 |
1349
- | Server notification after `kill()` | none is sent | a final envelope whose action is `KILLED` | v0.19.0 |
1350
- | End of a `subscribe_live()` stream after `kill()` | ORM pushes its own sentinel so the `async for` ends | ends on the sentinel or the `KILLED` envelope, whichever lands first | v0.19.0 |
1351
- | `session` key in a live notification envelope | absent | present (the SDK session uuid) | v0.19.0 |
1352
- | Live queries over HTTP | refused by the ORM with a message naming the WebSocket requirement | same on both lines | v0.19.0 |
1297
+ features introduced in SurrealDB 3.x. On 2.x the ORM degrades gracefully. Capabilities not
1298
+ listed behave the same on both lines. The 2.x column was measured on 2.6.5 and holds unchanged
1299
+ on 2.7.0. The 3.x column was measured on 3.2.4 and holds on 3.3.0, except where a row says
1300
+ otherwise. The full suite and every divergence below were re-verified on both new versions.
1301
+
1302
+ | ORM capability | SurrealDB 2.6.x / 2.7.x | SurrealDB 3.2.x / 3.3.x | Since |
1303
+ | -------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | ------- |
1304
+ | Transaction strategy auto-selected by `transaction()` | buffered batch (`BEGIN…COMMIT`) | native interactive on WebSocket | v0.9.0 |
1305
+ | Reads inside a transaction (`objects(tx=)`) | raise (buffered cannot read) | see uncommitted writes | v0.9.0 |
1306
+ | `save(tx=)` with an auto-generated id | raises — explicit id required | supported | v0.9.0 |
1307
+ | `refresh(tx=)` inside a transaction | raises | works | v0.9.0 |
1308
+ | `bulk_update` / `bulk_delete` / `QuerySet.patch` row count inside a tx | returns `0` (not knowable pre-commit) | real count | v0.9.0 |
1309
+ | "Already exists" error on create | normalised to `SurrealDbError` | normalised to `SurrealDbError` | v0.7.0 |
1310
+ | Cleanup on a missing target (`delete_table`, `remove_relation`) | native no-op | ORM makes it a silent no-op | v0.7.0 |
1311
+ | Aggregation over an empty set (`NaN` / `±inf`) | returns `0.0` / `None` | ORM normalises to `0.0` / `None` | v0.7.0 |
1312
+ | Namespace/db selection (`use()` ordering) | lenient (auto-creates) | strict — ORM signs in before `use()` | v0.7.0 |
1313
+ | `upsert()` / `update_or_create()` / `get_or_create()` | same on both lines | same on both lines | v0.10.0 |
1314
+ | `patch()` / `atomic_append` / `atomic_set_add` / `atomic_remove` / `atomic_increment` | same on both lines (portable `array::*` fns chosen over divergent `+=`/`-=`) | same on both lines | v0.11.0 |
1315
+ | `retry_on_conflict` / `SurrealDbConflictError` (retryable conflict) | same type + decorator; conflicts rarer (engine serialises more) | same type + decorator; conflicts are the normal optimistic-MVCC failure | v0.12.0 |
1316
+ | `SurrealFunc` / `server_values=` / `extra_vars=` on `save`/`merge` | same on both lines (compiled to portable `CREATE`/`UPDATE … SET`) | same on both lines | v0.13.0 |
1317
+ | Shipped function-name enums (`SurrealTimeFunction`, `SurrealCryptoFunction`, …) | every catalogued member verified on 2.7.0 | every catalogued member verified on 3.3.0 | v0.13.0 |
1318
+ | `server_values` inside a transaction — when the instance sees the computed value | only after commit (buffered; `refresh()` to read it) | immediately (interactive returns the row) | v0.13.0 |
1319
+ | `merge(server_values=)` on a missing record / never-created table | server returns no rows → ORM raises `SurrealDbError` | server raises `NotFound` for a missing table → ORM raises the same error | v0.13.0 |
1320
+ | Computed fields (`Computed[...]` → `DEFINE FIELD … VALUE`) | same on both lines (DDL, recompute triggers, precedence over client data) | same on both lines | v0.14.0 |
1321
+ | DDL run inside a transaction, then rolled back | definition rolled back with the transaction | same on both lines | v0.14.0 |
1322
+ | Invalid computed-field expression — raw SDK exception | `InternalError` | `ValidationError` | v0.14.0 |
1323
+ | Invalid computed-field expression — through the ORM | `SurrealDbError` (normalised) | `SurrealDbError` (normalised) | v0.14.0 |
1324
+ | Issue #156 correctness fixes (`Var`/`$$`, `first()`, `*_or_create` strictness, `created` on upsert, quoted ids, one-hop `get_related`) | same on both lines (each fix reproduced and verified on 2.6.5) | same on both lines (verified on 3.2.4) | v0.14.3 |
1325
+ | Record-id lookups (`filter(id=…)`, `id__in`, `get(…)`, `*_or_create(id=…)`) coerced to `RecordID` | same on both lines (int/str typing verified on 2.6.5) | same on both lines (verified on 3.2.4) | v0.14.4 |
1326
+ | Per-event-loop client cache (`get_client`, `close_connection`, `close_all_connections`) | same on both lines (loop binding is an asyncio/SDK property, not a server one) | same on both lines (verified on 3.2.4) | v0.14.5 |
1327
+ | `call_function()` — the call itself (`args`, `params`, `return_type`, nested `fn::a::b`) | same on both lines (bare call form chosen so it is portable) | same on both lines (verified on 3.2.4) | v0.15.0 |
1328
+ | `call_function(tx=)` — return value | `None` (buffered: queued until commit) | the function's value (interactive returns it immediately) | v0.15.0 |
1329
+ | `call_function(tx=, return_type=)` | raises `ValueError` (no value to coerce yet) | coerces the returned value | v0.15.0 |
1330
+ | Missing function called inside a transaction | surfaces at COMMIT as `SurrealDbError` (buffered: the call is only queued) | raises `SurrealDbNotFoundError` at call time | v0.15.0 |
1331
+ | Declared parameter name that collides with a reserved word, as echoed by `INFO FOR DB` | quoted: `` $`by` `` — parser accepts it | bare: `$by` | v0.15.0 |
1332
+ | Auth methods (`signin`, `signup`, `authenticate`, `invalidate`, `info`) | same on both lines (native SDK primitives, verified on 2.6.5) | same on both lines (verified on 3.2.4) | v0.16.0 |
1333
+ | Auth failure — raw SDK exception for a wrong password | `InternalError` | `NotFoundError` | v0.16.0 |
1334
+ | Auth failure — through the ORM | `SurrealDbAuthenticationError` (normalised) | `SurrealDbAuthenticationError` (normalised) | v0.16.0 |
1335
+ | `DEFINE ACCESS … WITH REFRESH` and `AuthTokens.refresh` | not supported — the clause does not parse; `refresh` is always `None` | supported; `refresh` populated | v0.16.0 |
1336
+ | `signin(access=…, refresh=…)` renewal | raises `SurrealDbAuthenticationError` (no such access method is definable) | returns a fresh, rotated token pair; the spent one is rejected | v0.16.0 |
1337
+ | Session token replayed on reconnect (`get_client`, `reconnect`) | same on both lines | same on both lines | v0.16.0 |
1338
+ | `info()` when the record's table denies it `select` on itself | returns `None` (no error) | returns `None` (no error) | v0.16.0 |
1339
+ | Signing in as a system user while a record session is open | permissions change, `$auth` still points at the record — only `invalidate()` clears it | 3.2.x: same as 2.x. **3.3.0**: the identity is cleared and `info()` returns `None`; `invalidate()` stays the portable log-out | v0.16.0 |
1340
+ | Duplicate signin identifier in the record table | signin fails (`No record was returned`) | signin succeeds, picking one record | v0.16.0 |
1341
+ | Model auth (`signup`/`signin`/`authenticate`, `access_ddl`/`define_access`) | same on both lines (verified on 2.6.5) | same on both lines (verified on 3.2.4) | v0.17.0 |
1342
+ | Model auth session isolation (each call on its own ephemeral connection) | same on both lines (an SDK/connection property, not a server one) | same on both lines (verified on 3.2.4) | v0.17.0 |
1343
+ | Hydrated instance's password field after `signup`/`signin` | holds the stored **hash**, never the plaintext | same on both lines | v0.17.0 |
1344
+ | Model config `with_refresh=True` → `define_access()` | raises `SurrealDbError` (clause does not parse; message names the 3.x requirement) | applies, and `AuthResult.tokens.refresh` is populated | v0.17.0 |
1345
+ | `Model.refresh(token)` renewal | unavailable — no refresh token can exist | returns a fresh, rotated pair; the spent token is rejected | v0.17.0 |
1346
+ | Field aliases (`Field(alias=…)`) across writes, hydration and every QuerySet clause | same on both lines (pure Pydantic + client-side name translation) | same on both lines (verified on 3.2.4) | v0.18.0 |
1347
+ | `server_fields` — excluded from creates, kept on replaces, hydrated back | same on both lines; a `DEFAULT` is a create-time default on 2.6.5 | same on both lines; a `DEFAULT` is a create-time default on 3.2.4 | v0.18.0 |
1348
+ | A server column omitted from a REPLACE (`UPDATE`/`UPSERT … CONTENT`) | an optional column is deleted; a required one raises `Found NONE for field …` | same on both lines — which is why the ORM keeps it in replace payloads | v0.18.0 |
1349
+ | `merge(refresh=False)` — skipped resync, `RETURN NONE`, forfeited missing-record check | same on both lines | same on both lines (verified on 3.2.4) | v0.18.0 |
1350
+ | `merge()` on a table that was never created | server returns no rows → ORM raises `SurrealDbError` ("no record found") | server raises `NotFoundError` → normalised to the same `SurrealDbError` | v0.18.0 |
1351
+ | Live queries (`live()`, `subscribe_live()`, `kill()`) — the subscription itself | supported over WebSocket (verified on 2.6.5) | supported over WebSocket (verified on 3.2.4) | v0.19.0 |
1352
+ | `live()` on a table that does not exist | succeeds; the stream simply never fires | server raises `NotFound` → ORM raises `SurrealDbNotFoundError` | v0.19.0 |
1353
+ | Server notification after `kill()` | none is sent | a final envelope whose action is `KILLED` | v0.19.0 |
1354
+ | End of a `subscribe_live()` stream after `kill()` | ORM pushes its own sentinel so the `async for` ends | ends on the sentinel or the `KILLED` envelope, whichever lands first | v0.19.0 |
1355
+ | `session` key in a live notification envelope | absent | present (the SDK session uuid) | v0.19.0 |
1356
+ | Live queries over HTTP | refused by the ORM with a message naming the WebSocket requirement | same on both lines | v0.19.0 |
1357
+ | Full suite and every divergence in this table, re-run on SurrealDB 2.7.0 and 3.3.0 | 2.7.0 identical to 2.6.5 (same passes, same 3.x-only skips) | 3.3.0 identical to 3.2.4, except the system-user signin row | v0.19.1 |
1353
1358
 
1354
1359
  > **Note on record IDs**: A record loaded from the database has its `id` field set to a native `surrealdb.RecordID` object, not a plain string. Use `model.get_raw_id()` to obtain the bare identifier string (e.g. `"alice"`), or compare directly with `model.id == RecordID("User", "alice")`. In-memory instances you construct yourself retain whatever value you assign.
1355
1360
 
@@ -1409,7 +1414,7 @@ SDK) and **server support**. Everything below is on the lite roadmap via the off
1409
1414
 
1410
1415
  | Feature | ORM-lite (official SDK) | ORM (custom SDK) |
1411
1416
  | ----------------------------- | ----------------------- | ---------------- |
1412
- | Supported SurrealDB | **2.6.x + 3.2.x** | 3.x only |
1417
+ | Supported SurrealDB | **2.6/2.7 + 3.2/3.3** | 3.x only |
1413
1418
  | CRUD & QuerySet | ✅ | ✅ |
1414
1419
  | Aggregations & GROUP BY | ✅ | ✅ |
1415
1420
  | Model Signals | ✅ | ✅ |
@@ -1449,7 +1454,7 @@ SDK) and **server support**. Everything below is on the lite roadmap via the off
1449
1454
  | Custom SDK / CBOR Protocol | ❌ never | ✅ |
1450
1455
 
1451
1456
  **Choose ORM-lite** if you want the official SDK, minimal dependencies, support for SurrealDB
1452
- 2.6.x **and** 3.2.x, and a full feature roadmap built entirely on the official SDK.
1457
+ 2.x (2.6 / 2.7) **and** 3.x (3.2 / 3.3), and a full feature roadmap built entirely on the official SDK.
1453
1458
 
1454
1459
  **Choose ORM** if you need the custom-SDK internals (CBOR protocol, native connection pool)
1455
1460
  or those features available today rather than on the roadmap.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "surreal-orm-lite"
3
- version = "0.19.0"
3
+ version = "0.19.2"
4
4
  description = "Lightweight Django-style ORM for SurrealDB using the official Python SDK. Async support with Pydantic validation."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -1,4 +1,4 @@
1
- __version__ = "0.19.0"
1
+ __version__ = "0.19.2"
2
2
 
3
3
  from .aggregations import Aggregation, Avg, Count, Max, Min, Sum
4
4
  from .auth import AuthTokens
@@ -573,9 +573,11 @@ class SurrealDBConnectionManager:
573
573
 
574
574
  await SurrealDBConnectionManager.invalidate() # log the record user out
575
575
 
576
- This is the only way to really end a record session: signing in as a system user swaps
577
- the permissions but leaves ``$auth`` pointing at the record, so ``info()`` would keep
578
- reporting it.
576
+ This is the only portable way to end a record session. Up to SurrealDB 3.2.x (and on
577
+ the whole 2.x line) signing in as a system user swaps the permissions but leaves
578
+ ``$auth`` pointing at the record, so ``info()`` would keep reporting it; 3.3.0 clears
579
+ the identity on such a signin, but code that must run on every supported line should
580
+ not rely on that.
579
581
 
580
582
  The SDK's ``invalidate()`` on its own leaves the session **anonymous**, and since the
581
583
  manager caches one client per event loop, every later ORM call on that connection —
@@ -676,9 +678,10 @@ class SurrealDBConnectionManager:
676
678
  model that was never the culprit.
677
679
 
678
680
  .. note::
679
- After signing in as a system user, this can still report the record from an
680
- earlier record session: SurrealDB swaps the permissions but leaves ``$auth`` in
681
- place. Only :meth:`invalidate` really ends a record session.
681
+ Up to SurrealDB 3.2.x, and on the 2.x line, signing in as a system user swaps the
682
+ permissions but leaves ``$auth`` in place, so this can still report the record from
683
+ an earlier record session. SurrealDB 3.3.0 clears the identity on that signin and
684
+ this returns ``None``. :meth:`invalidate` ends a record session on every line.
682
685
 
683
686
  :param return_type: a type annotation to coerce the record into — a Pydantic model, a
684
687
  dataclass, or anything else Pydantic can adapt. ``None`` returns the raw mapping.