Skip to content

Commit

Permalink
Merge pull request #277 from davidbrochart/asphalt
Browse files Browse the repository at this point in the history
Drop FPS in favor of Asphalt
  • Loading branch information
davidbrochart authored Mar 24, 2023
2 parents 18f2349 + 9483f60 commit 8476938
Show file tree
Hide file tree
Showing 109 changed files with 3,590 additions and 2,769 deletions.
4 changes: 3 additions & 1 deletion .github/workflows/check-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ jobs:
- name: Install Dependencies
run: |
pip install -e . --no-deps
pip install -e plugins/auth_base
pip install -e jupyverse_api
pip install -e plugins/frontend
pip install -e plugins/jupyterlab
pip install -e plugins/retrolab
Expand All @@ -54,6 +54,8 @@ jobs:
pip install -e plugins/nbconvert
pip install -e plugins/yjs
pip install -e plugins/auth
pip install -e plugins/noauth
pip install -e plugins/auth_fief
pip install -e plugins/login
- name: Check Release
if: ${{ matrix.group == 'check_release' }}
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,15 @@ jobs:
- name: Upgrade pip
run: python3 -m pip install --upgrade pip

- name: Create jupyterlab-auth dev environment
- name: Create jupyterlab dev environment
run: |
pip install hatch
hatch env create dev.jupyterlab-auth
hatch env create dev.jupyterlab
- name: Check types
run: |
hatch run dev.jupyterlab-auth:typecheck
hatch run dev.jupyterlab:typecheck
- name: Run tests
run: |
hatch run dev.jupyterlab-auth:test
hatch run dev.jupyterlab:test
52 changes: 52 additions & 0 deletions config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
---
component:
type: jupyverse
components:
app:
type: app
auth:
type: auth
#auth:
# type: auth_fief
#auth:
# type: noauth
contents:
type: contents
frontend:
type: frontend
lab:
type: lab
jupyterlab:
type: jupyterlab
kernels:
type: kernels
login:
type: login
nbconvert:
type: nbconvert
resource_usage:
type: resource_usage
track_cpu_percent: true
#retrolab:
# type: retrolab
terminals:
type: terminals
yjs:
type: yjs

logging:
version: 1
disable_existing_loggers: false
formatters:
default:
format: '[%(asctime)s %(levelname)s] %(message)s'
handlers:
console:
class: logging.StreamHandler
formatter: default
root:
handlers: [console]
level: INFO
loggers:
webnotifier:
level: DEBUG
File renamed without changes.
File renamed without changes.
3 changes: 3 additions & 0 deletions jupyverse_api/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Jupyverse API

The public API for Jupyverse.
42 changes: 42 additions & 0 deletions jupyverse_api/jupyverse_api/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
from typing import Dict

from pydantic import BaseModel, Extra

from .app import App


__version__ = "0.0.50"


class Singleton(type):
_instances: Dict = {}

def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instances[cls]


class Config(BaseModel):
class Config:
extra = Extra.forbid


class Router:
_app: App

def __init__(
self,
app: App,
) -> None:
self._app = app

@property
def _type(self):
return self.__class__.__name__

def include_router(self, router, **kwargs):
self._app._include_router(router, self._type, **kwargs)

def mount(self, path: str, *args, **kwargs) -> None:
self._app._mount(path, self._type, *args, **kwargs)
51 changes: 51 additions & 0 deletions jupyverse_api/jupyverse_api/app/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import logging
from collections import defaultdict
from typing import Dict, List

from fastapi import FastAPI

from ..exceptions import RedirectException, _redirect_exception_handler


logger = logging.getLogger("app")


class App:
"""A wrapper around FastAPI that checks for endpoint path conflicts."""

_app: FastAPI
_router_paths: Dict[str, List[str]]

def __init__(self, app: FastAPI):
self._app = app
app.add_exception_handler(RedirectException, _redirect_exception_handler)
self._router_paths = defaultdict(list)

@property
def _paths(self):
return [path for router, paths in self._router_paths.items() for path in paths]

def _include_router(self, router, _type, **kwargs) -> None:
new_paths = []
for route in router.routes:
path = kwargs.get("prefix", "") + route.path
for _router, _paths in self._router_paths.items():
if path in _paths:
raise RuntimeError(
f"{_type} adds a handler for a path that is already defined in "
f"{_router}: {path}"
)
logger.debug("%s added handler for path: %s", _type, path)
new_paths.append(path)
self._router_paths[_type].extend(new_paths)
self._app.include_router(router, **kwargs)

def _mount(self, path: str, _type, *args, **kwargs) -> None:
for _router, _paths in self._router_paths.items():
if path in _paths:
raise RuntimeError(
f"{_type } mounts a path that is already defined in {_router}: {path}"
)
self._router_paths[_type].append(path)
logger.debug("%s mounted path: %s", _type, path)
self._app.mount(path, *args, **kwargs)
27 changes: 27 additions & 0 deletions jupyverse_api/jupyverse_api/auth/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from abc import ABC, abstractmethod
from typing import Any, Callable, Dict, List, Optional, Tuple

from jupyverse_api import Config

from .models import User # noqa


class Auth(ABC):
@abstractmethod
def current_user(self, permissions: Optional[Dict[str, List[str]]] = None) -> Callable:
...

@abstractmethod
async def update_user(self) -> Callable:
...

@abstractmethod
def websocket_auth(
self,
permissions: Optional[Dict[str, List[str]]] = None,
) -> Callable[[], Tuple[Any, Dict[str, List[str]]]]:
...


class AuthConfig(Config):
pass
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
from typing import Optional

from pydantic import BaseModel


class User(BaseModel):
anonymous: bool = True
username: str = ""
name: str = ""
display_name: str = ""
Expand Down
38 changes: 38 additions & 0 deletions jupyverse_api/jupyverse_api/contents/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import asyncio
from abc import ABC, abstractmethod
from pathlib import Path
from typing import Dict, Union

from jupyverse_api import Router

from .models import Content, SaveContent


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

@abstractmethod
async def get_path(self, file_id: str) -> str:
...

@abstractmethod
async def get_id(self, file_path: str) -> str:
...


class Contents(Router, ABC):
@property
@abstractmethod
def file_id_manager(self) -> FileIdManager:
...

@abstractmethod
async def read_content(
self, path: Union[str, Path], get_content: bool, as_json: bool = False
) -> Content:
...

@abstractmethod
async def write_content(self, content: Union[SaveContent, Dict]) -> None:
...
23 changes: 23 additions & 0 deletions jupyverse_api/jupyverse_api/contents/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from typing import Dict, List, Optional, Union

from pydantic import BaseModel


class Content(BaseModel):
name: str
path: str
last_modified: Optional[str]
created: Optional[str]
content: Optional[Union[str, Dict, List[Dict]]]
format: Optional[str]
mimetype: Optional[str]
size: Optional[int]
writable: bool
type: str


class SaveContent(BaseModel):
content: Optional[Union[str, Dict]]
format: str
path: str
type: str
11 changes: 11 additions & 0 deletions jupyverse_api/jupyverse_api/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from fastapi import Request, Response
from fastapi.responses import RedirectResponse


class RedirectException(Exception):
def __init__(self, redirect_to: str):
self.redirect_to = redirect_to


async def _redirect_exception_handler(request: Request, exc: RedirectException) -> Response:
return RedirectResponse(url=exc.redirect_to)
6 changes: 6 additions & 0 deletions jupyverse_api/jupyverse_api/frontend/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from jupyverse_api import Config


class FrontendConfig(Config):
base_url: str = "/"
collaborative: bool = False
9 changes: 9 additions & 0 deletions jupyverse_api/jupyverse_api/jupyterlab/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from jupyverse_api import Config, Router


class JupyterLab(Router):
pass


class JupyterLabConfig(Config):
dev_mode: bool = False
16 changes: 16 additions & 0 deletions jupyverse_api/jupyverse_api/kernels/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from abc import ABC, abstractmethod
from pathlib import Path
from typing import Optional

from jupyverse_api import Router, Config


class Kernels(Router, ABC):
@abstractmethod
async def watch_connection_files(self, path: Path) -> None:
...


class KernelsConfig(Config):
default_kernel: str = "python3"
connection_path: Optional[str] = None
File renamed without changes.
18 changes: 18 additions & 0 deletions jupyverse_api/jupyverse_api/lab/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from abc import ABC, abstractmethod
from pathlib import Path
from typing import Any, Dict, List, Tuple

from fastapi import APIRouter
from jupyverse_api import Router


class Lab(Router, ABC):
@abstractmethod
def init_router(
self, router: APIRouter, redirect_after_root: str
) -> Tuple[Path, List[Dict[str, Any]]]:
...

@abstractmethod
def get_federated_extensions(self, extensions_dir: Path) -> Tuple[List, List]:
...
5 changes: 5 additions & 0 deletions jupyverse_api/jupyverse_api/login/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from jupyverse_api import Router


class Login(Router):
pass
24 changes: 24 additions & 0 deletions jupyverse_api/jupyverse_api/main/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from asphalt.core import Component, Context
from asphalt.web.fastapi import FastAPIComponent
from fastapi import FastAPI

from ..app import App


class AppComponent(Component):
async def start(
self,
ctx: Context,
) -> None:
app = await ctx.request_resource(FastAPI)

_app = App(app)
ctx.add_resource(_app)


class JupyverseComponent(FastAPIComponent):
async def start(
self,
ctx: Context,
) -> None:
await super().start(ctx)
5 changes: 5 additions & 0 deletions jupyverse_api/jupyverse_api/nbconvert/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from jupyverse_api import Router


class Nbconvert(Router):
pass
Empty file.
Loading

0 comments on commit 8476938

Please sign in to comment.