diff --git a/README.md b/README.md index 9df88d5f..53d38e0f 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,21 @@ # Responder: a familiar HTTP Service Framework for Python -[![Build Status](https://github.com/kennethreitz/responder/actions/workflows/test.yaml/badge.svg)](https://github.com/kennethreitz/responder/actions/workflows/test.yaml) +[![ci-tests](https://github.com/kennethreitz/responder/actions/workflows/test.yaml/badge.svg)](https://github.com/kennethreitz/responder/actions/workflows/test.yaml) +[![ci-docs](https://github.com/kennethreitz/responder/actions/workflows/docs.yaml/badge.svg)](https://github.com/kennethreitz/responder/actions/workflows/docs.yaml) [![Documentation Status](https://github.com/kennethreitz/responder/actions/workflows/pages/pages-build-deployment/badge.svg)](https://responder.kennethreitz.org/) -[![image](https://img.shields.io/pypi/v/responder.svg)](https://pypi.org/project/responder/) -[![image](https://img.shields.io/pypi/l/responder.svg)](https://pypi.org/project/responder/) -[![image](https://img.shields.io/pypi/pyversions/responder.svg)](https://pypi.org/project/responder/) -[![image](https://img.shields.io/github/contributors/kennethreitz/responder.svg)](https://github.com/kennethreitz/responder/graphs/contributors) -[![PyPI Downloads](https://pepy.tech/badge/responder/month)](https://pepy.tech/project/responder/) -[![Status](https://img.shields.io/pypi/status/responder.svg)](https://pypi.org/project/responder/) -[![License](https://img.shields.io/pypi/l/responder.svg)](https://pypi.org/project/responder/) +[![version](https://img.shields.io/pypi/v/responder.svg)](https://pypi.org/project/responder/) +[![license](https://img.shields.io/pypi/l/responder.svg)](https://pypi.org/project/responder/) +[![python-versions](https://img.shields.io/pypi/pyversions/responder.svg)](https://pypi.org/project/responder/) +[![downloads](https://static.pepy.tech/badge/responder/month)](https://pepy.tech/project/responder) +[![contributors](https://img.shields.io/github/contributors/kennethreitz/responder.svg)](https://github.com/kennethreitz/responder/graphs/contributors) +[![status](https://img.shields.io/pypi/status/responder.svg)](https://pypi.org/project/responder/) [![](https://farm2.staticflickr.com/1959/43750081370_a4e20752de_o_d.png)](https://responder.readthedocs.io) Powered by [Starlette](https://www.starlette.io/). That `async` declaration is optional. [View documentation](https://responder.readthedocs.io). -This gets you a ASGI app, with a production static files server pre-installed, jinja2 +This gets you a ASGI app, with a production static files server pre-installed, Jinja templating (without additional imports), and a production webserver based on uvloop, serving up requests with gzip compression automatically. diff --git a/docs/source/conf.py b/docs/source/conf.py index 09451a46..7c4b1df9 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -211,6 +211,12 @@ # -- Extension configuration ------------------------------------------------- +# -- Options for link checker ---------------------------------------------- +linkcheck_ignore = [ + # Feldroy.com links are ignored because it blocks GHA. + r"https://www.feldroy.com/.*", +] + # -- Options for intersphinx extension --------------------------------------- # Example configuration for intersphinx: refer to the Python standard library. diff --git a/docs/source/index.rst b/docs/source/index.rst index 88683e46..87b176a7 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -6,19 +6,23 @@ A familiar HTTP Service Framework ================================= -|Build Status| |image1| |image2| |image3| |image4| |image5| +|ci-tests| |version| |license| |python-versions| |downloads| |contributors| |say-thanks| -.. |Build Status| image:: https://github.com/kennethreitz/responder/actions/workflows/test.yaml/badge.svg +.. |ci-tests| image:: https://github.com/kennethreitz/responder/actions/workflows/test.yaml/badge.svg :target: https://github.com/kennethreitz/responder/actions/workflows/test.yaml -.. |image1| image:: https://img.shields.io/pypi/v/responder.svg +.. |ci-docs| image:: https://github.com/kennethreitz/responder/actions/workflows/docs.yaml/badge.svg + :target: https://github.com/kennethreitz/responder/actions/workflows/docs.yaml +.. |version| image:: https://img.shields.io/pypi/v/responder.svg :target: https://pypi.org/project/responder/ -.. |image2| image:: https://img.shields.io/pypi/l/responder.svg +.. |license| image:: https://img.shields.io/pypi/l/responder.svg :target: https://pypi.org/project/responder/ -.. |image3| image:: https://img.shields.io/pypi/pyversions/responder.svg +.. |python-versions| image:: https://img.shields.io/pypi/pyversions/responder.svg :target: https://pypi.org/project/responder/ -.. |image4| image:: https://img.shields.io/github/contributors/kennethreitz/responder.svg +.. |downloads| image:: https://static.pepy.tech/badge/responder/month + :target: https://www.pepy.tech/projects/responder +.. |contributors| image:: https://img.shields.io/github/contributors/kennethreitz/responder.svg :target: https://github.com/kennethreitz/responder/graphs/contributors -.. |image5| image:: https://img.shields.io/badge/Say%20Thanks-!-1EAEDB.svg +.. |say-thanks| image:: https://img.shields.io/badge/Say%20Thanks-!-1EAEDB.svg :target: https://saythanks.io/to/kennethreitz .. code:: python @@ -34,23 +38,22 @@ A familiar HTTP Service Framework if __name__ == '__main__': api.run() -Powered by `Starlette `_. That ``async`` declaration is optional. +Powered by `Starlette`_. That ``async`` declaration is optional. -This gets you a ASGI app, with a production static files server -(`WhiteNoise `_) -pre-installed, jinja2 templating (without additional imports), and a -production webserver based on uvloop, serving up requests with -automatic gzip compression. +This program provides an `ASGI`_ application, with production-ready components +like the `uvicorn`_ webserver based on `uvloop`_, the static files server +`WhiteNoise`_, and the `Jinja`_ templating library pre-installed. Features -------- - A pleasant API, with a single import statement. - Class-based views without inheritance. -- `ASGI `_ framework, the future of Python web services. +- `ASGI`_, the future of Python web services. +- Asynchronous Python frameworks and applications. - WebSocket support! - The ability to mount any ASGI / WSGI app at a subroute. -- `f-string syntax `_ route declaration. +- `f-string syntax`_ route declaration. - Mutable response object, passed into each view. No need to return anything. - Background tasks, spawned off in a ``ThreadPoolExecutor``. - GraphQL (with *GraphiQL*) support! @@ -61,8 +64,7 @@ Testimonials ------------ “Pleasantly very taken with python-responder. - `@kennethreitz `_ at his absolute - best.” + `@kennethreitz`_ at his absolute best.” —Rudraksh M.K. @@ -82,9 +84,6 @@ Testimonials — Danny Greenfield, author of `Two Scoops of Django`_ -.. _Django REST Framework: https://www.django-rest-framework.org/ -.. _Two Scoops of Django: https://www.feldroy.com/two-scoops-press#two-scoops-of-django - User Guides ----------- @@ -130,7 +129,7 @@ Ideas - Automatic gzipped-responses. - In addition to Falcon's ``on_get``, ``on_post``, etc methods, Responder features an ``on_request`` method, which gets called on every type of request, much like Requests. - A production static files server is built-in. -- `Uvicorn `_ is built-in as a production web server. I would have chosen Gunicorn, but it doesn't run on Windows. Plus, Uvicorn serves well to protect against `slowloris `_ attacks, making nginx unnecessary in production. +- `uvicorn`_ is built-in as a production web server. I would have chosen Gunicorn, but it doesn't run on Windows. Plus, uvicorn serves well to protect against `Slowloris`_ attacks, making Nginx unnecessary in production. - GraphQL support, via Graphene. The goal here is to have any GraphQL query exposable at any route, magically. @@ -140,3 +139,16 @@ Indices and tables * :ref:`genindex` * :ref:`modindex` * :ref:`search` + + +.. _@kennethreitz: https://x.com/kennethreitz +.. _ASGI: https://en.wikipedia.org/wiki/Asynchronous_Server_Gateway_Interface +.. _Django REST Framework: https://www.django-rest-framework.org/ +.. _f-string syntax: https://docs.python.org/3/whatsnew/3.6.html#pep-498-formatted-string-literals +.. _Jinja: https://jinja.palletsprojects.com/en/stable/ +.. _Slowloris: https://en.wikipedia.org/wiki/Slowloris_(computer_security) +.. _Starlette: https://www.starlette.io/ +.. _Two Scoops of Django: https://www.feldroy.com/two-scoops-press#two-scoops-of-django +.. _uvicorn: https://www.uvicorn.org/ +.. _uvloop: https://uvloop.readthedocs.io/ +.. _WhiteNoise: https://whitenoise.readthedocs.io/en/latest/ diff --git a/docs/source/quickstart.rst b/docs/source/quickstart.rst index 1ba6dd06..358ac335 100644 --- a/docs/source/quickstart.rst +++ b/docs/source/quickstart.rst @@ -73,7 +73,7 @@ If the client requests YAML instead (with a header of ``Accept: application/x-ya Rendering a Template -------------------- -Responder provides a built-in light `jinja2 `_ wrapper ``templates.Templates`` +Responder provides a built-in light `Jinja`_ wrapper ``templates.Templates`` Usage:: @@ -175,3 +175,6 @@ You can send a file easily with requests:: r = requests.post('http://127.0.0.1:8210/file', files=data) print(r.text) + + +.. _Jinja: https://jinja.palletsprojects.com/en/stable/ diff --git a/responder/api.py b/responder/api.py index a713da2a..1dc5a8ea 100644 --- a/responder/api.py +++ b/responder/api.py @@ -149,8 +149,11 @@ def add_middleware(self, middleware_cls, **middleware_config): self.app = middleware_cls(self.app, **middleware_config) def schema(self, name, **options): - """Decorator for creating new routes around function and class definitions. + """ + Decorator for creating new routes around function and class definitions. + Usage:: + from marshmallow import Schema, fields @api.schema("Pet") class PetSchema(Schema): @@ -224,7 +227,9 @@ async def _static_response(self, req, resp): def redirect( self, resp, location, *, set_text=True, status_code=status_codes.HTTP_301 ): - """Redirects a given response to a given location. + """ + Redirects a given response to a given location. + :param resp: The Response to mutate. :param location: The location of the redirect. :param set_text: If ``True``, sets the Redirect body content automatically. @@ -311,29 +316,37 @@ def url_for(self, endpoint, **params): return self.router.url_for(endpoint, **params) def template(self, filename, *args, **kwargs): - """Renders the given `jinja2 `_ template, with provided values supplied. + r""" + Render the given Jinja2 template file, with provided values supplied. - Note: The current ``api`` instance is by default passed into the view. This is set in the dict ``api.jinja_values_base``. + Note: The current ``api`` instance is by default passed into the view. + This is set in the dict ``api.jinja_values_base``. :param filename: The filename of the jinja2 template, in ``templates_dir``. - :param *args: Data to pass into the template. - :param *kwargs: Date to pass into the template. - """ # noqa: E501 + :param \*args: Data to pass into the template. + :param \*\*kwargs: Date to pass into the template. + """ return self.templates.render(filename, *args, **kwargs) def template_string(self, source, *args, **kwargs): - """Renders the given `jinja2 `_ template string, with provided values supplied. - Note: The current ``api`` instance is by default passed into the view. This is set in the dict ``api.jinja_values_base``. - :param source: The template to use. - :param *args: Data to pass into the template. - :param **kwargs: Data to pass into the template. - """ # noqa: E501 + r""" + Render the given Jinja2 template string, with provided values supplied. + + Note: The current ``api`` instance is by default passed into the view. + This is set in the dict ``api.jinja_values_base``. + + :param source: The template to use, a Jinja2 template string. + :param \*args: Data to pass into the template. + :param \*\*kwargs: Data to pass into the template. + """ return self.templates.render_string(source, *args, **kwargs) def serve(self, *, address=None, port=None, debug=False, **options): - """Runs the application with uvicorn. If the ``PORT`` environment - variable is set, requests will be served on that port automatically to all - known hosts. + """ + Run the application with uvicorn. + + If the ``PORT`` environment variable is set, requests will be served on that port + automatically to all known hosts. :param address: The address to bind to. :param port: The port to bind to. If none is provided, one will be selected at random.