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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ compile_flags.txt

# Sphinx
docs/_build/
docs/generated/
Binary file added docs/_static/favicon.ico
Binary file not shown.
102 changes: 0 additions & 102 deletions docs/_static/favicon.svg

This file was deleted.

8 changes: 8 additions & 0 deletions docs/api.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
API Reference
=============

.. autosummary::
:toctree: generated
:recursive:

view
31 changes: 27 additions & 4 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,20 @@
# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration

extensions = ["sphinx.ext.intersphinx"]
extensions = [
"sphinx.ext.intersphinx",
'sphinx.ext.autodoc',
'sphinx.ext.autosummary',
]
autosummary_generate = True
add_module_names = False # Cleaner output

# This is the key part for making detailed pages:
autodoc_default_options = {
'members': True,
'undoc-members': True,
'show-inheritance': True,
}

exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
intersphinx_mapping = {"python": ("https://docs.python.org/3", None)}
Expand All @@ -25,8 +38,18 @@
html_theme = "shibuya"
html_theme_options = {
"accent_color": "blue",
# "light_logo": "_static/logo_black.svg",
# "dark_logo": "_static/logo_white.svg",
"light_logo": "_static/logo_black.svg",
"dark_logo": "_static/logo_white.svg",
"logo_target": "https://view.zintensity.dev",
"github_url": "https://github.com/ZeroIntensity/view.py",
"announcement": "view.py is currently in alpha and not considered ready for production",
}
html_static_path = ["_static"]
# html_favicon = "_static/favicon.svg"
html_favicon = "_static/favicon.ico"
html_context = {
"source_type": "github",
"source_user": "ZeroIntensity",
"source_repo": "view.py",
"source_version": "main",
"source_docs_path": "/docs/",
}
2 changes: 2 additions & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ Nothing here yet...
.. toctree::
:maxdepth: 2
:caption: Contents:

api
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# Requirements for Netlify
sphinx>=7.0
shibuya~=2025.8
shibuya>=2025
2 changes: 1 addition & 1 deletion src/view/core/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ def run(

This is a sort of magic function that's supposed to "just work". If
finer control over the server settings is desired, explicitly use the
server's API with the app's `asgi` or `wsgi` method.
server's API with the app's :meth:`asgi` or :meth:`wsgi` method.
"""
from view.run.servers import ServerSettings

Expand Down
10 changes: 5 additions & 5 deletions src/view/core/headers.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,8 @@ def with_new_value(self, key: str, value: str) -> HTTPHeaders:

def as_real_headers(headers: HeadersLike | None, /) -> HTTPHeaders:
"""
Convenience function for casting a "header-like object" (or `None`)
to a `MultiMap`.
Convenience function for casting a "header-like object" (or ``None``)
to a :class:`MultiMap`.
"""
if headers is None:
return HTTPHeaders()
Expand Down Expand Up @@ -111,7 +111,7 @@ def as_real_headers(headers: HeadersLike | None, /) -> HTTPHeaders:

def wsgi_to_headers(environ: Mapping[str, Any]) -> HTTPHeaders:
"""
Convert WSGI headers (from the `environ`) to a case-insensitive multi-map.
Convert WSGI headers (from the ``environ``) to a case-insensitive multi-map.
"""
values: list[tuple[LowerStr, str]] = []

Expand All @@ -126,7 +126,7 @@ def wsgi_to_headers(environ: Mapping[str, Any]) -> HTTPHeaders:
return HTTPHeaders(values)


def headers_to_wsgi(headers: HTTPHeaders) -> WSGIHeaders:
def headers_to_wsgi(headers: HTTPHeaders, /) -> WSGIHeaders:
"""
Convert a case-insensitive multi-map to a WSGI header iterable.
"""
Expand All @@ -148,7 +148,7 @@ def asgi_to_headers(headers: ASGIHeaders, /) -> HTTPHeaders:
lower_str = LowerStr(key.decode("utf-8"))
values.append((lower_str, value.decode("utf-8")))

return MultiMap(values)
return HTTPHeaders(values)


def headers_to_asgi(headers: HTTPHeaders, /) -> ASGIHeaders:
Expand Down
4 changes: 2 additions & 2 deletions src/view/core/multi_map.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def __init__(self, items: Iterable[tuple[KeyT, ValueT]] = ()) -> None:

def __getitem__(self, key: KeyT, /) -> ValueT:
"""
Get the first value if it exists, or else raise a `KeyError`.
Get the first value if it exists, or else raise a :exc:`KeyError`.
"""

return self._values[key][0]
Expand Down Expand Up @@ -127,7 +127,7 @@ def get_many(self, key: KeyT) -> Sequence[ValueT]:
def get_exactly_one(self, key: KeyT) -> ValueT:
"""
Get precisely one value for a key. If more than one value is present,
then this raises a `HasMultipleValuesError`.
then this raises a :exc:`HasMultipleValuesError`.
"""
value = self._values[key]
if len(value) != 1:
Expand Down
4 changes: 2 additions & 2 deletions src/view/core/request.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,12 +115,12 @@ class Request(BodyMixin):

method: Method
"""
The HTTP method of the request. See `Method`.
The HTTP method of the request. See :class:`Method`.
"""

headers: HTTPHeaders
"""
A "multi-dictionary" containing the request headers. This is `dict`-like,
A "multi-dictionary" containing the request headers. This is :class:`dict`-like,
but if a header has multiple values, it is represented by a list.
"""

Expand Down
9 changes: 5 additions & 4 deletions src/view/core/response.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ def from_file(
content_type: str | None = None,
) -> FileResponse:
"""
Generate a `FileResponse` from a file path.
Generate a :class:`FileResponse` from a file path.
"""
if __debug__ and not isinstance(chunk_size, int):
raise InvalidTypeError(chunk_size, int)
Expand Down Expand Up @@ -145,7 +145,8 @@ def from_content(
headers: HeadersLike | None = None,
) -> TextResponse[AnyStr]:
"""
Generate a `TextResponse` from either a `str` or `bytes` object.
Generate a :class:`TextResponse` from either a :class:`str` or
:class:`bytes` object.
"""

if __debug__ and not isinstance(content, (str, bytes)):
Expand Down Expand Up @@ -232,7 +233,7 @@ def _wrap_response_tuple(response: _ResponseTuple) -> Response:

def _wrap_response(response: ResponseLike, /) -> Response:
"""
Wrap a response from a view into a `Response` object.
Wrap a response from a view into a :class:`Response` object.
"""
logger.debug(f"Got response: {response!r}")
if isinstance(response, Response):
Expand Down Expand Up @@ -266,7 +267,7 @@ async def stream() -> AsyncGenerator[bytes]:
async def wrap_view_result(result: ViewResult, /) -> Response:
"""
Turn the raw result of a view, which might be a coroutine, into a usable
`Response` object.
:class:`Response` object.
"""
if isinstance(result, Awaitable):
result = await result
Expand Down
3 changes: 2 additions & 1 deletion src/view/dom/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,8 @@ def html_response(
function: HTMLView,
) -> RouteView:
"""
Return a `Response` object from a function returning HTML.
Return a :class:`~view.core.response.Response` object from a function
returning HTML.
"""

async def wrapper(*args: P.args, **kwargs: P.kwargs) -> Response:
Expand Down
4 changes: 2 additions & 2 deletions src/view/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ def __init__(self, *msg: str) -> None:
class InvalidTypeError(ViewError, TypeError):
"""
Something got a type that it didn't expect. For example, passing a
`str` object in a place where a `bytes` object was expected would raise
this error.
:class:`str` object in a place where a :class:`bytes` object was
expected would raise this error.

In order to fix this, please review the documentation of the function
you're attempting to call and ensure that you are passing it the correct
Expand Down
2 changes: 1 addition & 1 deletion src/view/javascript.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
@runtime_checkable
class SupportsJavaScript(Protocol):
"""
Protocol for objects that want to allow use in `as_javascript_expression()`.
Protocol for objects that want to allow use in :func:`as_javascript_expression`.
"""

def as_javascript(self) -> str:
Expand Down
3 changes: 3 additions & 0 deletions src/view/run/asgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ def asgi_for_app(app: BaseApp, /) -> ASGIProtocol:
"""
Generate an ASGI-compliant callable for a given app, allowing
it to be executed in an ASGI server.

Don't use this directly; prefer the :meth:`view.core.app.BaseApp.wsgi`
method instead.
"""

async def asgi(
Expand Down
14 changes: 7 additions & 7 deletions src/view/run/servers.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,15 @@ class ServerSettings:

def run_uvicorn(self) -> None:
"""
Run the app using the `uvicorn` library.
Run the app using the ``uvicorn`` library.
"""
import uvicorn

uvicorn.run(self.app.asgi(), host=self.host, port=self.port)

def run_hypercorn(self) -> None:
"""
Run the app using the `hypercorn` library.
Run the app using the ``hypercorn`` library.
"""
import asyncio

Expand All @@ -69,7 +69,7 @@ def run_hypercorn(self) -> None:

def run_daphne(self) -> None:
"""
Run the app using the `daphne` library.
Run the app using the ``daphne`` library.
"""
from daphne.endpoints import build_endpoint_description_strings
from daphne.server import Server
Expand All @@ -83,7 +83,7 @@ def run_daphne(self) -> None:

def run_gunicorn(self) -> None:
"""
Run the app using the `gunicorn` library.
Run the app using the ``gunicorn`` library.
"""
from gunicorn.app.base import BaseApplication

Expand Down Expand Up @@ -111,15 +111,15 @@ def load(self):

def run_werkzeug(self) -> None:
"""
Run the app using the `werkzeug` library.
Run the app using the ``werkzeug`` library.
"""
from werkzeug.serving import run_simple

run_simple(self.host, self.port, self.app.wsgi())

def run_wsgiref(self) -> None:
"""
Run the app using the built-in `wsgiref` module.
Run the app using the built-in :mod:`wsgiref` module.
"""
from wsgiref.simple_server import make_server

Expand All @@ -131,7 +131,7 @@ def run_app_on_any_server(self) -> None:
Run the app on the nearest available ASGI or WSGI server.

This will always succeed, as it will fall back to the standard
`wsgiref` module if no other server is installed.
:mod:`wsgiref` module if no other server is installed.
"""
servers: dict[str, StartServer] = {
"uvicorn": self.run_uvicorn,
Expand Down
Loading