Implemented modular code structure
Refactored repository into kaya-core and kaya-rsgi packages
This commit is contained in:
@@ -26,14 +26,17 @@ jobs:
|
||||
.venv/bin/pip install -r requirements-dev.txt
|
||||
- name: Unit tests
|
||||
run: |
|
||||
.venv/bin/pip install .
|
||||
.venv/bin/python -m mypy -p src
|
||||
.venv/bin/python -m unittest discover -s tests
|
||||
.venv/bin/pip install packages/kaya-core packages/kaya-rsgi
|
||||
.venv/bin/python -m mypy -p kaya.core
|
||||
.venv/bin/python -m mypy -p kaya.rsgi
|
||||
.venv/bin/python -m unittest discover -s packages/kaya-core/tests
|
||||
.venv/bin/python -m unittest discover -s packages/kaya-rsgi/tests
|
||||
- name: Publish artifacts
|
||||
env:
|
||||
TWINE_REPOSITORY_URL: ${{ vars.PYPI_REGISTRY_URL }}
|
||||
TWINE_USERNAME: ${{ vars.PUBLISHER_USERNAME }}
|
||||
TWINE_PASSWORD: ${{ secrets.PUBLISHER_TOKEN }}
|
||||
run: |
|
||||
.venv/bin/pyproject-build
|
||||
.venv/bin/twine upload --repository gitea dist/*.whl dist/*.tar.gz
|
||||
.venv/bin/pyproject-build packages/kaya-core
|
||||
.venv/bin/pyproject-build packages/kaya-rsgi
|
||||
.venv/bin/twine upload --repository gitea packages/kaya-core/dist/*.whl packages/kaya-core/dist/*.tar.gz packages/kaya-rsgi/dist/*.whl packages/kaya-rsgi/dist/*.tar.gz
|
||||
|
||||
+2
-2
@@ -4,5 +4,5 @@ __pycache__
|
||||
.mypy_cache
|
||||
_version.py
|
||||
*.egg-info
|
||||
/build
|
||||
/dist
|
||||
build/
|
||||
dist/
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
# kaya
|
||||
|
||||
A lightweight ASGI/RSGI web framework with method-aware routing, path matching, and recursive wildcard support.
|
||||
A lightweight ASGI web framework with method-aware routing, path matching, and recursive wildcard support.
|
||||
|
||||
## Packages
|
||||
|
||||
This repository is a monorepo for the Kaya framework. The code is split into independent packages under `packages/`:
|
||||
|
||||
- **kaya-core** — core routing, HTTP/WS abstractions, and ASGI adapter (`packages/kaya-core/`)
|
||||
- **kaya-rsgi** — RSGI/Granian integration (`packages/kaya-rsgi/`)
|
||||
|
||||
Additional `kaya-*` packages can be added as new directories under `packages/`.
|
||||
|
||||
## Build & run locally
|
||||
|
||||
@@ -10,10 +19,10 @@ Install dev dependencies:
|
||||
pip install --index-url https://gitea.woggioni.net/api/packages/woggioni/pypi/simple --extra-index-url https://pypi.org/simple -r requirements-dev.txt
|
||||
```
|
||||
|
||||
Build the package:
|
||||
Install the packages in development mode:
|
||||
|
||||
```bash
|
||||
python -m build
|
||||
pip install -e packages/kaya-core -e packages/kaya-rsgi
|
||||
```
|
||||
|
||||
Run the example:
|
||||
@@ -25,11 +34,20 @@ python example/hello.py
|
||||
## Tests
|
||||
|
||||
```bash
|
||||
PYTHONPATH=src python -m unittest discover tests
|
||||
python -m unittest discover -s packages/kaya-core/tests
|
||||
python -m unittest discover -s packages/kaya-rsgi/tests
|
||||
```
|
||||
|
||||
## Static analysis
|
||||
|
||||
```bash
|
||||
mypy src/kaya
|
||||
mypy -p kaya.core
|
||||
mypy -p kaya.rsgi
|
||||
```
|
||||
|
||||
## Building packages
|
||||
|
||||
```bash
|
||||
python -m build packages/kaya-core
|
||||
python -m build packages/kaya-rsgi
|
||||
```
|
||||
|
||||
+2
-11
@@ -1,15 +1,6 @@
|
||||
from kaya import KayaApp, HttpContext, HttpMethod, WebSocket
|
||||
from kaya.core import KayaApp, HttpContext, HttpMethod, WebSocket
|
||||
from typing import List
|
||||
|
||||
class Hello(KayaApp):
|
||||
pass
|
||||
|
||||
# async def handle_request(self, ctx: HttpContext) -> None:
|
||||
# async for chunk in ctx.request_body:
|
||||
# print(chunk.decode())
|
||||
# await ctx.send_str(200, 'Hello World')
|
||||
|
||||
|
||||
app = KayaApp()
|
||||
|
||||
|
||||
@@ -53,4 +44,4 @@ async def echo(ws: WebSocket) -> None:
|
||||
elif msg.kind == 'binary':
|
||||
data = msg.data
|
||||
assert isinstance(data, bytes)
|
||||
await ws.send_bytes(data)
|
||||
await ws.send_bytes(data)
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
# kaya-core
|
||||
|
||||
Core package of the Kaya web framework.
|
||||
|
||||
Provides method-aware routing, path matching, HTTP/WebSocket abstractions, and an ASGI adapter.
|
||||
@@ -0,0 +1,57 @@
|
||||
[build-system]
|
||||
requires = ["setuptools>=61.0", "setuptools-scm>=8"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "kaya-core"
|
||||
dynamic = ["version"]
|
||||
authors = [
|
||||
{ name="Walter Oggioni", email="oggioni.walter@gmail.com" },
|
||||
]
|
||||
description = "Core package of the Kaya lightweight ASGI web framework"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10"
|
||||
license = "MIT"
|
||||
classifiers = [
|
||||
'Development Status :: 3 - Alpha',
|
||||
'Topic :: Utilities',
|
||||
'Intended Audience :: System Administrators',
|
||||
'Intended Audience :: Developers',
|
||||
'Environment :: Console',
|
||||
'Programming Language :: Python :: 3',
|
||||
]
|
||||
|
||||
dependencies = [
|
||||
"pwo",
|
||||
"typing-extensions",
|
||||
]
|
||||
|
||||
[project.optional-dependencies]
|
||||
dev = [
|
||||
"build", "mypy", "ipdb", "twine", "httpx"
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
"Homepage" = "https://github.com/woggioni/kaya"
|
||||
"Bug Tracker" = "https://github.com/woggioni/kaya/issues"
|
||||
|
||||
[tool.setuptools.packages.find]
|
||||
where = ["src"]
|
||||
namespaces = true
|
||||
|
||||
[tool.mypy]
|
||||
python_version = "3.12"
|
||||
disallow_untyped_defs = true
|
||||
show_error_codes = true
|
||||
no_implicit_optional = true
|
||||
warn_return_any = true
|
||||
warn_unused_ignores = true
|
||||
exclude = ["scripts", "docs", "test"]
|
||||
strict = true
|
||||
|
||||
[tool.setuptools_scm]
|
||||
root = "../.."
|
||||
version_file = "src/kaya/core/_version.py"
|
||||
|
||||
[tool.setuptools_scm.tag]
|
||||
prefix = "release/"
|
||||
@@ -1,17 +1,19 @@
|
||||
from ._app import KayaApp
|
||||
from ._app import AbstractKayaApp, KayaApp
|
||||
from ._http_method import HttpMethod
|
||||
from ._http_context import HttpContext
|
||||
from ._tree import Tree, PathIterator
|
||||
from ._path_handler import PathHandler
|
||||
from ._path_handler import PathHandler, Matches
|
||||
from ._websocket import WebSocket, WebSocketMessage
|
||||
|
||||
|
||||
__all__ = [
|
||||
'AbstractKayaApp',
|
||||
'HttpMethod',
|
||||
'KayaApp',
|
||||
'HttpContext',
|
||||
'Tree',
|
||||
'PathHandler',
|
||||
'Matches',
|
||||
'PathIterator',
|
||||
'WebSocket',
|
||||
'WebSocketMessage'
|
||||
@@ -2,31 +2,13 @@ from abc import ABC, abstractmethod
|
||||
from asyncio import Queue, AbstractEventLoop
|
||||
from asyncio import get_running_loop
|
||||
from logging import getLogger
|
||||
from typing import Callable, Awaitable, Any, Mapping, Sequence, Optional, Unpack, Tuple, TYPE_CHECKING, cast
|
||||
from pathlib import Path, PurePath
|
||||
from typing import Callable, Awaitable, Any, Mapping, Sequence, Optional, Unpack, Tuple, cast
|
||||
from pwo import Maybe, AsyncQueueIterator
|
||||
from hashlib import md5
|
||||
from ._http_context import HttpContext
|
||||
from ._http_method import HttpMethod
|
||||
from ._path_handler import Context
|
||||
from ._types import StrOrStrings
|
||||
from ._websocket import WebSocket
|
||||
from base64 import b64encode, b64decode
|
||||
from mimetypes import guess_type
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from _typeshed import StrOrBytesPath
|
||||
try:
|
||||
from ._rsgi import RsgiContext, RsgiWebSocket
|
||||
from granian._granian import ( # type: ignore
|
||||
RSGIHTTPProtocol,
|
||||
RSGIHTTPScope,
|
||||
RSGIWebsocketProtocol,
|
||||
RSGIWebsocketScope as RSGIWebSocketScope,
|
||||
)
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
from ._asgi import AsgiContext, AsgiWebSocket
|
||||
from ._tree import Tree
|
||||
from ._types.asgi import LifespanScope, HTTPScope as ASGIHTTPScope, WebSocketScope as ASGIWebSocketScope
|
||||
@@ -87,20 +69,6 @@ class AbstractKayaApp(ABC):
|
||||
async def handle_websocket(self, ws: WebSocket) -> None:
|
||||
raise NotImplementedError()
|
||||
|
||||
def __rsgi_init__(self, loop: AbstractEventLoop) -> None:
|
||||
self.setup(loop)
|
||||
|
||||
def __rsgi_del__(self, loop: AbstractEventLoop) -> None:
|
||||
self.shutdown(loop)
|
||||
|
||||
async def __rsgi__(self, scope: RSGIHTTPScope | RSGIWebSocketScope, protocol: RSGIHTTPProtocol | RSGIWebsocketProtocol) -> None:
|
||||
if scope.proto == 'ws':
|
||||
ws = RsgiWebSocket(scope, protocol) # type: ignore[arg-type]
|
||||
await self.handle_websocket(ws)
|
||||
else:
|
||||
ctx = RsgiContext(scope, protocol) # type: ignore[arg-type]
|
||||
await self.handle_request(ctx)
|
||||
|
||||
|
||||
class KayaApp(AbstractKayaApp):
|
||||
_tree: Tree
|
||||
@@ -115,7 +83,6 @@ class KayaApp(AbstractKayaApp):
|
||||
await handler.handle_request(ctx, captured)
|
||||
else:
|
||||
await ctx.send_empty(404)
|
||||
pass
|
||||
|
||||
async def handle_websocket(self, ws: WebSocket) -> None:
|
||||
result = self._tree.get_handler(ws.path, HttpMethod.WS)
|
||||
@@ -124,7 +91,6 @@ class KayaApp(AbstractKayaApp):
|
||||
await handler.handle_request(ws, captured)
|
||||
else:
|
||||
await ws.close(1000)
|
||||
pass
|
||||
|
||||
def route(self,
|
||||
paths: StrOrStrings,
|
||||
@@ -179,4 +145,3 @@ class KayaApp(AbstractKayaApp):
|
||||
|
||||
def PATCH(self, path: str, recursive: bool = False) -> Callable[[HttpHandler], HttpHandler]:
|
||||
return self.route(path, (HttpMethod.PATCH,), recursive)
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
from typing import (
|
||||
TypedDict,
|
||||
Literal,
|
||||
Iterable,
|
||||
Tuple,
|
||||
Optional,
|
||||
NotRequired,
|
||||
Dict,
|
||||
Any,
|
||||
Union,
|
||||
Mapping,
|
||||
Sequence
|
||||
)
|
||||
|
||||
from .base import StrOrStrings, PathMatcherResult
|
||||
from .asgi import ASGIVersions, HTTPScope, WebSocketScope, LifespanScope
|
||||
from .._http_method import HttpMethod
|
||||
|
||||
type NodeType = (str | HttpMethod)
|
||||
|
||||
|
||||
__all__ = [
|
||||
'HttpMethod',
|
||||
'HTTPScope',
|
||||
'LifespanScope',
|
||||
'ASGIVersions',
|
||||
'WebSocketScope',
|
||||
'NodeType',
|
||||
'StrOrStrings',
|
||||
'PathMatcherResult'
|
||||
]
|
||||
@@ -2,7 +2,7 @@ import unittest
|
||||
import json
|
||||
import httpx
|
||||
from pwo import async_test
|
||||
from kaya import KayaApp, HttpContext, HttpMethod
|
||||
from kaya.core import KayaApp, HttpContext, HttpMethod
|
||||
from typing import Sequence, List
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
from typing import Sequence, Tuple, Optional, List
|
||||
|
||||
from kaya import Tree, PathHandler, HttpContext, HttpMethod, PathIterator
|
||||
from kaya import HttpMethod
|
||||
from kaya.core import Tree, PathHandler, HttpContext, HttpMethod, PathIterator
|
||||
from pwo import Maybe
|
||||
import unittest
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import unittest
|
||||
from typing import Any, Callable, Awaitable, List, Mapping, Optional
|
||||
from pwo import async_test
|
||||
from kaya import KayaApp, WebSocket, WebSocketMessage
|
||||
from kaya.core import KayaApp, WebSocket, WebSocketMessage
|
||||
|
||||
|
||||
def websocket_scope(path: str = '/ws') -> Mapping[str, Any]:
|
||||
@@ -151,25 +151,3 @@ class WebSocketTest(unittest.TestCase):
|
||||
|
||||
self.assertEqual(sent_messages[0]['type'], 'websocket.accept')
|
||||
self.assertEqual(len(sent_messages), 1)
|
||||
|
||||
|
||||
class RsgiWebSocketTest(unittest.TestCase):
|
||||
|
||||
def test_misconfigured_granian(self):
|
||||
from kaya._rsgi import RsgiWebSocket
|
||||
|
||||
class FakeScope:
|
||||
scheme = 'ws'
|
||||
path = '/ws'
|
||||
query_string = ''
|
||||
headers = {}
|
||||
client = '127.0.0.1:12345'
|
||||
server = '127.0.0.1:80'
|
||||
|
||||
class FakeProtocol:
|
||||
pass
|
||||
|
||||
with self.assertRaises(RuntimeError) as ctx:
|
||||
RsgiWebSocket(FakeScope(), FakeProtocol()) # type: ignore[arg-type]
|
||||
|
||||
self.assertIn('Granian was not configured for websockets', str(ctx.exception))
|
||||
@@ -0,0 +1,5 @@
|
||||
# kaya-rsgi
|
||||
|
||||
RSGI/Granian integration for the Kaya web framework.
|
||||
|
||||
Provides `RsgiContext`, `RsgiWebSocket`, and `RsgiApplication` to run Kaya apps on Granian's RSGI protocol.
|
||||
@@ -3,42 +3,43 @@ requires = ["setuptools>=61.0", "setuptools-scm>=8"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "kaya"
|
||||
name = "kaya-rsgi"
|
||||
dynamic = ["version"]
|
||||
authors = [
|
||||
{ name="Walter Oggioni", email="oggioni.walter@gmail.com" },
|
||||
]
|
||||
description = "A lightweight ASGI/RSGI web framework"
|
||||
description = "RSGI/Granian integration for the Kaya web framework"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10"
|
||||
license = "MIT"
|
||||
classifiers = [
|
||||
'Development Status :: 3 - Alpha',
|
||||
'Topic :: Utilities',
|
||||
'License :: OSI Approved :: MIT License',
|
||||
'Intended Audience :: System Administrators',
|
||||
'Intended Audience :: Developers',
|
||||
'Environment :: Console',
|
||||
'License :: OSI Approved :: MIT License',
|
||||
'Programming Language :: Python :: 3',
|
||||
]
|
||||
|
||||
dependencies = [
|
||||
"kaya-core",
|
||||
"granian>=2.0",
|
||||
"pwo",
|
||||
]
|
||||
|
||||
[project.optional-dependencies]
|
||||
dev = [
|
||||
"build", "mypy", "ipdb", "twine", "granian", "httpx"
|
||||
]
|
||||
|
||||
rsgi = [
|
||||
"granian"
|
||||
"build", "mypy", "ipdb", "twine"
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
"Homepage" = "https://github.com/woggioni/kaya"
|
||||
"Bug Tracker" = "https://github.com/woggioni/kaya/issues"
|
||||
|
||||
[tool.setuptools.packages.find]
|
||||
where = ["src"]
|
||||
namespaces = true
|
||||
|
||||
[tool.mypy]
|
||||
python_version = "3.12"
|
||||
disallow_untyped_defs = true
|
||||
@@ -50,7 +51,8 @@ exclude = ["scripts", "docs", "test"]
|
||||
strict = true
|
||||
|
||||
[tool.setuptools_scm]
|
||||
version_file = "src/kaya/_version.py"
|
||||
root = "../.."
|
||||
version_file = "src/kaya/rsgi/_version.py"
|
||||
|
||||
[tool.setuptools_scm.tag]
|
||||
prefix = "release/"
|
||||
prefix = "release/"
|
||||
@@ -0,0 +1,11 @@
|
||||
from ._rsgi import RsgiApplication, RsgiContext, RsgiWebSocket
|
||||
from ._types import HTTPScope, WebSocketScope
|
||||
|
||||
|
||||
__all__ = [
|
||||
'RsgiApplication',
|
||||
'RsgiContext',
|
||||
'RsgiWebSocket',
|
||||
'HTTPScope',
|
||||
'WebSocketScope',
|
||||
]
|
||||
@@ -14,13 +14,17 @@ from typing import (
|
||||
cast
|
||||
)
|
||||
|
||||
from granian._granian import RSGIHTTPProtocol, RSGIHTTPScope, RSGIWebsocketProtocol, RSGIWebsocketScope, RSGIWebsocketTransport # type: ignore[attr-defined]
|
||||
from granian._granian import ( # type: ignore[attr-defined]
|
||||
RSGIHTTPProtocol,
|
||||
RSGIHTTPScope,
|
||||
RSGIWebsocketProtocol,
|
||||
RSGIWebsocketScope,
|
||||
RSGIWebsocketTransport,
|
||||
)
|
||||
from pwo import Maybe
|
||||
|
||||
from ._types import StrOrStrings
|
||||
from ._http_context import HttpContext
|
||||
from ._http_method import HttpMethod
|
||||
from ._websocket import WebSocket, WebSocketMessage
|
||||
from kaya.core import AbstractKayaApp, HttpContext, HttpMethod, WebSocket, WebSocketMessage
|
||||
from kaya.core._types import StrOrStrings
|
||||
|
||||
|
||||
class RsgiContext(HttpContext):
|
||||
@@ -56,12 +60,6 @@ class RsgiContext(HttpContext):
|
||||
self.request_body = cast(AsyncIterator[bytes], protocol)
|
||||
self.protocol = protocol
|
||||
|
||||
# @staticmethod
|
||||
# def _rearrange_headers(headers: Mapping[str, Sequence[str]]) -> List[Tuple[str, str]]:
|
||||
# return list(
|
||||
# ((key, value) for key, values in headers.items() for value in values)
|
||||
# )
|
||||
|
||||
@staticmethod
|
||||
def _rearrange_headers(headers: Mapping[str, StrOrStrings]) -> List[Tuple[str, str]]:
|
||||
result = []
|
||||
@@ -177,3 +175,26 @@ class RsgiWebSocket(WebSocket):
|
||||
if message.kind == 'close':
|
||||
raise StopAsyncIteration
|
||||
return message
|
||||
|
||||
|
||||
class RsgiApplication:
|
||||
_app: AbstractKayaApp
|
||||
|
||||
def __init__(self, app: AbstractKayaApp) -> None:
|
||||
self._app = app
|
||||
|
||||
def __rsgi_init__(self, loop: Any) -> None:
|
||||
self._app.setup(loop)
|
||||
|
||||
def __rsgi_del__(self, loop: Any) -> None:
|
||||
self._app.shutdown(loop)
|
||||
|
||||
async def __rsgi__(self,
|
||||
scope: RSGIHTTPScope | RSGIWebsocketScope,
|
||||
protocol: RSGIHTTPProtocol | RSGIWebsocketProtocol) -> None:
|
||||
if scope.proto == 'ws':
|
||||
ws = RsgiWebSocket(scope, protocol) # type: ignore[arg-type]
|
||||
await self._app.handle_websocket(ws)
|
||||
else:
|
||||
ctx = RsgiContext(scope, protocol) # type: ignore[arg-type]
|
||||
await self._app.handle_request(ctx)
|
||||
@@ -1,17 +1,11 @@
|
||||
from typing import (
|
||||
Sequence,
|
||||
TypedDict,
|
||||
Literal,
|
||||
Iterable,
|
||||
Tuple,
|
||||
Optional,
|
||||
NotRequired,
|
||||
Dict,
|
||||
Any,
|
||||
Union,
|
||||
Mapping,
|
||||
)
|
||||
|
||||
|
||||
class HTTPScope(TypedDict):
|
||||
proto: Literal['http']
|
||||
rsgi_version: str
|
||||
@@ -37,4 +31,10 @@ class WebSocketScope(TypedDict):
|
||||
path: str
|
||||
query_string: str
|
||||
headers: Mapping[str, str]
|
||||
authority: Optional[str]
|
||||
authority: Optional[str]
|
||||
|
||||
|
||||
__all__ = [
|
||||
'HTTPScope',
|
||||
'WebSocketScope',
|
||||
]
|
||||
@@ -0,0 +1,22 @@
|
||||
import unittest
|
||||
from kaya.rsgi import RsgiWebSocket
|
||||
|
||||
|
||||
class RsgiWebSocketTest(unittest.TestCase):
|
||||
|
||||
def test_misconfigured_granian(self):
|
||||
class FakeScope:
|
||||
scheme = 'ws'
|
||||
path = '/ws'
|
||||
query_string = ''
|
||||
headers = {}
|
||||
client = '127.0.0.1:12345'
|
||||
server = '127.0.0.1:80'
|
||||
|
||||
class FakeProtocol:
|
||||
pass
|
||||
|
||||
with self.assertRaises(RuntimeError) as ctx:
|
||||
RsgiWebSocket(FakeScope(), FakeProtocol()) # type: ignore[arg-type]
|
||||
|
||||
self.assertIn('Granian was not configured for websockets', str(ctx.exception))
|
||||
+11
-149
@@ -1,154 +1,16 @@
|
||||
#
|
||||
# This file is autogenerated by pip-compile with Python 3.14
|
||||
# by the following command:
|
||||
#
|
||||
# pip-compile --allow-unsafe --extra=dev --extra=rsgi --output-file=requirements-dev.txt pyproject.toml
|
||||
# Development dependencies for the Kaya monorepo.
|
||||
# Install with:
|
||||
# pip install -r requirements-dev.txt
|
||||
#
|
||||
--index-url https://gitea.woggioni.net/api/packages/woggioni/pypi/simple
|
||||
--extra-index-url https://pypi.org/simple
|
||||
|
||||
anyio==4.14.1
|
||||
# via httpx
|
||||
ast-serialize==0.5.0
|
||||
# via mypy
|
||||
asttokens==3.0.1
|
||||
# via stack-data
|
||||
build==1.5.0
|
||||
# via kaya (pyproject.toml)
|
||||
certifi==2026.6.17
|
||||
# via
|
||||
# httpcore
|
||||
# httpx
|
||||
# requests
|
||||
cffi==2.0.0
|
||||
# via cryptography
|
||||
charset-normalizer==3.4.7
|
||||
# via requests
|
||||
click==8.4.2
|
||||
# via granian
|
||||
cryptography==49.0.0
|
||||
# via secretstorage
|
||||
decorator==5.3.1
|
||||
# via
|
||||
# ipdb
|
||||
# ipython
|
||||
docutils==0.23
|
||||
# via readme-renderer
|
||||
executing==2.2.1
|
||||
# via stack-data
|
||||
granian==2.7.7
|
||||
# via kaya (pyproject.toml)
|
||||
h11==0.16.0
|
||||
# via httpcore
|
||||
httpcore==1.0.9
|
||||
# via httpx
|
||||
httpx==0.28.1
|
||||
# via kaya (pyproject.toml)
|
||||
id==1.6.1
|
||||
# via twine
|
||||
idna==3.18
|
||||
# via
|
||||
# anyio
|
||||
# httpx
|
||||
# requests
|
||||
ipdb==0.13.13
|
||||
# via kaya (pyproject.toml)
|
||||
ipython==9.15.0
|
||||
# via ipdb
|
||||
ipython-pygments-lexers==1.1.1
|
||||
# via ipython
|
||||
jaraco-classes==3.4.0
|
||||
# via keyring
|
||||
jaraco-context==6.1.2
|
||||
# via keyring
|
||||
jaraco-functools==4.5.0
|
||||
# via keyring
|
||||
jedi==0.20.0
|
||||
# via ipython
|
||||
jeepney==0.9.0
|
||||
# via
|
||||
# keyring
|
||||
# secretstorage
|
||||
keyring==25.7.0
|
||||
# via twine
|
||||
librt==0.11.0
|
||||
# via mypy
|
||||
markdown-it-py==4.2.0
|
||||
# via rich
|
||||
matplotlib-inline==0.2.2
|
||||
# via ipython
|
||||
mdurl==0.1.2
|
||||
# via markdown-it-py
|
||||
more-itertools==11.1.0
|
||||
# via
|
||||
# jaraco-classes
|
||||
# jaraco-functools
|
||||
mypy==2.1.0
|
||||
# via kaya (pyproject.toml)
|
||||
mypy-extensions==1.1.0
|
||||
# via mypy
|
||||
nh3==0.3.6
|
||||
# via readme-renderer
|
||||
packaging==26.2
|
||||
# via
|
||||
# build
|
||||
# twine
|
||||
parso==0.8.7
|
||||
# via jedi
|
||||
pathspec==1.1.1
|
||||
# via mypy
|
||||
pexpect==4.9.0
|
||||
# via ipython
|
||||
prompt-toolkit==3.0.52
|
||||
# via ipython
|
||||
psutil==7.2.2
|
||||
# via ipython
|
||||
ptyprocess==0.7.0
|
||||
# via pexpect
|
||||
pure-eval==0.2.3
|
||||
# via stack-data
|
||||
pwo==0.1.2
|
||||
# via kaya (pyproject.toml)
|
||||
pycparser==3.0
|
||||
# via cffi
|
||||
pygments==2.20.0
|
||||
# via
|
||||
# ipython
|
||||
# ipython-pygments-lexers
|
||||
# readme-renderer
|
||||
# rich
|
||||
pyproject-hooks==1.2.0
|
||||
# via build
|
||||
readme-renderer==45.0
|
||||
# via twine
|
||||
requests==2.34.2
|
||||
# via
|
||||
# requests-toolbelt
|
||||
# twine
|
||||
requests-toolbelt==1.0.0
|
||||
# via twine
|
||||
rfc3986==2.0.0
|
||||
# via twine
|
||||
rich==15.0.0
|
||||
# via twine
|
||||
secretstorage==3.5.0
|
||||
# via keyring
|
||||
stack-data==0.6.3
|
||||
# via ipython
|
||||
traitlets==5.15.1
|
||||
# via
|
||||
# ipython
|
||||
# matplotlib-inline
|
||||
twine==6.2.0
|
||||
# via kaya (pyproject.toml)
|
||||
typing-extensions==4.15.0
|
||||
# via
|
||||
# mypy
|
||||
# pwo
|
||||
urllib3==2.7.0
|
||||
# via
|
||||
# id
|
||||
# requests
|
||||
# twine
|
||||
wcwidth==0.8.1
|
||||
# via prompt-toolkit
|
||||
build
|
||||
mypy
|
||||
ipdb
|
||||
twine
|
||||
httpx
|
||||
granian
|
||||
pwo
|
||||
typing-extensions
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
#
|
||||
# This file is autogenerated by pip-compile with Python 3.14
|
||||
# by the following command:
|
||||
#
|
||||
# pip-compile --allow-unsafe --extra=rsgi --output-file=requirements.txt pyproject.toml
|
||||
#
|
||||
--index-url https://gitea.woggioni.net/api/packages/woggioni/pypi/simple
|
||||
--extra-index-url https://pypi.org/simple
|
||||
|
||||
click==8.4.2
|
||||
# via granian
|
||||
granian==2.7.7
|
||||
# via kaya (pyproject.toml)
|
||||
pwo==0.1.2
|
||||
# via kaya (pyproject.toml)
|
||||
typing-extensions==4.15.0
|
||||
# via pwo
|
||||
@@ -1,12 +0,0 @@
|
||||
from dataclasses import dataclass
|
||||
from typing import (
|
||||
Optional,
|
||||
Dict,
|
||||
List,
|
||||
)
|
||||
from ._types import NodeType
|
||||
from ._path_handler import PathHandler
|
||||
from ._path_matcher import PathMatcher
|
||||
|
||||
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
from typing import (
|
||||
TypedDict,
|
||||
Literal,
|
||||
Iterable,
|
||||
Tuple,
|
||||
Optional,
|
||||
NotRequired,
|
||||
Dict,
|
||||
Any,
|
||||
Union,
|
||||
Mapping,
|
||||
Sequence
|
||||
)
|
||||
|
||||
from .base import StrOrStrings, PathMatcherResult
|
||||
|
||||
from kaya._http_method import HttpMethod
|
||||
|
||||
type NodeType = (str | HttpMethod)
|
||||
|
||||
|
||||
class ASGIVersions(TypedDict):
|
||||
spec_version: str
|
||||
version: Union[Literal["2.0"], Literal["3.0"]]
|
||||
|
||||
|
||||
class HTTPScope(TypedDict):
|
||||
type: Literal["http"]
|
||||
asgi: ASGIVersions
|
||||
http_version: str
|
||||
method: str
|
||||
scheme: str
|
||||
path: str
|
||||
raw_path: bytes
|
||||
query_string: bytes
|
||||
root_path: str
|
||||
headers: Iterable[Tuple[bytes, bytes]]
|
||||
client: Optional[Tuple[str, int]]
|
||||
server: Optional[Tuple[str, Optional[int]]]
|
||||
state: NotRequired[Dict[str, Any]]
|
||||
extensions: Optional[Dict[str, Dict[object, object]]]
|
||||
|
||||
|
||||
class WebSocketScope(TypedDict):
|
||||
type: Literal["websocket"]
|
||||
asgi: ASGIVersions
|
||||
http_version: str
|
||||
scheme: str
|
||||
path: str
|
||||
raw_path: bytes
|
||||
query_string: bytes
|
||||
root_path: str
|
||||
headers: Iterable[Tuple[bytes, bytes]]
|
||||
client: Optional[Tuple[str, int]]
|
||||
server: Optional[Tuple[str, Optional[int]]]
|
||||
subprotocols: Iterable[str]
|
||||
state: NotRequired[Dict[str, Any]]
|
||||
extensions: Optional[Dict[str, Dict[object, object]]]
|
||||
|
||||
|
||||
class LifespanScope(TypedDict):
|
||||
type: Literal["lifespan"]
|
||||
asgi: ASGIVersions
|
||||
state: NotRequired[Dict[str, Any]]
|
||||
|
||||
|
||||
class RSGI:
|
||||
class Scope(TypedDict):
|
||||
proto: Literal['http'] # = 'http'
|
||||
rsgi_version: str
|
||||
http_version: str
|
||||
server: str
|
||||
client: str
|
||||
scheme: str
|
||||
method: str
|
||||
path: str
|
||||
query_string: str
|
||||
headers: Mapping[str, str]
|
||||
authority: Optional[str]
|
||||
|
||||
|
||||
__all__ = [
|
||||
'HttpMethod',
|
||||
'HTTPScope',
|
||||
'LifespanScope',
|
||||
'RSGI',
|
||||
'ASGIVersions',
|
||||
'WebSocketScope',
|
||||
'NodeType',
|
||||
'StrOrStrings',
|
||||
'PathMatcherResult'
|
||||
]
|
||||
Reference in New Issue
Block a user