Skip to content

Commit

Permalink
Merge pull request #47 from gdsfactory/kweb-2.0
Browse files Browse the repository at this point in the history
Kweb 2.0
  • Loading branch information
sebastian-goeldi authored Apr 11, 2024
2 parents bb67f7b + 167f51e commit f7b49dc
Show file tree
Hide file tree
Showing 9 changed files with 698 additions and 145 deletions.
5 changes: 3 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ dependencies = [
"klayout >= 0.28.17",
"fastapi",
"uvicorn[standard]",
"jinja2"
"jinja2",
"pydantic_extra_types>=2.6.0"
]
description = "KLayout API implementation of gdsfactory"
name = "kweb"
Expand Down Expand Up @@ -87,7 +88,7 @@ multi_line_output = 3
skip = ["kweb/__init__.py"]

[tool.mypy]
plugins = "pydantic.mypy"
plugins = "pydantic.mypy, numpy.typing.mypy_plugin"
python_version = "3.10"
strict = true

Expand Down
70 changes: 20 additions & 50 deletions src/kweb/api/viewer.py
Original file line number Diff line number Diff line change
@@ -1,75 +1,48 @@
from pathlib import Path
from typing import Annotated

from fastapi import APIRouter, Request
from fastapi import APIRouter, Depends, Request
from fastapi.exceptions import HTTPException
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
from pydantic import BaseModel
from starlette.templating import _TemplateResponse

# from . import __version__ as version
from .. import __version__ as version

router = APIRouter()
templates = Jinja2Templates(
directory=(Path(__file__).parent.parent / "templates").resolve()
)


@router.get("/gds/{gds_name:path}", response_class=HTMLResponse)
async def gds_view_static(
request: Request,
gds_name: str,
layer_props: str | None = None,
cell: str | None = None,
) -> _TemplateResponse:
settings = router.dependencies[0].dependency() # type: ignore[misc]
gds_file = (settings.fileslocation / f"{gds_name}").with_suffix(".gds")
class FileView(BaseModel):
file: Path
cell: str | None = None
layer_props: str | None = None
rdb: str | None = None

exists = (
gds_file.is_file()
and gds_file.stat().st_mode
)

if not exists:
raise HTTPException(
status_code=404,
detail=f'No gds found with name "{gds_name}".'
" It doesn't exist or is not accessible",
)

return await show_file(request, gds_file, layer_props, cell)


@router.get("/file/{file_name:path}", response_class=HTMLResponse)
@router.get("/view", response_class=HTMLResponse)
async def file_view_static(
request: Request,
file_name: str,
layer_props: str | None = None,
cell: str | None = None,
request: Request, params: Annotated[FileView, Depends()]
) -> _TemplateResponse:
settings = router.dependencies[0].dependency() # type: ignore[misc]
file = settings.fileslocation / f"{file_name}"
_file = settings.fileslocation / f"{params.file}"

exists = (
file.is_file()
and file.stat().st_mode
)
exists = _file.is_file() and _file.stat().st_mode

if not exists:
raise HTTPException(
status_code=404,
detail=f'No file found with name "{file_name}".'
detail=f'No file found with name "{_file}".'
" It doesn't exist or is not accessible",
)

return await show_file(request, file, layer_props, cell)
return await show_file(request, layout_params=params)


async def show_file(
request: Request,
file: Path,
layer_props: str | None = None,
cell: str | None = None,
) -> _TemplateResponse:
async def show_file(request: Request, layout_params: FileView) -> _TemplateResponse:
root_path = request.scope["root_path"]

match request.url.scheme:
Expand All @@ -94,19 +67,16 @@ async def show_file(
template_params = {
"request": request,
"url": url,
"file": file,
"layer_props": layer_props,
}

if cell is not None:
template_params["cell"] = cell
template_params["params"] = layout_params.model_dump(mode="json", exclude_none=True)

return templates.TemplateResponse(
"viewer.html",
template_params,
)


# @router.get("/status")
# async def status() -> dict[str, Any]:
# return {"server": "kweb", "version": version}
@router.get("/status")
async def kweb_status() -> dict[str, str | int]:
return {"server": "kweb", "version": version}
41 changes: 24 additions & 17 deletions src/kweb/browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,25 @@
from . import config
from .api.browser import router as browser_router
from .api.viewer import router as viewer_router
from .layout_server import EditableLayoutViewServerEndpoint, LayoutViewServerEndpoint
from .layout_server import LayoutViewServerEndpoint


def get_app(fileslocation: Path | str | None = None, editable: bool = False) -> FastAPI:
if fileslocation is None:

def settings() -> config.Config:
try:
return config.Config()
except ValidationError:
raise ValueError(
"To start the Kweb please set the environment "
"variable KWEB_FILESLOCATION to a path in your filesystem."
" Alternatively, you can set the filepath in the get_app function."
)
try:
_settings = config.Config()
except ValidationError:
raise ValueError(
"To start the Kweb please set the environment "
"variable KWEB_FILESLOCATION to a path in your filesystem."
" Alternatively, you can set the filepath in the get_app function."
)

else:
_settings = config.Config(fileslocation=fileslocation, editable=editable)

def settings() -> config.Config:
return config.Config(fileslocation=fileslocation)
def settings() -> config.Config:
return _settings

staticfiles = StaticFiles(directory=Path(__file__).parent / "static")

Expand All @@ -36,10 +35,18 @@ def settings() -> config.Config:
viewer_router.dependencies.insert(0, Depends(settings))
browser_router.dependencies.insert(0, Depends(settings))

if editable:
app.add_websocket_route("/ws", EditableLayoutViewServerEndpoint)
else:
app.add_websocket_route("/ws", LayoutViewServerEndpoint)
_settings = settings()

class BrowserLayoutViewServerEndpoint(
LayoutViewServerEndpoint,
root=_settings.fileslocation,
editable=editable,
add_missing_layers=_settings.add_missing_layers,
meta_splitter=_settings.meta_splitter,
):
pass

app.add_websocket_route("/ws", BrowserLayoutViewServerEndpoint)
viewer_router.dependencies.insert(0, Depends(settings))
app.include_router(viewer_router)
app.include_router(browser_router)
Expand Down
5 changes: 5 additions & 0 deletions src/kweb/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ class Config:
env_nested_delimiter = "_"

fileslocation: Path
meta_splitter: str = ":"
editable: bool = False
add_missing_layers: bool = True
max_rdb_limit: int = 100
"""Maximum rdb errors the client can request."""

@pydantic.validator("fileslocation")
@classmethod
Expand Down
Loading

0 comments on commit f7b49dc

Please sign in to comment.