Skip to content

Commit a4c010e

Browse files
Add helper methods for checking indexes, keys and transactions (#49)
1 parent b687489 commit a4c010e

File tree

4 files changed

+39
-52
lines changed

4 files changed

+39
-52
lines changed

python/pycrdt/array.py

+19-27
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,7 @@ def _init(self, value: list[Any] | None) -> None:
3838

3939
def _set(self, index: int, value: Any) -> None:
4040
with self.doc.transaction() as txn:
41-
if isinstance(txn, ReadTransaction):
42-
raise RuntimeError(
43-
"Read-only transaction cannot be used to modify document structure"
44-
)
41+
self._forbid_read_transaction(txn)
4542
if isinstance(value, BaseDoc):
4643
# subdoc
4744
self.integrated.insert_doc(txn._txn, index, value._doc)
@@ -76,16 +73,16 @@ def insert(self, index, object) -> None:
7673

7774
def pop(self, index: int = -1) -> Any:
7875
with self.doc.transaction():
76+
index = self._check_index(index)
7977
res = self[index]
8078
del self[index]
8179
return res
8280

8381
def move(self, source_index: int, destination_index: int) -> None:
8482
with self.doc.transaction() as txn:
85-
if isinstance(txn, ReadTransaction):
86-
raise RuntimeError(
87-
"Read-only transaction cannot be used to modify document structure"
88-
)
83+
self._forbid_read_transaction(txn)
84+
source_index = self._check_index(source_index)
85+
destination_index = self._check_index(destination_index)
8986
self.integrated.move_to(txn._txn, source_index, destination_index)
9087

9188
def __add__(self, value: list[Any]) -> Array:
@@ -102,11 +99,7 @@ def __radd__(self, value: list[Any]) -> Array:
10299
def __setitem__(self, key: int | slice, value: Any | list[Any]) -> None:
103100
with self.doc.transaction():
104101
if isinstance(key, int):
105-
length = len(self)
106-
if length == 0:
107-
raise IndexError("Array index out of range")
108-
if key < 0:
109-
key += length
102+
key = self._check_index(key)
110103
del self[key]
111104
self[key:key] = [value]
112105
elif isinstance(key, slice):
@@ -121,18 +114,21 @@ def __setitem__(self, key: int | slice, value: Any | list[Any]) -> None:
121114
else:
122115
raise RuntimeError(f"Index not supported: {key}")
123116

117+
def _check_index(self, idx: int) -> int:
118+
if not isinstance(idx, int):
119+
raise RuntimeError("Index must be of type int")
120+
length = len(self)
121+
if idx < 0:
122+
idx += length
123+
if idx < 0 or idx >= length:
124+
raise IndexError("Array index out of range")
125+
return idx
126+
124127
def __delitem__(self, key: int | slice) -> None:
125128
with self.doc.transaction() as txn:
126-
if isinstance(txn, ReadTransaction):
127-
raise RuntimeError(
128-
"Read-only transaction cannot be used to modify document structure"
129-
)
129+
self._forbid_read_transaction(txn)
130130
if isinstance(key, int):
131-
length = len(self)
132-
if length == 0:
133-
raise IndexError("Array index out of range")
134-
if key < 0:
135-
key += length
131+
key = self._check_index(key)
136132
self.integrated.remove_range(txn._txn, key, 1)
137133
elif isinstance(key, slice):
138134
if key.step is not None:
@@ -156,11 +152,7 @@ def __delitem__(self, key: int | slice) -> None:
156152
def __getitem__(self, key: int) -> BaseType:
157153
with self.doc.transaction() as txn:
158154
if isinstance(key, int):
159-
length = len(self)
160-
if length == 0:
161-
raise IndexError("Array index out of range")
162-
if key < 0:
163-
key += length
155+
key = self._check_index(key)
164156
return self._maybe_as_type_or_doc(self.integrated.get(txn._txn, key))
165157
elif isinstance(key, slice):
166158
i0 = 0 if key.start is None else key.start

python/pycrdt/base.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
from ._pycrdt import Doc as _Doc
77
from ._pycrdt import Transaction as _Transaction
8-
from .transaction import Transaction
8+
from .transaction import ReadTransaction, Transaction
99

1010
if TYPE_CHECKING:
1111
from .doc import Doc
@@ -74,6 +74,12 @@ def _get_or_insert(self, name: str, doc: Doc) -> Any:
7474
def _init(self, value: Any | None) -> None:
7575
...
7676

77+
def _forbid_read_transaction(self, txn: Transaction):
78+
if isinstance(txn, ReadTransaction):
79+
raise RuntimeError(
80+
"Read-only transaction cannot be used to modify document structure"
81+
)
82+
7783
def _current_transaction(self) -> Transaction:
7884
if self._doc is None:
7985
raise RuntimeError("Not associated with a document")

python/pycrdt/map.py

+10-12
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,7 @@ def _init(self, value: dict[str, Any] | None) -> None:
3838

3939
def _set(self, key: str, value: Any) -> None:
4040
with self.doc.transaction() as txn:
41-
if isinstance(txn, ReadTransaction):
42-
raise RuntimeError(
43-
"Read-only transaction cannot be used to modify document structure"
44-
)
41+
self._forbid_read_transaction(txn)
4542
if isinstance(value, BaseDoc):
4643
# subdoc
4744
self.integrated.insert_doc(txn._txn, key, value._doc)
@@ -70,19 +67,14 @@ def to_py(self) -> dict | None:
7067
return dict(self)
7168

7269
def __delitem__(self, key: str) -> None:
73-
if not isinstance(key, str):
74-
raise RuntimeError("Key must be of type string")
7570
with self.doc.transaction() as txn:
76-
if isinstance(txn, ReadTransaction):
77-
raise RuntimeError(
78-
"Read-only transaction cannot be used to modify document structure"
79-
)
71+
self._forbid_read_transaction(txn)
72+
self._check_key(key)
8073
self.integrated.remove(txn._txn, key)
8174

8275
def __getitem__(self, key: str) -> Any:
8376
with self.doc.transaction() as txn:
84-
if not isinstance(key, str):
85-
raise RuntimeError("Key must be of type string")
77+
self._check_key(key)
8678
return self._maybe_as_type_or_doc(self.integrated.get(txn._txn, key))
8779

8880
def __setitem__(self, key: str, value: Any) -> None:
@@ -112,6 +104,12 @@ def pop(self, key: str, default_value: Any | None = None) -> Any:
112104
del self[key]
113105
return res
114106

107+
def _check_key(self, key: str):
108+
if not isinstance(key, str):
109+
raise RuntimeError("Key must be of type string")
110+
if key not in self.keys():
111+
raise KeyError(f"KeyError: {key}")
112+
115113
def keys(self):
116114
with self.doc.transaction() as txn:
117115
return iter(self.integrated.keys(txn._txn))

python/pycrdt/text.py

+3-12
Original file line numberDiff line numberDiff line change
@@ -53,19 +53,13 @@ def to_py(self) -> str | None:
5353

5454
def __iadd__(self, value: str) -> Text:
5555
with self.doc.transaction() as txn:
56-
if isinstance(txn, ReadTransaction):
57-
raise RuntimeError(
58-
"Read-only transaction cannot be used to modify document structure"
59-
)
56+
self._forbid_read_transaction(txn)
6057
self.integrated.insert(txn._txn, len(self), value)
6158
return self
6259

6360
def __delitem__(self, key: int | slice) -> None:
6461
with self.doc.transaction() as txn:
65-
if isinstance(txn, ReadTransaction):
66-
raise RuntimeError(
67-
"Read-only transaction cannot be used to modify document structure"
68-
)
62+
self._forbid_read_transaction(txn)
6963
if isinstance(key, int):
7064
self.integrated.remove_range(txn._txn, key, 1)
7165
elif isinstance(key, slice):
@@ -89,10 +83,7 @@ def __delitem__(self, key: int | slice) -> None:
8983

9084
def __setitem__(self, key: int | slice, value: str) -> None:
9185
with self.doc.transaction() as txn:
92-
if isinstance(txn, ReadTransaction):
93-
raise RuntimeError(
94-
"Read-only transaction cannot be used to modify document structure"
95-
)
86+
self._forbid_read_transaction(txn)
9687
if isinstance(key, int):
9788
value_len = len(value)
9889
if value_len != 1:

0 commit comments

Comments
 (0)