Skip to content

Commit

Permalink
Improve API docs (#2488)
Browse files Browse the repository at this point in the history
  • Loading branch information
ahopkins committed Jun 28, 2022
1 parent b59da49 commit aba333b
Show file tree
Hide file tree
Showing 12 changed files with 151 additions and 27 deletions.
6 changes: 5 additions & 1 deletion docs/conf.py
Expand Up @@ -24,7 +24,11 @@

# -- General configuration ------------------------------------------------

extensions = ["sphinx.ext.autodoc", "m2r2"]
extensions = [
"sphinx.ext.autodoc",
"m2r2",
"enum_tools.autoenum",
]

templates_path = ["_templates"]

Expand Down
16 changes: 16 additions & 0 deletions docs/sanic/api/app.rst
Expand Up @@ -15,3 +15,19 @@ sanic.config
.. automodule:: sanic.config
:members:
: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:
8 changes: 8 additions & 0 deletions docs/sanic/api/core.rst
Expand Up @@ -17,6 +17,14 @@ sanic.handlers
:show-inheritance:


sanic.headers
--------------

.. automodule:: sanic.headers
:members:
:show-inheritance:


sanic.request
-------------

Expand Down
7 changes: 0 additions & 7 deletions docs/sanic/api/server.rst
Expand Up @@ -16,10 +16,3 @@ sanic.server
:members:
:show-inheritance:


sanic.worker
------------

.. automodule:: sanic.worker
:members:
:show-inheritance:
1 change: 1 addition & 0 deletions pyproject.toml
Expand Up @@ -19,6 +19,7 @@ profile = "black"

[[tool.mypy.overrides]]
module = [
"httptools.*",
"trustme.*",
"sanic_routing.*",
]
Expand Down
5 changes: 4 additions & 1 deletion sanic/app.py
Expand Up @@ -1369,7 +1369,10 @@ def auto_reload(self, value: bool):
self.config.AUTO_RELOAD = value

@property
def state(self):
def state(self) -> ApplicationState: # type: ignore
"""
:return: The application state
"""
return self._state

@property
Expand Down
3 changes: 2 additions & 1 deletion sanic/http/__init__.py
@@ -1,5 +1,6 @@
from .constants import Stage
from .http1 import Http
from .http3 import Http3


__all__ = ("Http", "Stage")
__all__ = ("Http", "Stage", "Http3")
2 changes: 1 addition & 1 deletion sanic/http/http1.py
Expand Up @@ -30,7 +30,7 @@

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 PayloadTooLarge:
Expand Down
4 changes: 4 additions & 0 deletions sanic/http/http3.py
Expand Up @@ -265,6 +265,10 @@ async def run(self):


class Http3:
"""
Internal helper for managing the HTTP/3 request/response cycle
"""

HANDLER_PROPERTY_MAPPING = {
DataReceived: "stream_id",
HeadersReceived: "stream_id",
Expand Down
9 changes: 6 additions & 3 deletions sanic/log.py
Expand Up @@ -57,6 +57,9 @@
},
},
)
"""
Defult logging configuration
"""


class Colors(str, Enum): # no cov
Expand All @@ -80,22 +83,22 @@ def filter(self, record: logging.LogRecord) -> bool:
_verbosity_filter = VerbosityFilter()

logger = logging.getLogger("sanic.root") # no cov
logger.addFilter(_verbosity_filter)
"""
General Sanic logger
"""
logger.addFilter(_verbosity_filter)

error_logger = logging.getLogger("sanic.error") # no cov
error_logger.addFilter(_verbosity_filter)
"""
Logger used by Sanic for error logging
"""
error_logger.addFilter(_verbosity_filter)

access_logger = logging.getLogger("sanic.access") # no cov
access_logger.addFilter(_verbosity_filter)
"""
Logger used by Sanic for access logging
"""
access_logger.addFilter(_verbosity_filter)


def deprecation(message: str, version: float): # no cov
Expand Down
116 changes: 103 additions & 13 deletions sanic/request.py
Expand Up @@ -34,12 +34,12 @@
from types import SimpleNamespace
from urllib.parse import parse_qs, parse_qsl, unquote, urlunparse

from httptools import parse_url # type: ignore
from httptools.parser.errors import HttpParserInvalidURLError # type: ignore
from httptools import parse_url
from httptools.parser.errors import HttpParserInvalidURLError

from sanic.compat import CancelledErrors, Header
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 (
AcceptContainer,
Options,
Expand Down Expand Up @@ -186,9 +186,27 @@ def __repr__(self):

@classmethod
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)
if not request:
raise SanicException("No current request")
raise ServerError("No current request")
return request

@classmethod
Expand All @@ -197,6 +215,12 @@ def generate_id(*_):

@property
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:
raise ServerError(
"Stream ID is only a property of a HTTP/3 request"
Expand Down Expand Up @@ -319,34 +343,67 @@ async def receive_body(self):
self.body = b"".join([data async for data in self.stream])

@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:
return self._name
elif self.route:
return self.route.name
return None

@property
def endpoint(self):
def endpoint(self) -> Optional[str]:
"""
:return: Alias of :attr:`sanic.request.Request.name`
:rtype: Optional[str]
"""
return self.name

@property
def uri_template(self):
return f"/{self.route.path}"
def uri_template(self) -> Optional[str]:
"""
:return: The defined URI template
:rtype: Optional[str]
"""
if self.route:
return f"/{self.route.path}"
return None

@property
def protocol(self):
"""
:return: The HTTP protocol instance
"""
if not self._protocol:
self._protocol = self.transport.get_protocol()
return self._protocol

@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)
return bytes(headers)

@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)
return bytes(reqline)

Expand Down Expand Up @@ -395,7 +452,11 @@ def generate_id(self):
return self._id # type: ignore

@property
def json(self):
def json(self) -> Any:
"""
:return: The request body parsed as JSON
:rtype: Any
"""
if self.parsed_json is None:
self.load_json()

Expand All @@ -413,6 +474,10 @@ def load_json(self, loads=json_loads):

@property
def accept(self) -> AcceptContainer:
"""
:return: The ``Accept`` header parsed
:rtype: AcceptContainer
"""
if self.parsed_accept is None:
accept_header = self.headers.getone("accept", "")
self.parsed_accept = parse_accept(accept_header)
Expand Down Expand Up @@ -458,6 +523,15 @@ def credentials(self) -> Optional[Credentials]:
def get_form(
self, keep_blank_values: bool = False
) -> 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_files = RequestParameters()
content_type = self.headers.getone(
Expand Down Expand Up @@ -487,13 +561,19 @@ def get_form(

@property
def form(self):
"""
:return: The request body parsed as form data
"""
if self.parsed_form is None:
self.get_form()

return self.parsed_form

@property
def files(self):
"""
:return: The request body parsed as uploaded files
"""
if self.parsed_files is None:
self.form # compute form to get files

Expand All @@ -507,8 +587,8 @@ def get_args(
errors: str = "replace",
) -> RequestParameters:
"""
Method to parse `query_string` using `urllib.parse.parse_qs`.
This methods is used by `args` property.
Method to parse ``query_string`` using ``urllib.parse.parse_qs``.
This methods is used by ``args`` property.
Can be used directly if you need to change default parameters.
:param keep_blank_values:
Expand Down Expand Up @@ -557,6 +637,10 @@ def get_args(
]

args = property(get_args)
"""
Convenience property to access :meth:`Request.get_args` with
default values.
"""

def get_query_args(
self,
Expand Down Expand Up @@ -676,6 +760,9 @@ def port(self) -> int:

@property
def socket(self):
"""
:return: Information about the connected socket if available
"""
return self.conn_info.peername if self.conn_info else (None, None)

@property
Expand All @@ -688,6 +775,9 @@ def path(self) -> str:

@property
def network_paths(self):
"""
Access the network paths if available
"""
return self.conn_info.network_paths

# Proxy properties (using SERVER_NAME/forwarded/request/transport info)
Expand Down
1 change: 1 addition & 0 deletions setup.py
Expand Up @@ -122,6 +122,7 @@ def open_local(paths, mode="r", encoding="utf8"):
"docutils",
"pygments",
"m2r2",
"enum-tools[sphinx]",
"mistune<2.0.0",
]

Expand Down

0 comments on commit aba333b

Please sign in to comment.