Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/check-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
fail-fast: false
matrix:
group: [check_release, link_check]
python-version: ["3.9"]
python-version: ["3.10"]
node-version: ["14.x"]
steps:
- name: Checkout
Expand Down
15 changes: 11 additions & 4 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,14 @@ jobs:
echo " pre-commit run --all-files --hook-stage=manual"

test:
name: Run tests on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
name: Run tests on ${{ matrix.os }} py${{ matrix.python-version }}
runs-on: ${{ matrix.os }}-latest
timeout-minutes: 10

strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
os: [ubuntu, windows, macos]
python-version: [ '3.10', '3.11', '3.12', '3.13', '3.14' ]
defaults:
run:
shell: bash -l {0}
Expand All @@ -48,7 +49,7 @@ jobs:
- name: Install dependencies
run: |
micromamba install pip nodejs=18
pip install ".[test]"
pip install . --group test
- name: Build JavaScript assets
working-directory: javascript
run: |
Expand Down Expand Up @@ -76,5 +77,11 @@ jobs:
yarn build:test
yarn test:cov
- name: Run Python tests
if: ${{ !((matrix.python-version == '3.14') && (matrix.os == 'ubuntu')) }}
run: |
python -m pytest -v
- name: Run Python code coverage
if: ${{ (matrix.python-version == '3.14') && (matrix.os == 'ubuntu') }}
run: |
coverage run -m pytest -v
coverage report --show-missing
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@ Which is just a shortcut to:

```py
from importlib.metadata import entry_points
# for Python < 3.10, install importlib_metadata and do:
# from importlib_metadata import entry_points

ydocs = {ep.name: ep.load() for ep in entry_points(group="jupyter_ydoc")}
```
Expand Down
9 changes: 1 addition & 8 deletions jupyter_ydoc/__init__.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,12 @@
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.

import sys
from importlib.metadata import entry_points

from ._version import __version__ as __version__
from .yblob import YBlob as YBlob
from .yfile import YFile as YFile
from .ynotebook import YNotebook as YNotebook
from .yunicode import YUnicode as YUnicode

# See compatibility note on `group` keyword in
# https://docs.python.org/3/library/importlib.metadata.html#entry-points
if sys.version_info < (3, 10):
from importlib_metadata import entry_points
else:
from importlib.metadata import entry_points

ydocs = {ep.name: ep.load() for ep in entry_points(group="jupyter_ydoc")}
9 changes: 3 additions & 6 deletions jupyter_ydoc/utils.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.

from typing import Dict, List, Type, Union

INT = Type[int]
FLOAT = Type[float]
INT = type[int]
FLOAT = type[float]


def cast_all(
o: Union[List, Dict], from_type: Union[INT, FLOAT], to_type: Union[FLOAT, INT]
) -> Union[List, Dict]:
def cast_all(o: list | dict, from_type: INT | FLOAT, to_type: FLOAT | INT) -> list | dict:
if isinstance(o, list):
for i, v in enumerate(o):
if type(v) is from_type:
Expand Down
13 changes: 7 additions & 6 deletions jupyter_ydoc/ybasedoc.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
# Distributed under the terms of the Modified BSD License.

from abc import ABC, abstractmethod
from typing import Any, Callable, Dict, Optional
from collections.abc import Callable
from typing import Any

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

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

_ydoc: Doc
_ystate: Map
_subscriptions: Dict[Any, Subscription]
_subscriptions: dict[Any, Subscription]
_undo_manager: UndoManager

def __init__(self, ydoc: Optional[Doc] = None, awareness: Optional[Awareness] = None):
def __init__(self, ydoc: Doc | None = None, awareness: Awareness | None = None):
"""
Constructs a YBaseDoc.

Expand Down Expand Up @@ -100,7 +101,7 @@ def source(self, value: Any):
return self.set(value)

@property
def dirty(self) -> Optional[bool]:
def dirty(self) -> bool | None:
"""
Returns whether the document is dirty.

Expand All @@ -120,7 +121,7 @@ def dirty(self, value: bool) -> None:
self._ystate["dirty"] = value

@property
def hash(self) -> Optional[str]:
def hash(self) -> str | None:
"""
Returns the document hash as computed by contents manager.

Expand All @@ -140,7 +141,7 @@ def hash(self, value: str) -> None:
self._ystate["hash"] = value

@property
def path(self) -> Optional[str]:
def path(self) -> str | None:
"""
Returns document's path.

Expand Down
8 changes: 6 additions & 2 deletions jupyter_ydoc/yblob.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.

from collections.abc import Callable
from functools import partial
from typing import Any, Callable, Optional
from typing import Any

from pycrdt import Awareness, Doc, Map

Expand All @@ -24,7 +25,7 @@ class YBlob(YBaseDoc):
}
"""

def __init__(self, ydoc: Optional[Doc] = None, awareness: Optional[Awareness] = None):
def __init__(self, ydoc: Doc | None = None, awareness: Awareness | None = None):
"""
Constructs a YBlob.

Expand Down Expand Up @@ -64,6 +65,9 @@ def set(self, value: bytes) -> None:
:param value: The content of the document.
:type value: bytes
"""
if self.get() == value:
return

self._ysource["bytes"] = value

def observe(self, callback: Callable[[str, Any], None]) -> None:
Expand Down
21 changes: 11 additions & 10 deletions jupyter_ydoc/ynotebook.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
# Distributed under the terms of the Modified BSD License.

import copy
from collections.abc import Callable
from functools import partial
from typing import Any, Callable, Dict, List, Optional
from typing import Any
from uuid import uuid4

from pycrdt import Array, Awareness, Doc, Map, Text
Expand Down Expand Up @@ -47,7 +48,7 @@ class YNotebook(YBaseDoc):
}
"""

def __init__(self, ydoc: Optional[Doc] = None, awareness: Optional[Awareness] = None):
def __init__(self, ydoc: Doc | None = None, awareness: Awareness | None = None):
"""
Constructs a YNotebook.

Expand Down Expand Up @@ -92,7 +93,7 @@ def cell_number(self) -> int:
"""
return len(self._ycells)

def get_cell(self, index: int) -> Dict[str, Any]:
def get_cell(self, index: int) -> dict[str, Any]:
"""
Returns a cell.

Expand All @@ -104,7 +105,7 @@ def get_cell(self, index: int) -> Dict[str, Any]:
"""
return self._cell_to_py(self._ycells[index])

def _cell_to_py(self, ycell: Map) -> Dict[str, Any]:
def _cell_to_py(self, ycell: Map) -> dict[str, Any]:
meta = self._ymeta.to_py()
cell = ycell.to_py()
cell.pop("execution_state", None)
Expand All @@ -120,7 +121,7 @@ def _cell_to_py(self, ycell: Map) -> Dict[str, Any]:
del cell["attachments"]
return cell

def append_cell(self, value: Dict[str, Any]) -> None:
def append_cell(self, value: dict[str, Any]) -> None:
"""
Appends a cell.

Expand All @@ -130,7 +131,7 @@ def append_cell(self, value: Dict[str, Any]) -> None:
ycell = self.create_ycell(value)
self._ycells.append(ycell)

def set_cell(self, index: int, value: Dict[str, Any]) -> None:
def set_cell(self, index: int, value: dict[str, Any]) -> None:
"""
Sets a cell into indicated position.

Expand All @@ -143,7 +144,7 @@ def set_cell(self, index: int, value: Dict[str, Any]) -> None:
ycell = self.create_ycell(value)
self.set_ycell(index, ycell)

def create_ycell(self, value: Dict[str, Any]) -> Map:
def create_ycell(self, value: dict[str, Any]) -> Map:
"""
Creates YMap with the content of the cell.

Expand Down Expand Up @@ -193,7 +194,7 @@ def set_ycell(self, index: int, ycell: Map) -> None:
"""
self._ycells[index] = ycell

def get(self) -> Dict:
def get(self) -> dict:
"""
Returns the content of the document.

Expand Down Expand Up @@ -227,7 +228,7 @@ def get(self) -> Dict:
nbformat_minor=int(meta.get("nbformat_minor", 0)),
)

def set(self, value: Dict) -> None:
def set(self, value: dict) -> None:
"""
Sets the content of the document.

Expand All @@ -251,7 +252,7 @@ def set(self, value: Dict) -> None:
old_ycells_by_id = {ycell["id"]: ycell for ycell in self._ycells}

with self._ydoc.transaction():
new_cell_list: List[dict] = []
new_cell_list: list[dict] = []
retained_cells = set()

# Determine cells to be retained
Expand Down
6 changes: 4 additions & 2 deletions jupyter_ydoc/yunicode.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.

from collections.abc import Callable
from functools import partial
from typing import Any, Callable, Optional
from typing import Any

from pycrdt import Awareness, Doc, Text

Expand All @@ -23,7 +24,7 @@ class YUnicode(YBaseDoc):
}
"""

def __init__(self, ydoc: Optional[Doc] = None, awareness: Optional[Awareness] = None):
def __init__(self, ydoc: Doc | None = None, awareness: Awareness | None = None):
"""
Constructs a YUnicode.

Expand Down Expand Up @@ -67,6 +68,7 @@ def set(self, value: str) -> None:
# no-op if the values are already the same,
# to avoid side-effects such as cursor jumping to the top
return

with self._ydoc.transaction():
# clear document
self._ysource.clear()
Expand Down
6 changes: 3 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,17 @@ build-backend = "hatchling.build"
name = "jupyter-ydoc"
dynamic = ["version"]
description = "Document structures for collaborative editing using Ypy"
requires-python = ">=3.8"
requires-python = ">=3.10"
keywords = ["jupyter", "pycrdt", "yjs"]
dependencies = [
"importlib_metadata >=3.6; python_version<'3.10'",
"pycrdt >=0.10.1,<0.13.0",
]

[[project.authors]]
name = "Jupyter Development Team"
email = "jupyter@googlegroups.com"

[project.optional-dependencies]
[dependency-groups]
dev = [
"click",
"jupyter_releaser",
Expand All @@ -33,6 +32,7 @@ test = [
"httpx-ws >=0.5.2",
"hypercorn >=0.16.0",
"pycrdt-websocket >=0.16.0,<0.17.0",
"coverage",
]
docs = [
"sphinx",
Expand Down
13 changes: 5 additions & 8 deletions readthedocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,12 @@ version: 2
build:
os: ubuntu-20.04
tools:
python: "3.8"
python: "3.10"
nodejs: "14"
jobs:
install:
- pip install --upgrade pip
- pip install --group 'docs'

sphinx:
configuration: docs/source/conf.py

python:
install:
- method: pip
path: .
extra_requirements:
- docs
2 changes: 1 addition & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,5 +63,5 @@ def yjs_client(request):
p.terminate()
try:
p.wait(timeout=10)
except Exception:
except Exception: # pragma: nocover
p.kill()
Loading
Loading