Skip to content

Commit 46a7695

Browse files
Update yrs v0.18.0 (#83)
* Update yrs v0.18.0 * Add Subscription class * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix typing * Fix linting, replace black with ruff-format * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 2381f03 commit 46a7695

22 files changed

+146
-207
lines changed

.pre-commit-config.yaml

+2-6
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
repos:
2-
- repo: https://github.com/psf/black
3-
rev: 24.2.0
4-
hooks:
5-
- id: black
6-
72
- repo: https://github.com/astral-sh/ruff-pre-commit
83
rev: v0.3.2
94
hooks:
105
- id: ruff
11-
args: ["--fix"]
6+
args: [--fix, --show-fixes]
7+
- id: ruff-format

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ name = "pycrdt"
88
crate-type = ["cdylib"]
99

1010
[dependencies]
11-
yrs = "0.17.4"
11+
yrs = "0.18.0"
1212

1313
[dependencies.pyo3]
1414
version = "0.20.2"

README.md

-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
[![Build Status](https://github.com/jupyter-server/pycrdt/actions/workflows/test.yml/badge.svg?query=branch%3Amain++)](https://github.com/jupyter-server/pycrdt/actions/workflows/test.yml/badge.svg?query=branch%3Amain++)
22
[![Code Coverage](https://img.shields.io/badge/coverage-100%25-green)](https://img.shields.io/badge/coverage-100%25-green)
3-
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
43

54
# pycrdt
65

pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ module-name = "pycrdt._pycrdt"
4949

5050
[tool.ruff]
5151
line-length = 100
52-
select = ["F", "E", "W", "I001"]
52+
lint.select = ["F", "E", "W", "I001"]
5353

5454
[tool.coverage.run]
5555
source = ["python", "tests"]

python/pycrdt/_array.py

+1-30
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
from __future__ import annotations
22

3-
from functools import partial
4-
from typing import TYPE_CHECKING, Any, Callable
3+
from typing import TYPE_CHECKING, Any
54

65
from ._base import BaseDoc, BaseEvent, BaseType, base_types, event_types
76
from ._pycrdt import Array as _Array
@@ -183,34 +182,6 @@ def to_py(self) -> list | None:
183182
py[idx] = val.to_py()
184183
return py
185184

186-
def observe(self, callback: Callable[[Any], None]) -> str:
187-
_callback = partial(observe_callback, callback, self.doc)
188-
return f"o_{self.integrated.observe(_callback)}"
189-
190-
def observe_deep(self, callback: Callable[[Any], None]) -> str:
191-
_callback = partial(observe_deep_callback, callback, self.doc)
192-
return f"od{self.integrated.observe_deep(_callback)}"
193-
194-
def unobserve(self, subscription_id: str) -> None:
195-
sid = int(subscription_id[2:])
196-
if subscription_id.startswith("o_"):
197-
self.integrated.unobserve(sid)
198-
else:
199-
self.integrated.unobserve_deep(sid)
200-
201-
202-
def observe_callback(callback: Callable[[Any], None], doc: Doc, event: Any):
203-
_event = event_types[type(event)](event, doc)
204-
with doc._read_transaction(event.transaction):
205-
callback(_event)
206-
207-
208-
def observe_deep_callback(callback: Callable[[Any], None], doc: Doc, events: list[Any]):
209-
for idx, event in enumerate(events):
210-
events[idx] = event_types[type(event)](event, doc)
211-
with doc._read_transaction(event.transaction):
212-
callback(events)
213-
214185

215186
class ArrayEvent(BaseEvent):
216187
__slots__ = "target", "delta", "path"

python/pycrdt/_base.py

+29-7
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
from __future__ import annotations
22

33
from abc import ABC, abstractmethod
4-
from typing import TYPE_CHECKING, Any, Type, cast
4+
from functools import partial
5+
from typing import TYPE_CHECKING, Any, Callable, Type, cast
56

67
from ._pycrdt import Doc as _Doc
8+
from ._pycrdt import Subscription
79
from ._pycrdt import Transaction as _Transaction
810
from ._transaction import ReadTransaction, Transaction
911

@@ -73,9 +75,7 @@ def _init(self, value: Any | None) -> None: ...
7375

7476
def _forbid_read_transaction(self, txn: Transaction):
7577
if isinstance(txn, ReadTransaction):
76-
raise RuntimeError(
77-
"Read-only transaction cannot be used to modify document structure"
78-
)
78+
raise RuntimeError("Read-only transaction cannot be used to modify document structure")
7979

8080
def _integrate(self, doc: Doc, integrated: Any) -> Any:
8181
prelim = self._prelim
@@ -84,9 +84,7 @@ def _integrate(self, doc: Doc, integrated: Any) -> Any:
8484
self._integrated = integrated
8585
return prelim
8686

87-
def _do_and_integrate(
88-
self, action: str, value: BaseType, txn: _Transaction, *args
89-
) -> None:
87+
def _do_and_integrate(self, action: str, value: BaseType, txn: _Transaction, *args) -> None:
9088
method = getattr(self._integrated, f"{action}_{value.type_name}_prelim")
9189
integrated = method(txn, *args)
9290
assert self._doc is not None
@@ -132,6 +130,30 @@ def prelim(self) -> Any:
132130
def type_name(self) -> str:
133131
return self._type_name
134132

133+
def observe(self, callback: Callable[[Any], None]) -> Subscription:
134+
_callback = partial(observe_callback, callback, self.doc)
135+
return self.integrated.observe(_callback)
136+
137+
def observe_deep(self, callback: Callable[[Any], None]) -> Subscription:
138+
_callback = partial(observe_deep_callback, callback, self.doc)
139+
return self.integrated.observe_deep(_callback)
140+
141+
def unobserve(self, subscription: Subscription) -> None:
142+
subscription.drop()
143+
144+
145+
def observe_callback(callback: Callable[[Any], None], doc: Doc, event: Any):
146+
_event = event_types[type(event)](event, doc)
147+
with doc._read_transaction(event.transaction):
148+
callback(_event)
149+
150+
151+
def observe_deep_callback(callback: Callable[[Any], None], doc: Doc, events: list[Any]):
152+
for idx, event in enumerate(events):
153+
events[idx] = event_types[type(event)](event, doc)
154+
with doc._read_transaction(event.transaction):
155+
callback(events)
156+
135157

136158
class BaseEvent:
137159
__slots__ = ()

python/pycrdt/_doc.py

+1-3
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,7 @@ def _roots(self) -> dict[str, BaseType]:
9797
key: (
9898
None
9999
if val is None
100-
else cast(Type[BaseType], base_types[type(val)])(
101-
_integrated=val, _doc=self
102-
)
100+
else cast(Type[BaseType], base_types[type(val)])(_integrated=val, _doc=self)
103101
)
104102
for key, val in self._doc.roots(txn._txn).items()
105103
}

python/pycrdt/_map.py

+1-30
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
from __future__ import annotations
22

3-
from functools import partial
4-
from typing import TYPE_CHECKING, Any, Callable
3+
from typing import TYPE_CHECKING, Any
54

65
from ._base import BaseDoc, BaseEvent, BaseType, base_types, event_types
76
from ._pycrdt import Map as _Map
@@ -140,34 +139,6 @@ def clear(self) -> None:
140139
def update(self, value: dict[str, Any]) -> None:
141140
self._init(value)
142141

143-
def observe(self, callback: Callable[[Any], None]) -> str:
144-
_callback = partial(observe_callback, callback, self.doc)
145-
return f"o_{self.integrated.observe(_callback)}"
146-
147-
def observe_deep(self, callback: Callable[[Any], None]) -> str:
148-
_callback = partial(observe_deep_callback, callback, self.doc)
149-
return f"od{self.integrated.observe_deep(_callback)}"
150-
151-
def unobserve(self, subscription_id: str) -> None:
152-
sid = int(subscription_id[2:])
153-
if subscription_id.startswith("o_"):
154-
self.integrated.unobserve(sid)
155-
else:
156-
self.integrated.unobserve_deep(sid)
157-
158-
159-
def observe_callback(callback: Callable[[Any], None], doc: Doc, event: Any):
160-
_event = event_types[type(event)](event, doc)
161-
with doc._read_transaction(event.transaction):
162-
callback(_event)
163-
164-
165-
def observe_deep_callback(callback: Callable[[Any], None], doc: Doc, events: list[Any]):
166-
for idx, event in enumerate(events):
167-
events[idx] = event_types[type(event)](event, doc)
168-
with doc._read_transaction(event.transaction):
169-
callback(events)
170-
171142

172143
class MapEvent(BaseEvent):
173144
__slots__ = "target", "keys", "path"

python/pycrdt/_pycrdt.pyi

+22-16
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,12 @@ class Doc:
4545
"""Subscribes a callback to be called with the shared document subdoc change event.
4646
Returns a subscription ID that can be used to unsubscribe."""
4747

48+
class Subscription:
49+
"""Observer subscription"""
50+
51+
def drop(self) -> None:
52+
"""Drop the subscription, effectively unobserving."""
53+
4854
class Transaction:
4955
"""Document transaction"""
5056

@@ -94,13 +100,13 @@ class Text:
94100
def get_string(self, txn: Transaction) -> str:
95101
"""Returns a text representation of the current shared text."""
96102

97-
def observe(self, callback: Callable[[TextEvent], None]) -> int:
103+
def observe(self, callback: Callable[[TextEvent], None]) -> Subscription:
98104
"""Subscribes a callback to be called with the shared text change event.
99-
Returns a subscription ID that can be used to unsubscribe."""
105+
Returns a subscription that can be used to unsubscribe."""
100106

101-
def unobserve(self, subscription_id: int) -> None:
107+
def unobserve(self, subscription: Subscription) -> None:
102108
"""Unsubscribes previously subscribed event callback identified by given
103-
`subscription_id`."""
109+
`subscription`."""
104110

105111
class Array:
106112
"""Shared array."""
@@ -123,18 +129,18 @@ class Array:
123129
def to_json(self, txn: Transaction) -> str:
124130
"""Returns a JSON representation of the current array."""
125131

126-
def observe(self, callback: Callable[[TextEvent], None]) -> int:
132+
def observe(self, callback: Callable[[TextEvent], None]) -> Subscription:
127133
"""Subscribes a callback to be called with the array change event.
128-
Returns a subscription ID that can be used to unsubscribe."""
134+
Returns a subscription that can be used to unsubscribe."""
129135

130-
def observe_deep(self, callback: Callable[[TextEvent], None]) -> int:
136+
def observe_deep(self, callback: Callable[[TextEvent], None]) -> Subscription:
131137
"""Subscribes a callback to be called with the array change event
132138
and its nested elements.
133-
Returns a subscription ID that can be used to unsubscribe."""
139+
Returns a subscription that can be used to unsubscribe."""
134140

135-
def unobserve(self, subscription_id: int) -> None:
141+
def unobserve(self, subscription: Subscription) -> None:
136142
"""Unsubscribes previously subscribed event callback identified by given
137-
`subscription_id`."""
143+
`subscription`."""
138144

139145
class Map:
140146
"""Shared map."""
@@ -154,15 +160,15 @@ class Map:
154160
def to_json(self, txn: Transaction) -> str:
155161
"""Returns a JSON representation of the current map."""
156162

157-
def observe(self, callback: Callable[[TextEvent], None]) -> int:
163+
def observe(self, callback: Callable[[TextEvent], None]) -> Subscription:
158164
"""Subscribes a callback to be called with the map change event.
159-
Returns a subscription ID that can be used to unsubscribe."""
165+
Returns a subscription that can be used to unsubscribe."""
160166

161-
def observe_deep(self, callback: Callable[[TextEvent], None]) -> int:
167+
def observe_deep(self, callback: Callable[[TextEvent], None]) -> Subscription:
162168
"""Subscribes a callback to be called with the map change event
163169
and its nested elements.
164-
Returns a subscription ID that can be used to unsubscribe."""
170+
Returns a subscription that can be used to unsubscribe."""
165171

166-
def unobserve(self, subscription_id: int) -> None:
172+
def unobserve(self, subscription: Subscription) -> None:
167173
"""Unsubscribes previously subscribed event callback identified by given
168-
`subscription_id`."""
174+
`subscription`."""

python/pycrdt/_text.py

+1-16
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
from __future__ import annotations
22

3-
from functools import partial
4-
from typing import TYPE_CHECKING, Any, Callable
3+
from typing import TYPE_CHECKING
54

65
from ._base import BaseEvent, BaseType, base_types, event_types
76
from ._pycrdt import Text as _Text
@@ -124,20 +123,6 @@ def insert(self, index: int, value: str) -> None:
124123
"""Insert 'value' at character position 'index'."""
125124
self[index:index] = value
126125

127-
def observe(self, callback: Callable[[Any], None]) -> str:
128-
_callback = partial(observe_callback, callback, self.doc)
129-
return f"o_{self.integrated.observe(_callback)}"
130-
131-
def unobserve(self, subscription_id: str) -> None:
132-
sid = int(subscription_id[2:])
133-
self.integrated.unobserve(sid)
134-
135-
136-
def observe_callback(callback: Callable[[Any], None], doc: Doc, event: Any):
137-
_event = event_types[type(event)](event, doc)
138-
with doc._read_transaction(event.transaction):
139-
callback(_event)
140-
141126

142127
class TextEvent(BaseEvent):
143128
__slots__ = "target", "delta", "path"

src/array.rs

+11-20
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use yrs::types::text::TextPrelim;
1515
use yrs::types::array::{ArrayPrelim, ArrayEvent as _ArrayEvent};
1616
use yrs::types::map::MapPrelim;
1717
use crate::transaction::Transaction;
18+
use crate::subscription::Subscription;
1819
use crate::type_conversions::{events_into_py, py_to_any, ToPython};
1920
use crate::text::Text;
2021
use crate::map::Map;
@@ -123,42 +124,32 @@ impl Array {
123124
Python::with_gil(|py| PyString::new(py, s.as_str()).into())
124125
}
125126

126-
pub fn observe(&mut self, f: PyObject) -> PyResult<u32> {
127-
let id: u32 = self.array
127+
pub fn observe(&mut self, py: Python<'_>, f: PyObject) -> PyResult<Py<Subscription>> {
128+
let sub = self.array
128129
.observe(move |txn, e| {
129130
Python::with_gil(|py| {
130131
let event = ArrayEvent::new(e, txn);
131132
if let Err(err) = f.call1(py, (event,)) {
132133
err.restore(py)
133134
}
134135
})
135-
})
136-
.into();
137-
Ok(id)
136+
});
137+
let s: Py<Subscription> = Py::new(py, Subscription::from(sub))?;
138+
Ok(s)
138139
}
139140

140-
pub fn observe_deep(&mut self, f: PyObject) -> PyResult<u32> {
141-
let id: u32 = self.array
141+
pub fn observe_deep(&mut self, py: Python<'_>, f: PyObject) -> PyResult<Py<Subscription>> {
142+
let sub = self.array
142143
.observe_deep(move |txn, events| {
143144
Python::with_gil(|py| {
144145
let events = events_into_py(txn, events);
145146
if let Err(err) = f.call1(py, (events,)) {
146147
err.restore(py)
147148
}
148149
})
149-
})
150-
.into();
151-
Ok(id)
152-
}
153-
154-
pub fn unobserve(&mut self, subscription_id: u32) -> PyResult<()> {
155-
self.array.unobserve(subscription_id);
156-
Ok(())
157-
}
158-
159-
pub fn unobserve_deep(&mut self, subscription_id: u32) -> PyResult<()> {
160-
self.array.unobserve_deep(subscription_id);
161-
Ok(())
150+
});
151+
let s: Py<Subscription> = Py::new(py, Subscription::from(sub))?;
152+
Ok(s)
162153
}
163154
}
164155

0 commit comments

Comments
 (0)