Skip to content
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

Use anyio instead of asyncio #345

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
1 change: 0 additions & 1 deletion .devcontainer/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,3 @@
fps[unicorn] >=0.0.10
mypy
pytest
pytest-asyncio
6 changes: 3 additions & 3 deletions jupyverse_api/jupyverse_api/contents/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import asyncio
from abc import ABC, abstractmethod
from pathlib import Path
from typing import Dict, List, Optional, Union

from anyio import Event
from fastapi import APIRouter, Depends, Request, Response
from jupyverse_api import Router

Expand All @@ -12,8 +12,8 @@


class FileIdManager(ABC):
stop_watching_files: asyncio.Event
stopped_watching_files: asyncio.Event
stop_watching_files: Event
stopped_watching_files: Event

@abstractmethod
async def get_path(self, file_id: str) -> str:
Expand Down
1 change: 1 addition & 0 deletions jupyverse_api/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ dependencies = [
"rich-click >=1.6.1,<2",
"asphalt >=4.11.0,<5",
"asphalt-web[fastapi] >=1.1.0,<2",
"anyio>=3.6.2,<5",
]
dynamic = ["version"]

Expand Down
3 changes: 2 additions & 1 deletion plugins/auth_jupyterhub/fps_auth_jupyterhub/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from typing_extensions import Annotated

import httpx
from anyio import Lock
from fastapi import APIRouter, Cookie, Depends, HTTPException, Request, WebSocket, status
from fastapi.responses import RedirectResponse
from jupyterhub.services.auth import HubOAuth
Expand All @@ -31,7 +32,7 @@ class AuthJupyterHub(Auth, Router):
def __init__(self) -> None:
super().__init__(app)
self.hub_auth = HubOAuth()
self.db_lock = asyncio.Lock()
self.db_lock = Lock()
self.activity_url = os.environ.get("JUPYTERHUB_ACTIVITY_URL")
self.server_name = os.environ.get("JUPYTERHUB_SERVER_NAME")
self.background_tasks = set()
Expand Down
1 change: 1 addition & 0 deletions plugins/auth_jupyterhub/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ dependencies = [
"httpx >=0.24.1,<1",
"jupyterhub >=4.0.1,<5",
"jupyverse-api >=0.1.2,<1",
"anyio>=3.6.2,<5",
]

[[project.authors]]
Expand Down
16 changes: 8 additions & 8 deletions plugins/contents/fps_contents/fileid.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from uuid import uuid4

import aiosqlite
from anyio import Path
from anyio import Event, Lock, Path
from jupyverse_api import Singleton
from watchfiles import Change, awatch

Expand All @@ -14,7 +14,7 @@
class Watcher:
def __init__(self, path: str) -> None:
self.path = path
self._event = asyncio.Event()
self._event = Event()

def __aiter__(self):
return self
Expand All @@ -31,18 +31,18 @@ def notify(self, change):

class FileIdManager(metaclass=Singleton):
db_path: str
initialized: asyncio.Event
initialized: Event
watchers: Dict[str, List[Watcher]]
lock: asyncio.Lock
lock: Lock

def __init__(self, db_path: str = ".fileid.db"):
self.db_path = db_path
self.initialized = asyncio.Event()
self.initialized = Event()
self.watchers = {}
self.watch_files_task = asyncio.create_task(self.watch_files())
self.stop_watching_files = asyncio.Event()
self.stopped_watching_files = asyncio.Event()
self.lock = asyncio.Lock()
self.stop_watching_files = Event()
self.stopped_watching_files = Event()
self.lock = Lock()

async def get_id(self, path: str) -> Optional[str]:
await self.initialized.wait()
Expand Down
3 changes: 2 additions & 1 deletion plugins/terminals/fps_terminals/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import struct
import termios

from anyio import Event
from fastapi import WebSocketDisconnect
from jupyverse_api.terminals import TerminalServer

Expand All @@ -29,7 +30,7 @@ def __init__(self):
async def serve(self, websocket, permissions):
self.websocket = websocket
self.websockets.append(websocket)
self.event = asyncio.Event()
self.event = Event()
self.loop = asyncio.get_event_loop()

task = asyncio.create_task(self.send_data())
Expand Down
3 changes: 2 additions & 1 deletion plugins/terminals/fps_terminals/win_server.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import asyncio
import os

from anyio import sleep
from jupyverse_api.terminals import TerminalServer
from winpty import PTY # type: ignore

Expand Down Expand Up @@ -36,7 +37,7 @@ async def send_data(self):
await self.websocket.send_json(["disconnect", 1])
return
if not data:
await asyncio.sleep(0.1)
await sleep(0.1)
else:
for websocket in self.websockets:
await websocket.send_json(["stdout", data])
Expand Down
1 change: 1 addition & 0 deletions plugins/terminals/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ dependencies = [
"websockets",
"pywinpty;platform_system=='Windows'",
"jupyverse-api >=0.1.2,<1",
"anyio>=3.6.2,<5",
]
dynamic = ["version"]
[[project.authors]]
Expand Down
1 change: 0 additions & 1 deletion plugins/webdav/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ Homepage = "https://jupyter.org"
test = [
"easywebdav",
"pytest",
"pytest-asyncio",
]

[tool.check-manifest]
Expand Down
2 changes: 1 addition & 1 deletion plugins/webdav/tests/test_webdav.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def configure(components, config):
return _components


@pytest.mark.asyncio
@pytest.mark.anyio
@pytest.mark.skipif(sys.version_info < (3, 10), reason="Requires Python >=3.10")
async def test_webdav(unused_tcp_port):
components = configure(
Expand Down
9 changes: 5 additions & 4 deletions plugins/yjs/fps_yjs/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from typing import Dict
from uuid import uuid4

from anyio import Lock, sleep
from fastapi import (
HTTPException,
Request,
Expand Down Expand Up @@ -138,7 +139,7 @@ class RoomManager:
cleaners: Dict[YRoom, asyncio.Task]
last_modified: Dict[str, datetime]
websocket_server: JupyterWebsocketServer
lock: asyncio.Lock
lock: Lock

def __init__(self, contents: Contents):
self.contents = contents
Expand All @@ -149,7 +150,7 @@ def __init__(self, contents: Contents):
self.last_modified = {} # a dictionary of file_id:last_modification_date
self.websocket_server = JupyterWebsocketServer(rooms_ready=False, auto_clean_rooms=False)
self.websocket_server_task = asyncio.create_task(self.websocket_server.start())
self.lock = asyncio.Lock()
self.lock = Lock()

def stop(self):
for watcher in self.watchers.values():
Expand Down Expand Up @@ -305,7 +306,7 @@ async def maybe_save_document(
self, file_id: str, file_type: str, file_format: str, document: YBaseDoc
) -> None:
# save after 1 second of inactivity to prevent too frequent saving
await asyncio.sleep(1) # FIXME: pass in config
await sleep(1) # FIXME: pass in config
# if the room cannot be found, don't save
try:
file_path = await self.get_file_path(file_id, document)
Expand Down Expand Up @@ -342,7 +343,7 @@ async def maybe_save_document(
async def maybe_clean_room(self, room, ws_path: str) -> None:
file_id = ws_path.split(":", 2)[2]
# keep the document for a while in case someone reconnects
await asyncio.sleep(60) # FIXME: pass in config
await sleep(60) # FIXME: pass in config
document = self.documents[ws_path]
document.unobserve()
del self.documents[ws_path]
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ noauth = ["fps-noauth >=0.1.2,<1"]
test = [
"mypy",
"types-setuptools",
"anyio>=3.6.2,<5",
"pytest",
"pytest-asyncio",
"pytest-rerunfailures",
"pytest-timeout",
"pytest-env",
Expand Down
2 changes: 1 addition & 1 deletion tests/test_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from utils import configure


@pytest.mark.asyncio
@pytest.mark.anyio
@pytest.mark.parametrize(
"mount_path",
(
Expand Down
12 changes: 6 additions & 6 deletions tests/test_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
}


@pytest.mark.asyncio
@pytest.mark.anyio
async def test_kernel_channels_unauthenticated(unused_tcp_port):
async with Context() as ctx:
await JupyverseComponent(
Expand All @@ -35,7 +35,7 @@ async def test_kernel_channels_unauthenticated(unused_tcp_port):
pass


@pytest.mark.asyncio
@pytest.mark.anyio
async def test_kernel_channels_authenticated(unused_tcp_port):
async with Context() as ctx, AsyncClient() as http:
await JupyverseComponent(
Expand All @@ -51,7 +51,7 @@ async def test_kernel_channels_authenticated(unused_tcp_port):
pass


@pytest.mark.asyncio
@pytest.mark.anyio
@pytest.mark.parametrize("auth_mode", ("noauth", "token", "user"))
async def test_root_auth(auth_mode, unused_tcp_port):
components = configure(COMPONENTS, {"auth": {"mode": auth_mode}})
Expand All @@ -71,7 +71,7 @@ async def test_root_auth(auth_mode, unused_tcp_port):
assert response.headers["content-type"] == "application/json"


@pytest.mark.asyncio
@pytest.mark.anyio
@pytest.mark.parametrize("auth_mode", ("noauth",))
async def test_no_auth(auth_mode, unused_tcp_port):
components = configure(COMPONENTS, {"auth": {"mode": auth_mode}})
Expand All @@ -85,7 +85,7 @@ async def test_no_auth(auth_mode, unused_tcp_port):
assert response.status_code == 200


@pytest.mark.asyncio
@pytest.mark.anyio
@pytest.mark.parametrize("auth_mode", ("token",))
async def test_token_auth(auth_mode, unused_tcp_port):
components = configure(COMPONENTS, {"auth": {"mode": auth_mode}})
Expand All @@ -105,7 +105,7 @@ async def test_token_auth(auth_mode, unused_tcp_port):
assert response.status_code == 302


@pytest.mark.asyncio
@pytest.mark.anyio
@pytest.mark.parametrize("auth_mode", ("user",))
@pytest.mark.parametrize(
"permissions",
Expand Down
2 changes: 1 addition & 1 deletion tests/test_contents.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
}


@pytest.mark.asyncio
@pytest.mark.anyio
@pytest.mark.parametrize("auth_mode", ("noauth",))
async def test_tree(auth_mode, tmp_path, unused_tcp_port):
prev_dir = os.getcwd()
Expand Down
2 changes: 1 addition & 1 deletion tests/test_kernels.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
}


@pytest.mark.asyncio
@pytest.mark.anyio
@pytest.mark.parametrize("auth_mode", ("noauth",))
async def test_kernel_messages(auth_mode, capfd, unused_tcp_port):
kernel_id = "kernel_id_0"
Expand Down
8 changes: 4 additions & 4 deletions tests/test_server.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import asyncio
import json
from pathlib import Path

import pytest
import requests
import y_py as Y
from anyio import sleep
from websockets import connect
from ypy_websocket import WebsocketProvider

Expand Down Expand Up @@ -45,7 +45,7 @@ def test_settings_persistence_get(start_jupyverse):
assert response.status_code == 204


@pytest.mark.asyncio
@pytest.mark.anyio
@pytest.mark.parametrize("auth_mode", ("noauth",))
@pytest.mark.parametrize("clear_users", (False,))
async def test_rest_api(start_jupyverse):
Expand Down Expand Up @@ -85,7 +85,7 @@ async def test_rest_api(start_jupyverse):
) as websocket, WebsocketProvider(ydoc, websocket):
# connect to the shared notebook document
# wait for file to be loaded and Y model to be created in server and client
await asyncio.sleep(0.5)
await sleep(0.5)
# execute notebook
for cell_idx in range(3):
response = requests.post(
Expand All @@ -98,7 +98,7 @@ async def test_rest_api(start_jupyverse):
),
)
# wait for Y model to be updated
await asyncio.sleep(0.5)
await sleep(0.5)
# retrieve cells
cells = json.loads(ydoc.get_array("cells").to_json())
assert cells[0]["outputs"] == [
Expand Down
2 changes: 1 addition & 1 deletion tests/test_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
}


@pytest.mark.asyncio
@pytest.mark.anyio
@pytest.mark.parametrize("auth_mode", ("noauth",))
async def test_settings(auth_mode, unused_tcp_port):
components = configure(COMPONENTS, {"auth": {"mode": auth_mode}})
Expand Down
Loading