-
Notifications
You must be signed in to change notification settings - Fork 17
Add transaction to event slots #233
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
ff1b491
806c179
866c4aa
c9b332e
8a0afba
9fed42e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,7 +2,7 @@ | |
|
||
from functools import partial | ||
from types import TracebackType | ||
from typing import TYPE_CHECKING, Any | ||
from typing import TYPE_CHECKING, Any, cast | ||
|
||
from anyio import to_thread | ||
|
||
|
@@ -12,7 +12,38 @@ | |
from ._doc import Doc | ||
|
||
|
||
class Transaction: | ||
class BaseTransaction: | ||
""" | ||
Base class for read-write and read-only transactions. | ||
|
||
It allows to persist the origin of the transaction outside of its context. | ||
""" | ||
|
||
_doc: Doc | ||
_origin_hash: int | None | ||
|
||
def __init__( | ||
self, | ||
doc: Doc, | ||
origin: Any = None, | ||
) -> None: | ||
self._doc = doc | ||
if origin is None: | ||
self._origin_hash = None | ||
else: | ||
self._origin_hash = doc._origins.add(origin) | ||
|
||
def __del__(self) -> None: | ||
if getattr(self, "_origin_hash", None) is not None: | ||
self._doc._origins.remove(cast(int, self._origin_hash)) | ||
|
||
@property | ||
def origin(self) -> Any: | ||
"""The origin of the transaction.""" | ||
return None if self._origin_hash is None else self._doc._origins.get(self._origin_hash) | ||
|
||
|
||
class Transaction(BaseTransaction): | ||
""" | ||
A read-write transaction that can be used to mutate a document. | ||
It must be used with a context manager (see [Doc.transaction()][pycrdt.Doc.transaction]): | ||
|
@@ -22,10 +53,8 @@ class Transaction: | |
``` | ||
""" | ||
|
||
_doc: Doc | ||
_txn: _Transaction | None | ||
_leases: int | ||
_origin_hash: int | None | ||
_timeout: float | ||
|
||
def __init__( | ||
|
@@ -36,14 +65,9 @@ def __init__( | |
origin: Any = None, | ||
timeout: float | None = None, | ||
) -> None: | ||
self._doc = doc | ||
super().__init__(doc, origin) | ||
self._txn = _txn | ||
self._leases = 0 | ||
if origin is None: | ||
self._origin_hash = None | ||
else: | ||
self._origin_hash = hash_origin(origin) | ||
doc._origins[self._origin_hash] = origin | ||
self._timeout = -1 if timeout is None else timeout | ||
|
||
def __enter__(self, _acquire_transaction: bool = True) -> Transaction: | ||
|
@@ -75,9 +99,6 @@ def __exit__( | |
assert self._txn is not None | ||
if not isinstance(self, ReadTransaction): | ||
self._txn.commit() | ||
origin_hash = self._txn.origin() | ||
if origin_hash is not None: | ||
del self._doc._origins[origin_hash] | ||
Comment on lines
-78
to
-80
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That was a very deterministic behavior, why did you move that part to the transaction's There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you leave the logic there, then the tests won't work. Because when requesting the transaction origin from the event object the entry in the The usage of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I think it's just a matter of dropping the transaction after removing the origin from the doc.
Yes it is, because now tests behave differently if running on CPython or PyPy. |
||
if self._doc._allow_multithreading: | ||
self._doc._txn_lock.release() | ||
self._txn.drop() | ||
|
@@ -99,7 +120,7 @@ def origin(self) -> Any: | |
if origin_hash is None: | ||
return None | ||
|
||
return self._doc._origins[origin_hash] | ||
return self._doc._origins.get(origin_hash) | ||
|
||
|
||
class NewTransaction(Transaction): | ||
|
@@ -141,10 +162,3 @@ class ReadTransaction(Transaction): | |
""" | ||
A read-only transaction that cannot be used to mutate a document. | ||
""" | ||
|
||
|
||
def hash_origin(origin: Any) -> int: | ||
try: | ||
return hash(origin) | ||
except Exception: | ||
raise TypeError("Origin must be hashable") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why using
getattr
, since_origin_hash
is set in__init__
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know why, but I got hit by missing
_origin_hash
in some cases. It may have to do with some inheritance pattern.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that it's something to investigate, instead of using
getattr
because we don't know why it doesn't work without. Otherwise that's going to lead to code that we don't understand and don't know how to maintain.