Improve API docs (#2488)

This commit is contained in:
Adam Hopkins 2022-06-28 10:53:03 +03:00 committed by GitHub
parent b59da498cc
commit aba333bfb6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 151 additions and 27 deletions

View File

@ -24,7 +24,11 @@ import sanic
# -- General configuration ------------------------------------------------ # -- General configuration ------------------------------------------------
extensions = ["sphinx.ext.autodoc", "m2r2"] extensions = [
"sphinx.ext.autodoc",
"m2r2",
"enum_tools.autoenum",
]
templates_path = ["_templates"] templates_path = ["_templates"]

View File

@ -15,3 +15,19 @@ sanic.config
.. automodule:: sanic.config .. automodule:: sanic.config
:members: :members:
:show-inheritance: :show-inheritance:
sanic.application.constants
---------------------------
.. automodule:: sanic.application.constants
:exclude-members: StrEnum
:members:
:show-inheritance:
:inherited-members:
sanic.application.state
-----------------------
.. automodule:: sanic.application.state
:members:
:show-inheritance:

View File

@ -17,6 +17,14 @@ sanic.handlers
:show-inheritance: :show-inheritance:
sanic.headers
--------------
.. automodule:: sanic.headers
:members:
:show-inheritance:
sanic.request sanic.request
------------- -------------

View File

@ -16,10 +16,3 @@ sanic.server
:members: :members:
:show-inheritance: :show-inheritance:
sanic.worker
------------
.. automodule:: sanic.worker
:members:
:show-inheritance:

View File

@ -19,6 +19,7 @@ profile = "black"
[[tool.mypy.overrides]] [[tool.mypy.overrides]]
module = [ module = [
"httptools.*",
"trustme.*", "trustme.*",
"sanic_routing.*", "sanic_routing.*",
] ]

View File

@ -1369,7 +1369,10 @@ class Sanic(BaseSanic, RunnerMixin, metaclass=TouchUpMeta):
self.config.AUTO_RELOAD = value self.config.AUTO_RELOAD = value
@property @property
def state(self): def state(self) -> ApplicationState: # type: ignore
"""
:return: The application state
"""
return self._state return self._state
@property @property

View File

@ -1,5 +1,6 @@
from .constants import Stage from .constants import Stage
from .http1 import Http from .http1 import Http
from .http3 import Http3
__all__ = ("Http", "Stage") __all__ = ("Http", "Stage", "Http3")

View File

@ -30,7 +30,7 @@ HTTP_CONTINUE = b"HTTP/1.1 100 Continue\r\n\r\n"
class Http(Stream, metaclass=TouchUpMeta): class Http(Stream, metaclass=TouchUpMeta):
""" """
Internal helper for managing the HTTP request/response cycle Internal helper for managing the HTTP/1.1 request/response cycle
:raises ServerError: :raises ServerError:
:raises PayloadTooLarge: :raises PayloadTooLarge:

View File

@ -265,6 +265,10 @@ class WebTransportReceiver(Receiver): # noqa
class Http3: class Http3:
"""
Internal helper for managing the HTTP/3 request/response cycle
"""
HANDLER_PROPERTY_MAPPING = { HANDLER_PROPERTY_MAPPING = {
DataReceived: "stream_id", DataReceived: "stream_id",
HeadersReceived: "stream_id", HeadersReceived: "stream_id",

View File

@ -57,6 +57,9 @@ LOGGING_CONFIG_DEFAULTS: Dict[str, Any] = dict( # no cov
}, },
}, },
) )
"""
Defult logging configuration
"""
class Colors(str, Enum): # no cov class Colors(str, Enum): # no cov
@ -80,22 +83,22 @@ class VerbosityFilter(logging.Filter):
_verbosity_filter = VerbosityFilter() _verbosity_filter = VerbosityFilter()
logger = logging.getLogger("sanic.root") # no cov logger = logging.getLogger("sanic.root") # no cov
logger.addFilter(_verbosity_filter)
""" """
General Sanic logger General Sanic logger
""" """
logger.addFilter(_verbosity_filter)
error_logger = logging.getLogger("sanic.error") # no cov error_logger = logging.getLogger("sanic.error") # no cov
error_logger.addFilter(_verbosity_filter)
""" """
Logger used by Sanic for error logging Logger used by Sanic for error logging
""" """
error_logger.addFilter(_verbosity_filter)
access_logger = logging.getLogger("sanic.access") # no cov access_logger = logging.getLogger("sanic.access") # no cov
access_logger.addFilter(_verbosity_filter)
""" """
Logger used by Sanic for access logging Logger used by Sanic for access logging
""" """
access_logger.addFilter(_verbosity_filter)
def deprecation(message: str, version: float): # no cov def deprecation(message: str, version: float): # no cov

View File

@ -34,12 +34,12 @@ from http.cookies import SimpleCookie
from types import SimpleNamespace from types import SimpleNamespace
from urllib.parse import parse_qs, parse_qsl, unquote, urlunparse from urllib.parse import parse_qs, parse_qsl, unquote, urlunparse
from httptools import parse_url # type: ignore from httptools import parse_url
from httptools.parser.errors import HttpParserInvalidURLError # type: ignore from httptools.parser.errors import HttpParserInvalidURLError
from sanic.compat import CancelledErrors, Header from sanic.compat import CancelledErrors, Header
from sanic.constants import DEFAULT_HTTP_CONTENT_TYPE from sanic.constants import DEFAULT_HTTP_CONTENT_TYPE
from sanic.exceptions import BadRequest, BadURL, SanicException, ServerError from sanic.exceptions import BadRequest, BadURL, ServerError
from sanic.headers import ( from sanic.headers import (
AcceptContainer, AcceptContainer,
Options, Options,
@ -186,9 +186,27 @@ class Request:
@classmethod @classmethod
def get_current(cls) -> Request: def get_current(cls) -> Request:
"""
Retrieve the currrent request object
This implements `Context Variables
<https://docs.python.org/3/library/contextvars.html>`_
to allow for accessing the current request from anywhere.
Raises :exc:`sanic.exceptions.ServerError` if it is outside of
a request lifecycle.
.. code-block:: python
from sanic import Request
current_request = Request.get_current()
:return: the current :class:`sanic.request.Request`
"""
request = cls._current.get(None) request = cls._current.get(None)
if not request: if not request:
raise SanicException("No current request") raise ServerError("No current request")
return request return request
@classmethod @classmethod
@ -197,6 +215,12 @@ class Request:
@property @property
def stream_id(self): def stream_id(self):
"""
Access the HTTP/3 stream ID.
Raises :exc:`sanic.exceptions.ServerError` if it is not an
HTTP/3 request.
"""
if self.protocol.version is not HTTP.VERSION_3: if self.protocol.version is not HTTP.VERSION_3:
raise ServerError( raise ServerError(
"Stream ID is only a property of a HTTP/3 request" "Stream ID is only a property of a HTTP/3 request"
@ -319,7 +343,19 @@ class Request:
self.body = b"".join([data async for data in self.stream]) self.body = b"".join([data async for data in self.stream])
@property @property
def name(self): def name(self) -> Optional[str]:
"""
The route name
In the following pattern:
.. code-block::
<AppName>.[<BlueprintName>.]<HandlerName>
:return: Route name
:rtype: Optional[str]
"""
if self._name: if self._name:
return self._name return self._name
elif self.route: elif self.route:
@ -327,26 +363,47 @@ class Request:
return None return None
@property @property
def endpoint(self): def endpoint(self) -> Optional[str]:
"""
:return: Alias of :attr:`sanic.request.Request.name`
:rtype: Optional[str]
"""
return self.name return self.name
@property @property
def uri_template(self): def uri_template(self) -> Optional[str]:
return f"/{self.route.path}" """
:return: The defined URI template
:rtype: Optional[str]
"""
if self.route:
return f"/{self.route.path}"
return None
@property @property
def protocol(self): def protocol(self):
"""
:return: The HTTP protocol instance
"""
if not self._protocol: if not self._protocol:
self._protocol = self.transport.get_protocol() self._protocol = self.transport.get_protocol()
return self._protocol return self._protocol
@property @property
def raw_headers(self): def raw_headers(self) -> bytes:
"""
:return: The unparsed HTTP headers
:rtype: bytes
"""
_, headers = self.head.split(b"\r\n", 1) _, headers = self.head.split(b"\r\n", 1)
return bytes(headers) return bytes(headers)
@property @property
def request_line(self): def request_line(self) -> bytes:
"""
:return: The first line of a HTTP request
:rtype: bytes
"""
reqline, _ = self.head.split(b"\r\n", 1) reqline, _ = self.head.split(b"\r\n", 1)
return bytes(reqline) return bytes(reqline)
@ -395,7 +452,11 @@ class Request:
return self._id # type: ignore return self._id # type: ignore
@property @property
def json(self): def json(self) -> Any:
"""
:return: The request body parsed as JSON
:rtype: Any
"""
if self.parsed_json is None: if self.parsed_json is None:
self.load_json() self.load_json()
@ -413,6 +474,10 @@ class Request:
@property @property
def accept(self) -> AcceptContainer: def accept(self) -> AcceptContainer:
"""
:return: The ``Accept`` header parsed
:rtype: AcceptContainer
"""
if self.parsed_accept is None: if self.parsed_accept is None:
accept_header = self.headers.getone("accept", "") accept_header = self.headers.getone("accept", "")
self.parsed_accept = parse_accept(accept_header) self.parsed_accept = parse_accept(accept_header)
@ -458,6 +523,15 @@ class Request:
def get_form( def get_form(
self, keep_blank_values: bool = False self, keep_blank_values: bool = False
) -> Optional[RequestParameters]: ) -> Optional[RequestParameters]:
"""
Method to extract and parse the form data from a request.
:param keep_blank_values:
Whether to discard blank values from the form data
:type keep_blank_values: bool
:return: the parsed form data
:rtype: Optional[RequestParameters]
"""
self.parsed_form = RequestParameters() self.parsed_form = RequestParameters()
self.parsed_files = RequestParameters() self.parsed_files = RequestParameters()
content_type = self.headers.getone( content_type = self.headers.getone(
@ -487,6 +561,9 @@ class Request:
@property @property
def form(self): def form(self):
"""
:return: The request body parsed as form data
"""
if self.parsed_form is None: if self.parsed_form is None:
self.get_form() self.get_form()
@ -494,6 +571,9 @@ class Request:
@property @property
def files(self): def files(self):
"""
:return: The request body parsed as uploaded files
"""
if self.parsed_files is None: if self.parsed_files is None:
self.form # compute form to get files self.form # compute form to get files
@ -507,8 +587,8 @@ class Request:
errors: str = "replace", errors: str = "replace",
) -> RequestParameters: ) -> RequestParameters:
""" """
Method to parse `query_string` using `urllib.parse.parse_qs`. Method to parse ``query_string`` using ``urllib.parse.parse_qs``.
This methods is used by `args` property. This methods is used by ``args`` property.
Can be used directly if you need to change default parameters. Can be used directly if you need to change default parameters.
:param keep_blank_values: :param keep_blank_values:
@ -557,6 +637,10 @@ class Request:
] ]
args = property(get_args) args = property(get_args)
"""
Convenience property to access :meth:`Request.get_args` with
default values.
"""
def get_query_args( def get_query_args(
self, self,
@ -676,6 +760,9 @@ class Request:
@property @property
def socket(self): def socket(self):
"""
:return: Information about the connected socket if available
"""
return self.conn_info.peername if self.conn_info else (None, None) return self.conn_info.peername if self.conn_info else (None, None)
@property @property
@ -688,6 +775,9 @@ class Request:
@property @property
def network_paths(self): def network_paths(self):
"""
Access the network paths if available
"""
return self.conn_info.network_paths return self.conn_info.network_paths
# Proxy properties (using SERVER_NAME/forwarded/request/transport info) # Proxy properties (using SERVER_NAME/forwarded/request/transport info)

View File

@ -122,6 +122,7 @@ docs_require = [
"docutils", "docutils",
"pygments", "pygments",
"m2r2", "m2r2",
"enum-tools[sphinx]",
"mistune<2.0.0", "mistune<2.0.0",
] ]