Skip to content

Commit dd1fb84

Browse files
Add more tests, require Python 3.10+ (#359)
* Add more tests * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Remove coverage pinning * Use python 3.10 in check-release * In readthedocs.yml too * Fix pip install in readthedocs --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 182354f commit dd1fb84

File tree

18 files changed

+107
-75
lines changed

18 files changed

+107
-75
lines changed

.github/workflows/check-release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515
fail-fast: false
1616
matrix:
1717
group: [check_release, link_check]
18-
python-version: ["3.9"]
18+
python-version: ["3.10"]
1919
node-version: ["14.x"]
2020
steps:
2121
- name: Checkout

.github/workflows/test.yml

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,14 @@ jobs:
2828
echo " pre-commit run --all-files --hook-stage=manual"
2929
3030
test:
31-
name: Run tests on ${{ matrix.os }}
32-
runs-on: ${{ matrix.os }}
31+
name: Run tests on ${{ matrix.os }} py${{ matrix.python-version }}
32+
runs-on: ${{ matrix.os }}-latest
3333
timeout-minutes: 10
3434

3535
strategy:
3636
matrix:
37-
os: [ubuntu-latest, windows-latest, macos-latest]
37+
os: [ubuntu, windows, macos]
38+
python-version: [ '3.10', '3.11', '3.12', '3.13', '3.14' ]
3839
defaults:
3940
run:
4041
shell: bash -l {0}
@@ -48,7 +49,7 @@ jobs:
4849
- name: Install dependencies
4950
run: |
5051
micromamba install pip nodejs=18
51-
pip install ".[test]"
52+
pip install . --group test
5253
- name: Build JavaScript assets
5354
working-directory: javascript
5455
run: |
@@ -76,5 +77,11 @@ jobs:
7677
yarn build:test
7778
yarn test:cov
7879
- name: Run Python tests
80+
if: ${{ !((matrix.python-version == '3.14') && (matrix.os == 'ubuntu')) }}
7981
run: |
8082
python -m pytest -v
83+
- name: Run Python code coverage
84+
if: ${{ (matrix.python-version == '3.14') && (matrix.os == 'ubuntu') }}
85+
run: |
86+
coverage run -m pytest -v
87+
coverage report --show-missing

README.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,6 @@ Which is just a shortcut to:
3030

3131
```py
3232
from importlib.metadata import entry_points
33-
# for Python < 3.10, install importlib_metadata and do:
34-
# from importlib_metadata import entry_points
3533

3634
ydocs = {ep.name: ep.load() for ep in entry_points(group="jupyter_ydoc")}
3735
```

jupyter_ydoc/__init__.py

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,12 @@
11
# Copyright (c) Jupyter Development Team.
22
# Distributed under the terms of the Modified BSD License.
33

4-
import sys
4+
from importlib.metadata import entry_points
55

66
from ._version import __version__ as __version__
77
from .yblob import YBlob as YBlob
88
from .yfile import YFile as YFile
99
from .ynotebook import YNotebook as YNotebook
1010
from .yunicode import YUnicode as YUnicode
1111

12-
# See compatibility note on `group` keyword in
13-
# https://docs.python.org/3/library/importlib.metadata.html#entry-points
14-
if sys.version_info < (3, 10):
15-
from importlib_metadata import entry_points
16-
else:
17-
from importlib.metadata import entry_points
18-
1912
ydocs = {ep.name: ep.load() for ep in entry_points(group="jupyter_ydoc")}

jupyter_ydoc/utils.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
11
# Copyright (c) Jupyter Development Team.
22
# Distributed under the terms of the Modified BSD License.
33

4-
from typing import Dict, List, Type, Union
54

6-
INT = Type[int]
7-
FLOAT = Type[float]
5+
INT = type[int]
6+
FLOAT = type[float]
87

98

10-
def cast_all(
11-
o: Union[List, Dict], from_type: Union[INT, FLOAT], to_type: Union[FLOAT, INT]
12-
) -> Union[List, Dict]:
9+
def cast_all(o: list | dict, from_type: INT | FLOAT, to_type: FLOAT | INT) -> list | dict:
1310
if isinstance(o, list):
1411
for i, v in enumerate(o):
1512
if type(v) is from_type:

jupyter_ydoc/ybasedoc.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
# Distributed under the terms of the Modified BSD License.
33

44
from abc import ABC, abstractmethod
5-
from typing import Any, Callable, Dict, Optional
5+
from collections.abc import Callable
6+
from typing import Any
67

78
from pycrdt import Awareness, Doc, Map, Subscription, UndoManager
89

@@ -17,10 +18,10 @@ class YBaseDoc(ABC):
1718

1819
_ydoc: Doc
1920
_ystate: Map
20-
_subscriptions: Dict[Any, Subscription]
21+
_subscriptions: dict[Any, Subscription]
2122
_undo_manager: UndoManager
2223

23-
def __init__(self, ydoc: Optional[Doc] = None, awareness: Optional[Awareness] = None):
24+
def __init__(self, ydoc: Doc | None = None, awareness: Awareness | None = None):
2425
"""
2526
Constructs a YBaseDoc.
2627
@@ -100,7 +101,7 @@ def source(self, value: Any):
100101
return self.set(value)
101102

102103
@property
103-
def dirty(self) -> Optional[bool]:
104+
def dirty(self) -> bool | None:
104105
"""
105106
Returns whether the document is dirty.
106107
@@ -120,7 +121,7 @@ def dirty(self, value: bool) -> None:
120121
self._ystate["dirty"] = value
121122

122123
@property
123-
def hash(self) -> Optional[str]:
124+
def hash(self) -> str | None:
124125
"""
125126
Returns the document hash as computed by contents manager.
126127
@@ -140,7 +141,7 @@ def hash(self, value: str) -> None:
140141
self._ystate["hash"] = value
141142

142143
@property
143-
def path(self) -> Optional[str]:
144+
def path(self) -> str | None:
144145
"""
145146
Returns document's path.
146147

jupyter_ydoc/yblob.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
# Copyright (c) Jupyter Development Team.
22
# Distributed under the terms of the Modified BSD License.
33

4+
from collections.abc import Callable
45
from functools import partial
5-
from typing import Any, Callable, Optional
6+
from typing import Any
67

78
from pycrdt import Awareness, Doc, Map
89

@@ -24,7 +25,7 @@ class YBlob(YBaseDoc):
2425
}
2526
"""
2627

27-
def __init__(self, ydoc: Optional[Doc] = None, awareness: Optional[Awareness] = None):
28+
def __init__(self, ydoc: Doc | None = None, awareness: Awareness | None = None):
2829
"""
2930
Constructs a YBlob.
3031
@@ -64,6 +65,9 @@ def set(self, value: bytes) -> None:
6465
:param value: The content of the document.
6566
:type value: bytes
6667
"""
68+
if self.get() == value:
69+
return
70+
6771
self._ysource["bytes"] = value
6872

6973
def observe(self, callback: Callable[[str, Any], None]) -> None:

jupyter_ydoc/ynotebook.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22
# Distributed under the terms of the Modified BSD License.
33

44
import copy
5+
from collections.abc import Callable
56
from functools import partial
6-
from typing import Any, Callable, Dict, List, Optional
7+
from typing import Any
78
from uuid import uuid4
89

910
from pycrdt import Array, Awareness, Doc, Map, Text
@@ -47,7 +48,7 @@ class YNotebook(YBaseDoc):
4748
}
4849
"""
4950

50-
def __init__(self, ydoc: Optional[Doc] = None, awareness: Optional[Awareness] = None):
51+
def __init__(self, ydoc: Doc | None = None, awareness: Awareness | None = None):
5152
"""
5253
Constructs a YNotebook.
5354
@@ -92,7 +93,7 @@ def cell_number(self) -> int:
9293
"""
9394
return len(self._ycells)
9495

95-
def get_cell(self, index: int) -> Dict[str, Any]:
96+
def get_cell(self, index: int) -> dict[str, Any]:
9697
"""
9798
Returns a cell.
9899
@@ -104,7 +105,7 @@ def get_cell(self, index: int) -> Dict[str, Any]:
104105
"""
105106
return self._cell_to_py(self._ycells[index])
106107

107-
def _cell_to_py(self, ycell: Map) -> Dict[str, Any]:
108+
def _cell_to_py(self, ycell: Map) -> dict[str, Any]:
108109
meta = self._ymeta.to_py()
109110
cell = ycell.to_py()
110111
cell.pop("execution_state", None)
@@ -120,7 +121,7 @@ def _cell_to_py(self, ycell: Map) -> Dict[str, Any]:
120121
del cell["attachments"]
121122
return cell
122123

123-
def append_cell(self, value: Dict[str, Any]) -> None:
124+
def append_cell(self, value: dict[str, Any]) -> None:
124125
"""
125126
Appends a cell.
126127
@@ -130,7 +131,7 @@ def append_cell(self, value: Dict[str, Any]) -> None:
130131
ycell = self.create_ycell(value)
131132
self._ycells.append(ycell)
132133

133-
def set_cell(self, index: int, value: Dict[str, Any]) -> None:
134+
def set_cell(self, index: int, value: dict[str, Any]) -> None:
134135
"""
135136
Sets a cell into indicated position.
136137
@@ -143,7 +144,7 @@ def set_cell(self, index: int, value: Dict[str, Any]) -> None:
143144
ycell = self.create_ycell(value)
144145
self.set_ycell(index, ycell)
145146

146-
def create_ycell(self, value: Dict[str, Any]) -> Map:
147+
def create_ycell(self, value: dict[str, Any]) -> Map:
147148
"""
148149
Creates YMap with the content of the cell.
149150
@@ -193,7 +194,7 @@ def set_ycell(self, index: int, ycell: Map) -> None:
193194
"""
194195
self._ycells[index] = ycell
195196

196-
def get(self) -> Dict:
197+
def get(self) -> dict:
197198
"""
198199
Returns the content of the document.
199200
@@ -227,7 +228,7 @@ def get(self) -> Dict:
227228
nbformat_minor=int(meta.get("nbformat_minor", 0)),
228229
)
229230

230-
def set(self, value: Dict) -> None:
231+
def set(self, value: dict) -> None:
231232
"""
232233
Sets the content of the document.
233234
@@ -251,7 +252,7 @@ def set(self, value: Dict) -> None:
251252
old_ycells_by_id = {ycell["id"]: ycell for ycell in self._ycells}
252253

253254
with self._ydoc.transaction():
254-
new_cell_list: List[dict] = []
255+
new_cell_list: list[dict] = []
255256
retained_cells = set()
256257

257258
# Determine cells to be retained

jupyter_ydoc/yunicode.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
# Copyright (c) Jupyter Development Team.
22
# Distributed under the terms of the Modified BSD License.
33

4+
from collections.abc import Callable
45
from functools import partial
5-
from typing import Any, Callable, Optional
6+
from typing import Any
67

78
from pycrdt import Awareness, Doc, Text
89

@@ -23,7 +24,7 @@ class YUnicode(YBaseDoc):
2324
}
2425
"""
2526

26-
def __init__(self, ydoc: Optional[Doc] = None, awareness: Optional[Awareness] = None):
27+
def __init__(self, ydoc: Doc | None = None, awareness: Awareness | None = None):
2728
"""
2829
Constructs a YUnicode.
2930
@@ -67,6 +68,7 @@ def set(self, value: str) -> None:
6768
# no-op if the values are already the same,
6869
# to avoid side-effects such as cursor jumping to the top
6970
return
71+
7072
with self._ydoc.transaction():
7173
# clear document
7274
self._ysource.clear()

pyproject.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,17 @@ build-backend = "hatchling.build"
99
name = "jupyter-ydoc"
1010
dynamic = ["version"]
1111
description = "Document structures for collaborative editing using Ypy"
12-
requires-python = ">=3.8"
12+
requires-python = ">=3.10"
1313
keywords = ["jupyter", "pycrdt", "yjs"]
1414
dependencies = [
15-
"importlib_metadata >=3.6; python_version<'3.10'",
1615
"pycrdt >=0.10.1,<0.13.0",
1716
]
1817

1918
[[project.authors]]
2019
name = "Jupyter Development Team"
2120
2221

23-
[project.optional-dependencies]
22+
[dependency-groups]
2423
dev = [
2524
"click",
2625
"jupyter_releaser",
@@ -33,6 +32,7 @@ test = [
3332
"httpx-ws >=0.5.2",
3433
"hypercorn >=0.16.0",
3534
"pycrdt-websocket >=0.16.0,<0.17.0",
35+
"coverage",
3636
]
3737
docs = [
3838
"sphinx",

0 commit comments

Comments
 (0)