LTS v21.12 Deprecations (#2306)
Co-authored-by: Néstor Pérez <25409753+prryplatypus@users.noreply.github.com>
This commit is contained in:
parent
98ce4bdeb2
commit
8c07e388cd
192
CHANGELOG.rst
192
CHANGELOG.rst
|
@ -1,12 +1,12 @@
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
From v21.9, CHANGELOG files are maintained in ``./docs/sanic/releases``
|
CHANGELOG files are maintained in ``./docs/sanic/releases``. To view the full CHANGELOG, please visit https://sanic.readthedocs.io/en/stable/sanic/changelog.html.
|
||||||
|
|
||||||
|
|
||||||
Version 21.6.1
|
Version 21.6.1
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
Bugfixes
|
**Bugfixes**
|
||||||
********
|
|
||||||
|
|
||||||
* `#2178 <https://github.com/sanic-org/sanic/pull/2178>`_
|
* `#2178 <https://github.com/sanic-org/sanic/pull/2178>`_
|
||||||
Update sanic-routing to allow for better splitting of complex URI templates
|
Update sanic-routing to allow for better splitting of complex URI templates
|
||||||
|
@ -20,8 +20,7 @@ Bugfixes
|
||||||
Version 21.6.0
|
Version 21.6.0
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
Features
|
**Features**
|
||||||
********
|
|
||||||
|
|
||||||
* `#2094 <https://github.com/sanic-org/sanic/pull/2094>`_
|
* `#2094 <https://github.com/sanic-org/sanic/pull/2094>`_
|
||||||
Add ``response.eof()`` method for closing a stream in a handler
|
Add ``response.eof()`` method for closing a stream in a handler
|
||||||
|
@ -68,8 +67,7 @@ Features
|
||||||
* `#2170 <https://github.com/sanic-org/sanic/pull/2170>`_
|
* `#2170 <https://github.com/sanic-org/sanic/pull/2170>`_
|
||||||
Additional methods for attaching ``HTTPMethodView``
|
Additional methods for attaching ``HTTPMethodView``
|
||||||
|
|
||||||
Bugfixes
|
**Bugfixes**
|
||||||
********
|
|
||||||
|
|
||||||
* `#2091 <https://github.com/sanic-org/sanic/pull/2091>`_
|
* `#2091 <https://github.com/sanic-org/sanic/pull/2091>`_
|
||||||
Fix ``UserWarning`` in ASGI mode for missing ``__slots__``
|
Fix ``UserWarning`` in ASGI mode for missing ``__slots__``
|
||||||
|
@ -85,8 +83,7 @@ Bugfixes
|
||||||
Fix issue where Blueprint exception handlers do not consistently route to proper handler
|
Fix issue where Blueprint exception handlers do not consistently route to proper handler
|
||||||
|
|
||||||
|
|
||||||
Deprecations and Removals
|
**Deprecations and Removals**
|
||||||
*************************
|
|
||||||
|
|
||||||
* `#2156 <https://github.com/sanic-org/sanic/pull/2156>`_
|
* `#2156 <https://github.com/sanic-org/sanic/pull/2156>`_
|
||||||
Remove config value ``REQUEST_BUFFER_QUEUE_SIZE``
|
Remove config value ``REQUEST_BUFFER_QUEUE_SIZE``
|
||||||
|
@ -95,14 +92,12 @@ Deprecations and Removals
|
||||||
* `#2172 <https://github.com/sanic-org/sanic/pull/2170>`_
|
* `#2172 <https://github.com/sanic-org/sanic/pull/2170>`_
|
||||||
Deprecate StreamingHTTPResponse
|
Deprecate StreamingHTTPResponse
|
||||||
|
|
||||||
Developer infrastructure
|
**Developer infrastructure**
|
||||||
************************
|
|
||||||
|
|
||||||
* `#2149 <https://github.com/sanic-org/sanic/pull/2149>`_
|
* `#2149 <https://github.com/sanic-org/sanic/pull/2149>`_
|
||||||
Remove Travis CI in favor of GitHub Actions
|
Remove Travis CI in favor of GitHub Actions
|
||||||
|
|
||||||
Improved Documentation
|
**Improved Documentation**
|
||||||
**********************
|
|
||||||
|
|
||||||
* `#2164 <https://github.com/sanic-org/sanic/pull/2164>`_
|
* `#2164 <https://github.com/sanic-org/sanic/pull/2164>`_
|
||||||
Fix typo in documentation
|
Fix typo in documentation
|
||||||
|
@ -112,8 +107,7 @@ Improved Documentation
|
||||||
Version 21.3.2
|
Version 21.3.2
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
Bugfixes
|
**Bugfixes**
|
||||||
********
|
|
||||||
|
|
||||||
* `#2081 <https://github.com/sanic-org/sanic/pull/2081>`_
|
* `#2081 <https://github.com/sanic-org/sanic/pull/2081>`_
|
||||||
Disable response timeout on websocket connections
|
Disable response timeout on websocket connections
|
||||||
|
@ -124,8 +118,7 @@ Bugfixes
|
||||||
Version 21.3.1
|
Version 21.3.1
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
Bugfixes
|
**Bugfixes**
|
||||||
********
|
|
||||||
|
|
||||||
* `#2076 <https://github.com/sanic-org/sanic/pull/2076>`_
|
* `#2076 <https://github.com/sanic-org/sanic/pull/2076>`_
|
||||||
Static files inside subfolders are not accessible (404)
|
Static files inside subfolders are not accessible (404)
|
||||||
|
@ -135,8 +128,7 @@ Version 21.3.0
|
||||||
|
|
||||||
`Release Notes <https://sanicframework.org/en/guide/release-notes/v21.3.html>`_
|
`Release Notes <https://sanicframework.org/en/guide/release-notes/v21.3.html>`_
|
||||||
|
|
||||||
Features
|
**Features**
|
||||||
********
|
|
||||||
|
|
||||||
*
|
*
|
||||||
`#1876 <https://github.com/sanic-org/sanic/pull/1876>`_
|
`#1876 <https://github.com/sanic-org/sanic/pull/1876>`_
|
||||||
|
@ -189,8 +181,7 @@ Features
|
||||||
`#2063 <https://github.com/sanic-org/sanic/pull/2063>`_
|
`#2063 <https://github.com/sanic-org/sanic/pull/2063>`_
|
||||||
App and connection level context objects
|
App and connection level context objects
|
||||||
|
|
||||||
Bugfixes and issues resolved
|
**Bugfixes**
|
||||||
****************************
|
|
||||||
|
|
||||||
* Resolve `#1420 <https://github.com/sanic-org/sanic/pull/1420>`_
|
* Resolve `#1420 <https://github.com/sanic-org/sanic/pull/1420>`_
|
||||||
``url_for`` where ``strict_slashes`` are on for a path ending in ``/``
|
``url_for`` where ``strict_slashes`` are on for a path ending in ``/``
|
||||||
|
@ -220,8 +211,7 @@ Bugfixes and issues resolved
|
||||||
`#2001 <https://github.com/sanic-org/sanic/pull/2001>`_
|
`#2001 <https://github.com/sanic-org/sanic/pull/2001>`_
|
||||||
Raise ValueError when cookie max-age is not an integer
|
Raise ValueError when cookie max-age is not an integer
|
||||||
|
|
||||||
Deprecations and Removals
|
**Deprecations and Removals**
|
||||||
*************************
|
|
||||||
|
|
||||||
*
|
*
|
||||||
`#2007 <https://github.com/sanic-org/sanic/pull/2007>`_
|
`#2007 <https://github.com/sanic-org/sanic/pull/2007>`_
|
||||||
|
@ -240,8 +230,7 @@ Deprecations and Removals
|
||||||
* ``Request.endpoint`` deprecated in favor of ``Request.name``
|
* ``Request.endpoint`` deprecated in favor of ``Request.name``
|
||||||
* handler type name prefixes removed (static, websocket, etc)
|
* handler type name prefixes removed (static, websocket, etc)
|
||||||
|
|
||||||
Developer infrastructure
|
**Developer infrastructure**
|
||||||
************************
|
|
||||||
|
|
||||||
*
|
*
|
||||||
`#1995 <https://github.com/sanic-org/sanic/pull/1995>`_
|
`#1995 <https://github.com/sanic-org/sanic/pull/1995>`_
|
||||||
|
@ -259,8 +248,7 @@ Developer infrastructure
|
||||||
`#2049 <https://github.com/sanic-org/sanic/pull/2049>`_
|
`#2049 <https://github.com/sanic-org/sanic/pull/2049>`_
|
||||||
Updated setup.py to use ``find_packages``
|
Updated setup.py to use ``find_packages``
|
||||||
|
|
||||||
Improved Documentation
|
**Improved Documentation**
|
||||||
**********************
|
|
||||||
|
|
||||||
*
|
*
|
||||||
`#1218 <https://github.com/sanic-org/sanic/pull/1218>`_
|
`#1218 <https://github.com/sanic-org/sanic/pull/1218>`_
|
||||||
|
@ -282,8 +270,7 @@ Improved Documentation
|
||||||
`#2052 <https://github.com/sanic-org/sanic/pull/2052>`_
|
`#2052 <https://github.com/sanic-org/sanic/pull/2052>`_
|
||||||
Fix some examples and docs
|
Fix some examples and docs
|
||||||
|
|
||||||
Miscellaneous
|
**Miscellaneous**
|
||||||
*************
|
|
||||||
|
|
||||||
* ``Request.route`` property
|
* ``Request.route`` property
|
||||||
* Better websocket subprotocols support
|
* Better websocket subprotocols support
|
||||||
|
@ -329,8 +316,7 @@ Miscellaneous
|
||||||
Version 20.12.3
|
Version 20.12.3
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
Bugfixes
|
**Bugfixes**
|
||||||
********
|
|
||||||
|
|
||||||
*
|
*
|
||||||
`#2021 <https://github.com/sanic-org/sanic/pull/2021>`_
|
`#2021 <https://github.com/sanic-org/sanic/pull/2021>`_
|
||||||
|
@ -339,8 +325,7 @@ Bugfixes
|
||||||
Version 20.12.2
|
Version 20.12.2
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
Dependencies
|
**Dependencies**
|
||||||
************
|
|
||||||
|
|
||||||
*
|
*
|
||||||
`#2026 <https://github.com/sanic-org/sanic/pull/2026>`_
|
`#2026 <https://github.com/sanic-org/sanic/pull/2026>`_
|
||||||
|
@ -353,8 +338,7 @@ Dependencies
|
||||||
Version 19.12.5
|
Version 19.12.5
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
Dependencies
|
**Dependencies**
|
||||||
************
|
|
||||||
|
|
||||||
*
|
*
|
||||||
`#2025 <https://github.com/sanic-org/sanic/pull/2025>`_
|
`#2025 <https://github.com/sanic-org/sanic/pull/2025>`_
|
||||||
|
@ -367,8 +351,7 @@ Dependencies
|
||||||
Version 20.12.0
|
Version 20.12.0
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
Features
|
**Features**
|
||||||
********
|
|
||||||
|
|
||||||
*
|
*
|
||||||
`#1993 <https://github.com/sanic-org/sanic/pull/1993>`_
|
`#1993 <https://github.com/sanic-org/sanic/pull/1993>`_
|
||||||
|
@ -377,8 +360,7 @@ Features
|
||||||
Version 20.12.0
|
Version 20.12.0
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
Features
|
**Features**
|
||||||
********
|
|
||||||
|
|
||||||
*
|
*
|
||||||
`#1945 <https://github.com/sanic-org/sanic/pull/1945>`_
|
`#1945 <https://github.com/sanic-org/sanic/pull/1945>`_
|
||||||
|
@ -416,22 +398,19 @@ Features
|
||||||
`#1979 <https://github.com/sanic-org/sanic/pull/1979>`_
|
`#1979 <https://github.com/sanic-org/sanic/pull/1979>`_
|
||||||
Add app registry and Sanic class level app retrieval
|
Add app registry and Sanic class level app retrieval
|
||||||
|
|
||||||
Bugfixes
|
**Bugfixes**
|
||||||
********
|
|
||||||
|
|
||||||
*
|
*
|
||||||
`#1965 <https://github.com/sanic-org/sanic/pull/1965>`_
|
`#1965 <https://github.com/sanic-org/sanic/pull/1965>`_
|
||||||
Fix Chunked Transport-Encoding in ASGI streaming response
|
Fix Chunked Transport-Encoding in ASGI streaming response
|
||||||
|
|
||||||
Deprecations and Removals
|
**Deprecations and Removals**
|
||||||
*************************
|
|
||||||
|
|
||||||
*
|
*
|
||||||
`#1981 <https://github.com/sanic-org/sanic/pull/1981>`_
|
`#1981 <https://github.com/sanic-org/sanic/pull/1981>`_
|
||||||
Cleanup and remove deprecated code
|
Cleanup and remove deprecated code
|
||||||
|
|
||||||
Developer infrastructure
|
**Developer infrastructure**
|
||||||
************************
|
|
||||||
|
|
||||||
*
|
*
|
||||||
`#1956 <https://github.com/sanic-org/sanic/pull/1956>`_
|
`#1956 <https://github.com/sanic-org/sanic/pull/1956>`_
|
||||||
|
@ -445,8 +424,7 @@ Developer infrastructure
|
||||||
`#1986 <https://github.com/sanic-org/sanic/pull/1986>`_
|
`#1986 <https://github.com/sanic-org/sanic/pull/1986>`_
|
||||||
Update tox requirements
|
Update tox requirements
|
||||||
|
|
||||||
Improved Documentation
|
**Improved Documentation**
|
||||||
**********************
|
|
||||||
|
|
||||||
*
|
*
|
||||||
`#1951 <https://github.com/sanic-org/sanic/pull/1951>`_
|
`#1951 <https://github.com/sanic-org/sanic/pull/1951>`_
|
||||||
|
@ -464,8 +442,7 @@ Improved Documentation
|
||||||
Version 20.9.1
|
Version 20.9.1
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
Bugfixes
|
**Bugfixes**
|
||||||
********
|
|
||||||
|
|
||||||
*
|
*
|
||||||
`#1954 <https://github.com/sanic-org/sanic/pull/1954>`_
|
`#1954 <https://github.com/sanic-org/sanic/pull/1954>`_
|
||||||
|
@ -478,8 +455,7 @@ Bugfixes
|
||||||
Version 19.12.3
|
Version 19.12.3
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
Bugfixes
|
**Bugfixes**
|
||||||
********
|
|
||||||
|
|
||||||
*
|
*
|
||||||
`#1959 <https://github.com/sanic-org/sanic/pull/1959>`_
|
`#1959 <https://github.com/sanic-org/sanic/pull/1959>`_
|
||||||
|
@ -490,8 +466,7 @@ Version 20.9.0
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
|
|
||||||
Features
|
**Features**
|
||||||
********
|
|
||||||
|
|
||||||
*
|
*
|
||||||
`#1887 <https://github.com/sanic-org/sanic/pull/1887>`_
|
`#1887 <https://github.com/sanic-org/sanic/pull/1887>`_
|
||||||
|
@ -518,22 +493,19 @@ Features
|
||||||
`#1937 <https://github.com/sanic-org/sanic/pull/1937>`_
|
`#1937 <https://github.com/sanic-org/sanic/pull/1937>`_
|
||||||
Added auto, text, and json fallback error handlers (in v21.3, the default will change form html to auto)
|
Added auto, text, and json fallback error handlers (in v21.3, the default will change form html to auto)
|
||||||
|
|
||||||
Bugfixes
|
**Bugfixes**
|
||||||
********
|
|
||||||
|
|
||||||
*
|
*
|
||||||
`#1897 <https://github.com/sanic-org/sanic/pull/1897>`_
|
`#1897 <https://github.com/sanic-org/sanic/pull/1897>`_
|
||||||
Resolves exception from unread bytes in stream
|
Resolves exception from unread bytes in stream
|
||||||
|
|
||||||
Deprecations and Removals
|
**Deprecations and Removals**
|
||||||
*************************
|
|
||||||
|
|
||||||
*
|
*
|
||||||
`#1903 <https://github.com/sanic-org/sanic/pull/1903>`_
|
`#1903 <https://github.com/sanic-org/sanic/pull/1903>`_
|
||||||
config.from_envar, config.from_pyfile, and config.from_object are deprecated and set to be removed in v21.3
|
config.from_envar, config.from_pyfile, and config.from_object are deprecated and set to be removed in v21.3
|
||||||
|
|
||||||
Developer infrastructure
|
**Developer infrastructure**
|
||||||
************************
|
|
||||||
|
|
||||||
*
|
*
|
||||||
`#1890 <https://github.com/sanic-org/sanic/pull/1890>`_,
|
`#1890 <https://github.com/sanic-org/sanic/pull/1890>`_,
|
||||||
|
@ -548,8 +520,7 @@ Developer infrastructure
|
||||||
`#1924 <https://github.com/sanic-org/sanic/pull/1924>`_
|
`#1924 <https://github.com/sanic-org/sanic/pull/1924>`_
|
||||||
Adding --strict-markers for pytest
|
Adding --strict-markers for pytest
|
||||||
|
|
||||||
Improved Documentation
|
**Improved Documentation**
|
||||||
**********************
|
|
||||||
|
|
||||||
*
|
*
|
||||||
`#1922 <https://github.com/sanic-org/sanic/pull/1922>`_
|
`#1922 <https://github.com/sanic-org/sanic/pull/1922>`_
|
||||||
|
@ -559,8 +530,7 @@ Improved Documentation
|
||||||
Version 20.6.3
|
Version 20.6.3
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
Bugfixes
|
**Bugfixes**
|
||||||
********
|
|
||||||
|
|
||||||
*
|
*
|
||||||
`#1884 <https://github.com/sanic-org/sanic/pull/1884>`_
|
`#1884 <https://github.com/sanic-org/sanic/pull/1884>`_
|
||||||
|
@ -570,8 +540,7 @@ Bugfixes
|
||||||
Version 20.6.2
|
Version 20.6.2
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
Features
|
**Features**
|
||||||
********
|
|
||||||
|
|
||||||
*
|
*
|
||||||
`#1641 <https://github.com/sanic-org/sanic/pull/1641>`_
|
`#1641 <https://github.com/sanic-org/sanic/pull/1641>`_
|
||||||
|
@ -581,8 +550,7 @@ Features
|
||||||
Version 20.6.1
|
Version 20.6.1
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
Features
|
**Features**
|
||||||
********
|
|
||||||
|
|
||||||
*
|
*
|
||||||
`#1760 <https://github.com/sanic-org/sanic/pull/1760>`_
|
`#1760 <https://github.com/sanic-org/sanic/pull/1760>`_
|
||||||
|
@ -596,8 +564,7 @@ Features
|
||||||
`#1880 <https://github.com/sanic-org/sanic/pull/1880>`_
|
`#1880 <https://github.com/sanic-org/sanic/pull/1880>`_
|
||||||
Add handler names for websockets for url_for usage
|
Add handler names for websockets for url_for usage
|
||||||
|
|
||||||
Bugfixes
|
**Bugfixes**
|
||||||
********
|
|
||||||
|
|
||||||
*
|
*
|
||||||
`#1776 <https://github.com/sanic-org/sanic/pull/1776>`_
|
`#1776 <https://github.com/sanic-org/sanic/pull/1776>`_
|
||||||
|
@ -619,15 +586,13 @@ Bugfixes
|
||||||
`#1853 <https://github.com/sanic-org/sanic/pull/1853>`_
|
`#1853 <https://github.com/sanic-org/sanic/pull/1853>`_
|
||||||
Fix pickle error when attempting to pickle an application which contains websocket routes
|
Fix pickle error when attempting to pickle an application which contains websocket routes
|
||||||
|
|
||||||
Deprecations and Removals
|
**Deprecations and Removals**
|
||||||
*************************
|
|
||||||
|
|
||||||
*
|
*
|
||||||
`#1739 <https://github.com/sanic-org/sanic/pull/1739>`_
|
`#1739 <https://github.com/sanic-org/sanic/pull/1739>`_
|
||||||
Deprecate body_bytes to merge into body
|
Deprecate body_bytes to merge into body
|
||||||
|
|
||||||
Developer infrastructure
|
**Developer infrastructure**
|
||||||
************************
|
|
||||||
|
|
||||||
*
|
*
|
||||||
`#1852 <https://github.com/sanic-org/sanic/pull/1852>`_
|
`#1852 <https://github.com/sanic-org/sanic/pull/1852>`_
|
||||||
|
@ -642,8 +607,7 @@ Developer infrastructure
|
||||||
Wrap run()'s "protocol" type annotation in Optional[]
|
Wrap run()'s "protocol" type annotation in Optional[]
|
||||||
|
|
||||||
|
|
||||||
Improved Documentation
|
**Improved Documentation**
|
||||||
**********************
|
|
||||||
|
|
||||||
*
|
*
|
||||||
`#1846 <https://github.com/sanic-org/sanic/pull/1846>`_
|
`#1846 <https://github.com/sanic-org/sanic/pull/1846>`_
|
||||||
|
@ -663,8 +627,7 @@ Version 20.6.0
|
||||||
Version 20.3.0
|
Version 20.3.0
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
Features
|
**Features**
|
||||||
********
|
|
||||||
|
|
||||||
*
|
*
|
||||||
`#1762 <https://github.com/sanic-org/sanic/pull/1762>`_
|
`#1762 <https://github.com/sanic-org/sanic/pull/1762>`_
|
||||||
|
@ -695,8 +658,7 @@ Features
|
||||||
`#1820 <https://github.com/sanic-org/sanic/pull/1820>`_
|
`#1820 <https://github.com/sanic-org/sanic/pull/1820>`_
|
||||||
Do not set content-type and content-length headers in exceptions
|
Do not set content-type and content-length headers in exceptions
|
||||||
|
|
||||||
Bugfixes
|
**Bugfixes**
|
||||||
********
|
|
||||||
|
|
||||||
*
|
*
|
||||||
`#1748 <https://github.com/sanic-org/sanic/pull/1748>`_
|
`#1748 <https://github.com/sanic-org/sanic/pull/1748>`_
|
||||||
|
@ -714,8 +676,7 @@ Bugfixes
|
||||||
`#1808 <https://github.com/sanic-org/sanic/pull/1808>`_
|
`#1808 <https://github.com/sanic-org/sanic/pull/1808>`_
|
||||||
Fix Ctrl+C and tests on Windows
|
Fix Ctrl+C and tests on Windows
|
||||||
|
|
||||||
Deprecations and Removals
|
**Deprecations and Removals**
|
||||||
*************************
|
|
||||||
|
|
||||||
*
|
*
|
||||||
`#1800 <https://github.com/sanic-org/sanic/pull/1800>`_
|
`#1800 <https://github.com/sanic-org/sanic/pull/1800>`_
|
||||||
|
@ -733,8 +694,7 @@ Deprecations and Removals
|
||||||
`#1818 <https://github.com/sanic-org/sanic/pull/1818>`_
|
`#1818 <https://github.com/sanic-org/sanic/pull/1818>`_
|
||||||
Complete deprecation of ``app.remove_route`` and ``request.raw_args``
|
Complete deprecation of ``app.remove_route`` and ``request.raw_args``
|
||||||
|
|
||||||
Dependencies
|
**Dependencies**
|
||||||
************
|
|
||||||
|
|
||||||
*
|
*
|
||||||
`#1794 <https://github.com/sanic-org/sanic/pull/1794>`_
|
`#1794 <https://github.com/sanic-org/sanic/pull/1794>`_
|
||||||
|
@ -744,15 +704,13 @@ Dependencies
|
||||||
`#1806 <https://github.com/sanic-org/sanic/pull/1806>`_
|
`#1806 <https://github.com/sanic-org/sanic/pull/1806>`_
|
||||||
Import ``ASGIDispatch`` from top-level ``httpx`` (from third-party deprecation)
|
Import ``ASGIDispatch`` from top-level ``httpx`` (from third-party deprecation)
|
||||||
|
|
||||||
Developer infrastructure
|
**Developer infrastructure**
|
||||||
************************
|
|
||||||
|
|
||||||
*
|
*
|
||||||
`#1833 <https://github.com/sanic-org/sanic/pull/1833>`_
|
`#1833 <https://github.com/sanic-org/sanic/pull/1833>`_
|
||||||
Resolve broken documentation builds
|
Resolve broken documentation builds
|
||||||
|
|
||||||
Improved Documentation
|
**Improved Documentation**
|
||||||
**********************
|
|
||||||
|
|
||||||
*
|
*
|
||||||
`#1755 <https://github.com/sanic-org/sanic/pull/1755>`_
|
`#1755 <https://github.com/sanic-org/sanic/pull/1755>`_
|
||||||
|
@ -794,8 +752,7 @@ Improved Documentation
|
||||||
Version 19.12.0
|
Version 19.12.0
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
Bugfixes
|
**Bugfixes**
|
||||||
********
|
|
||||||
|
|
||||||
- Fix blueprint middleware application
|
- Fix blueprint middleware application
|
||||||
|
|
||||||
|
@ -814,8 +771,7 @@ Bugfixes
|
||||||
due to an `AttributeError`. This fix makes the availability of `SERVER_NAME` on our `app.config` an optional behavior. (`#1707 <https://github.com/sanic-org/sanic/issues/1707>`__)
|
due to an `AttributeError`. This fix makes the availability of `SERVER_NAME` on our `app.config` an optional behavior. (`#1707 <https://github.com/sanic-org/sanic/issues/1707>`__)
|
||||||
|
|
||||||
|
|
||||||
Improved Documentation
|
**Improved Documentation**
|
||||||
**********************
|
|
||||||
|
|
||||||
- Move docs from MD to RST
|
- Move docs from MD to RST
|
||||||
|
|
||||||
|
@ -829,8 +785,7 @@ Improved Documentation
|
||||||
Version 19.6.3
|
Version 19.6.3
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
Features
|
**Features**
|
||||||
********
|
|
||||||
|
|
||||||
- Enable Towncrier Support
|
- Enable Towncrier Support
|
||||||
|
|
||||||
|
@ -838,8 +793,7 @@ Features
|
||||||
of generating and managing change logs as part of each of pull requests. (`#1631 <https://github.com/sanic-org/sanic/issues/1631>`__)
|
of generating and managing change logs as part of each of pull requests. (`#1631 <https://github.com/sanic-org/sanic/issues/1631>`__)
|
||||||
|
|
||||||
|
|
||||||
Improved Documentation
|
**Improved Documentation**
|
||||||
**********************
|
|
||||||
|
|
||||||
- Documentation infrastructure changes
|
- Documentation infrastructure changes
|
||||||
|
|
||||||
|
@ -852,8 +806,7 @@ Improved Documentation
|
||||||
Version 19.6.2
|
Version 19.6.2
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
Features
|
**Features**
|
||||||
********
|
|
||||||
|
|
||||||
*
|
*
|
||||||
`#1562 <https://github.com/sanic-org/sanic/pull/1562>`_
|
`#1562 <https://github.com/sanic-org/sanic/pull/1562>`_
|
||||||
|
@ -869,8 +822,7 @@ Features
|
||||||
Add Configure support from object string
|
Add Configure support from object string
|
||||||
|
|
||||||
|
|
||||||
Bugfixes
|
**Bugfixes**
|
||||||
********
|
|
||||||
|
|
||||||
*
|
*
|
||||||
`#1587 <https://github.com/sanic-org/sanic/pull/1587>`_
|
`#1587 <https://github.com/sanic-org/sanic/pull/1587>`_
|
||||||
|
@ -888,8 +840,7 @@ Bugfixes
|
||||||
`#1594 <https://github.com/sanic-org/sanic/pull/1594>`_
|
`#1594 <https://github.com/sanic-org/sanic/pull/1594>`_
|
||||||
Strict Slashes behavior fix
|
Strict Slashes behavior fix
|
||||||
|
|
||||||
Deprecations and Removals
|
**Deprecations and Removals**
|
||||||
*************************
|
|
||||||
|
|
||||||
*
|
*
|
||||||
`#1544 <https://github.com/sanic-org/sanic/pull/1544>`_
|
`#1544 <https://github.com/sanic-org/sanic/pull/1544>`_
|
||||||
|
@ -913,8 +864,7 @@ Deprecations and Removals
|
||||||
Version 19.3
|
Version 19.3
|
||||||
------------
|
------------
|
||||||
|
|
||||||
Features
|
**Features**
|
||||||
********
|
|
||||||
|
|
||||||
*
|
*
|
||||||
`#1497 <https://github.com/sanic-org/sanic/pull/1497>`_
|
`#1497 <https://github.com/sanic-org/sanic/pull/1497>`_
|
||||||
|
@ -982,8 +932,7 @@ Features
|
||||||
|
|
||||||
This is a breaking change.
|
This is a breaking change.
|
||||||
|
|
||||||
Bugfixes
|
**Bugfixes**
|
||||||
********
|
|
||||||
|
|
||||||
|
|
||||||
*
|
*
|
||||||
|
@ -1019,8 +968,7 @@ Bugfixes
|
||||||
This allows the access log to be disabled for example when running via
|
This allows the access log to be disabled for example when running via
|
||||||
gunicorn.
|
gunicorn.
|
||||||
|
|
||||||
Developer infrastructure
|
**Developer infrastructure**
|
||||||
************************
|
|
||||||
|
|
||||||
* `#1529 <https://github.com/sanic-org/sanic/pull/1529>`_ Update project PyPI credentials
|
* `#1529 <https://github.com/sanic-org/sanic/pull/1529>`_ Update project PyPI credentials
|
||||||
* `#1515 <https://github.com/sanic-org/sanic/pull/1515>`_ fix linter issue causing travis build failures (fix #1514)
|
* `#1515 <https://github.com/sanic-org/sanic/pull/1515>`_ fix linter issue causing travis build failures (fix #1514)
|
||||||
|
@ -1028,8 +976,7 @@ Developer infrastructure
|
||||||
* `#1478 <https://github.com/sanic-org/sanic/pull/1478>`_ Upgrade setuptools version and use native docutils in doc build
|
* `#1478 <https://github.com/sanic-org/sanic/pull/1478>`_ Upgrade setuptools version and use native docutils in doc build
|
||||||
* `#1464 <https://github.com/sanic-org/sanic/pull/1464>`_ Upgrade pytest, and fix caplog unit tests
|
* `#1464 <https://github.com/sanic-org/sanic/pull/1464>`_ Upgrade pytest, and fix caplog unit tests
|
||||||
|
|
||||||
Improved Documentation
|
**Improved Documentation**
|
||||||
**********************
|
|
||||||
|
|
||||||
* `#1516 <https://github.com/sanic-org/sanic/pull/1516>`_ Fix typo at the exception documentation
|
* `#1516 <https://github.com/sanic-org/sanic/pull/1516>`_ Fix typo at the exception documentation
|
||||||
* `#1510 <https://github.com/sanic-org/sanic/pull/1510>`_ fix typo in Asyncio example
|
* `#1510 <https://github.com/sanic-org/sanic/pull/1510>`_ fix typo in Asyncio example
|
||||||
|
@ -1096,15 +1043,13 @@ Version 18.12
|
||||||
Version 0.8
|
Version 0.8
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
0.8.3
|
**0.8.3**
|
||||||
*****
|
|
||||||
|
|
||||||
* Changes:
|
* Changes:
|
||||||
|
|
||||||
* Ownership changed to org 'sanic-org'
|
* Ownership changed to org 'sanic-org'
|
||||||
|
|
||||||
0.8.0
|
**0.8.0**
|
||||||
*****
|
|
||||||
|
|
||||||
* Changes:
|
* Changes:
|
||||||
|
|
||||||
|
@ -1184,19 +1129,16 @@ Version 0.1
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
|
|
||||||
0.1.7
|
**0.1.7**
|
||||||
*****
|
|
||||||
|
|
||||||
* Reversed static url and directory arguments to meet spec
|
* Reversed static url and directory arguments to meet spec
|
||||||
|
|
||||||
0.1.6
|
**0.1.6**
|
||||||
*****
|
|
||||||
|
|
||||||
* Static files
|
* Static files
|
||||||
* Lazy Cookie Loading
|
* Lazy Cookie Loading
|
||||||
|
|
||||||
0.1.5
|
**0.1.5**
|
||||||
*****
|
|
||||||
|
|
||||||
* Cookies
|
* Cookies
|
||||||
* Blueprint listeners and ordering
|
* Blueprint listeners and ordering
|
||||||
|
@ -1204,23 +1146,19 @@ Version 0.1
|
||||||
* Fix: Incomplete file reads on medium+ sized post requests
|
* Fix: Incomplete file reads on medium+ sized post requests
|
||||||
* Breaking: after_start and before_stop now pass sanic as their first argument
|
* Breaking: after_start and before_stop now pass sanic as their first argument
|
||||||
|
|
||||||
0.1.4
|
**0.1.4**
|
||||||
*****
|
|
||||||
|
|
||||||
* Multiprocessing
|
* Multiprocessing
|
||||||
|
|
||||||
0.1.3
|
**0.1.3**
|
||||||
*****
|
|
||||||
|
|
||||||
* Blueprint support
|
* Blueprint support
|
||||||
* Faster Response processing
|
* Faster Response processing
|
||||||
|
|
||||||
0.1.1 - 0.1.2
|
**0.1.1 - 0.1.2**
|
||||||
*************
|
|
||||||
|
|
||||||
* Struggling to update pypi via CI
|
* Struggling to update pypi via CI
|
||||||
|
|
||||||
0.1.0
|
**0.1.0**
|
||||||
*****
|
|
||||||
|
|
||||||
* Released to public
|
* Released to public
|
||||||
|
|
|
@ -38,10 +38,3 @@ sanic.views
|
||||||
.. automodule:: sanic.views
|
.. automodule:: sanic.views
|
||||||
:members:
|
:members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|
||||||
sanic.websocket
|
|
||||||
---------------
|
|
||||||
|
|
||||||
.. automodule:: sanic.websocket
|
|
||||||
:members:
|
|
||||||
:show-inheritance:
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
📜 Changelog
|
📜 Changelog
|
||||||
============
|
============
|
||||||
|
|
||||||
.. mdinclude:: ./releases/21.9.md
|
.. mdinclude:: ./releases/21/21.12.md
|
||||||
|
.. mdinclude:: ./releases/21/21.9.md
|
||||||
.. include:: ../../CHANGELOG.rst
|
.. include:: ../../CHANGELOG.rst
|
||||||
|
|
58
docs/sanic/releases/21/21.12.md
Normal file
58
docs/sanic/releases/21/21.12.md
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
## Version 21.12.0
|
||||||
|
|
||||||
|
### Features
|
||||||
|
- [#2260](https://github.com/sanic-org/sanic/pull/2260) Allow early Blueprint registrations to still apply later added objects
|
||||||
|
- [#2262](https://github.com/sanic-org/sanic/pull/2262) Noisy exceptions - force logging of all exceptions
|
||||||
|
- [#2264](https://github.com/sanic-org/sanic/pull/2264) Optional `uvloop` by configuration
|
||||||
|
- [#2270](https://github.com/sanic-org/sanic/pull/2270) Vhost support using multiple TLS certificates
|
||||||
|
- [#2277](https://github.com/sanic-org/sanic/pull/2277) Change signal routing for increased consistency
|
||||||
|
- *BREAKING CHANGE*: If you were manually routing signals there is a breaking change. The signal router's `get` is no longer 100% determinative. There is now an additional step to loop thru the returned signals for proper matching on the requirements. If signals are being dispatched using `app.dispatch` or `bp.dispatch`, there is no change.
|
||||||
|
- [#2290](https://github.com/sanic-org/sanic/pull/2290) Add contextual exceptions
|
||||||
|
- [#2291](https://github.com/sanic-org/sanic/pull/2291) Increase join concat performance
|
||||||
|
- [#2295](https://github.com/sanic-org/sanic/pull/2295), [#2316](https://github.com/sanic-org/sanic/pull/2316), [#2331](https://github.com/sanic-org/sanic/pull/2331) Restructure of CLI and application state with new displays and more command parity with `app.run`
|
||||||
|
- [#2302](https://github.com/sanic-org/sanic/pull/2302) Add route context at definition time
|
||||||
|
- [#2304](https://github.com/sanic-org/sanic/pull/2304) Named tasks and new API for managing background tasks
|
||||||
|
- [#2307](https://github.com/sanic-org/sanic/pull/2307) On app auto-reload, provide insight of changed files
|
||||||
|
- [#2308](https://github.com/sanic-org/sanic/pull/2308) Auto extend application with [Sanic Extensions](https://sanicframework.org/en/plugins/sanic-ext/getting-started.html) if it is installed, and provide first class support for accessing the extensions
|
||||||
|
- [#2309](https://github.com/sanic-org/sanic/pull/2309) Builtin signals changed to `Enum`
|
||||||
|
- [#2313](https://github.com/sanic-org/sanic/pull/2313) Support additional config implementation use case
|
||||||
|
- [#2321](https://github.com/sanic-org/sanic/pull/2321) Refactor environment variable hydration logic
|
||||||
|
- [#2327](https://github.com/sanic-org/sanic/pull/2327) Prevent sending multiple or mixed responses on a single request
|
||||||
|
- [#2330](https://github.com/sanic-org/sanic/pull/2330) Custom type casting on environment variables
|
||||||
|
- [#2332](https://github.com/sanic-org/sanic/pull/2332) Make all deprecation notices consistent
|
||||||
|
- [#2335](https://github.com/sanic-org/sanic/pull/2335) Allow underscore to start instance names
|
||||||
|
|
||||||
|
### Bugfixes
|
||||||
|
- [#2273](https://github.com/sanic-org/sanic/pull/2273) Replace assignation by typing for `websocket_handshake`
|
||||||
|
- [#2285](https://github.com/sanic-org/sanic/pull/2285) Fix IPv6 display in startup logs
|
||||||
|
- [#2299](https://github.com/sanic-org/sanic/pull/2299) Dispatch `http.lifecyle.response` from exception handler
|
||||||
|
|
||||||
|
### Deprecations and Removals
|
||||||
|
- [#2306](https://github.com/sanic-org/sanic/pull/2306) Removal of deprecated items
|
||||||
|
- `Sanic` and `Blueprint` may no longer have arbitrary properties attached to them
|
||||||
|
- `Sanic` and `Blueprint` forced to have compliant names
|
||||||
|
- alphanumeric + `_` + `-`
|
||||||
|
- must start with letter or `_`
|
||||||
|
- `load_env` keyword argument of `Sanic`
|
||||||
|
- `sanic.exceptions.abort`
|
||||||
|
- `sanic.views.CompositionView`
|
||||||
|
- `sanic.response.StreamingHTTPResponse`
|
||||||
|
- *NOTE:* the `stream()` response method (where you pass a callable streaming function) has been deprecated and will be removed in v22.6. You should upgrade all streaming responses to the new style: https://sanicframework.org/en/guide/advanced/streaming.html#response-streaming
|
||||||
|
- [#2320](https://github.com/sanic-org/sanic/pull/2320) Remove app instance from Config for error handler setting
|
||||||
|
|
||||||
|
### Developer infrastructure
|
||||||
|
- [#2251](https://github.com/sanic-org/sanic/pull/2251) Change dev install command
|
||||||
|
- [#2286](https://github.com/sanic-org/sanic/pull/2286) Change codeclimate complexity threshold from 5 to 10
|
||||||
|
- [#2287](https://github.com/sanic-org/sanic/pull/2287) Update host test function names so they are not overwritten
|
||||||
|
- [#2292](https://github.com/sanic-org/sanic/pull/2292) Fail CI on error
|
||||||
|
- [#2311](https://github.com/sanic-org/sanic/pull/2311), [#2324](https://github.com/sanic-org/sanic/pull/2324) Do not run tests for draft PRs
|
||||||
|
- [#2336](https://github.com/sanic-org/sanic/pull/2336) Remove paths from coverage checks
|
||||||
|
- [#2338](https://github.com/sanic-org/sanic/pull/2338) Cleanup ports on tests
|
||||||
|
|
||||||
|
### Improved Documentation
|
||||||
|
- [#2269](https://github.com/sanic-org/sanic/pull/2269), [#2329](https://github.com/sanic-org/sanic/pull/2329), [#2333](https://github.com/sanic-org/sanic/pull/2333) Cleanup typos and fix language
|
||||||
|
|
||||||
|
### Miscellaneous
|
||||||
|
- [#2257](https://github.com/sanic-org/sanic/pull/2257), [#2294](https://github.com/sanic-org/sanic/pull/2294), [#2341](https://github.com/sanic-org/sanic/pull/2341) Add Python 3.10 support
|
||||||
|
- [#2279](https://github.com/sanic-org/sanic/pull/2279), [#2317](https://github.com/sanic-org/sanic/pull/2317), [#2322](https://github.com/sanic-org/sanic/pull/2322) Add/correct missing type annotations
|
||||||
|
- [#2305](https://github.com/sanic-org/sanic/pull/2305) Fix examples to use modern implementations
|
|
@ -1,4 +1,14 @@
|
||||||
## Version 21.9
|
## Version 21.9.3
|
||||||
|
*Rerelease of v21.9.2 with some cleanup*
|
||||||
|
|
||||||
|
## Version 21.9.2
|
||||||
|
- [#2268](https://github.com/sanic-org/sanic/pull/2268) Make HTTP connections start in IDLE stage, avoiding delays and error messages
|
||||||
|
- [#2310](https://github.com/sanic-org/sanic/pull/2310) More consistent config setting with post-FALLBACK_ERROR_FORMAT apply
|
||||||
|
|
||||||
|
## Version 21.9.1
|
||||||
|
- [#2259](https://github.com/sanic-org/sanic/pull/2259) Allow non-conforming ErrorHandlers
|
||||||
|
|
||||||
|
## Version 21.9.0
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
- [#2158](https://github.com/sanic-org/sanic/pull/2158), [#2248](https://github.com/sanic-org/sanic/pull/2248) Complete overhaul of I/O to websockets
|
- [#2158](https://github.com/sanic-org/sanic/pull/2158), [#2248](https://github.com/sanic-org/sanic/pull/2248) Complete overhaul of I/O to websockets
|
|
@ -5,7 +5,7 @@ import asyncio
|
||||||
from sanic import Sanic
|
from sanic import Sanic
|
||||||
|
|
||||||
|
|
||||||
app = Sanic(__name__)
|
app = Sanic("Example")
|
||||||
|
|
||||||
|
|
||||||
async def notify_server_started_after_five_seconds():
|
async def notify_server_started_after_five_seconds():
|
||||||
|
|
|
@ -4,7 +4,7 @@ from sanic import Sanic
|
||||||
from sanic.response import text
|
from sanic.response import text
|
||||||
|
|
||||||
|
|
||||||
app = Sanic(__name__)
|
app = Sanic("Example")
|
||||||
|
|
||||||
|
|
||||||
@app.middleware("request")
|
@app.middleware("request")
|
||||||
|
|
|
@ -6,7 +6,7 @@ from sanic import Sanic
|
||||||
from sanic.response import json
|
from sanic.response import json
|
||||||
|
|
||||||
|
|
||||||
app = Sanic(__name__)
|
app = Sanic("Example")
|
||||||
|
|
||||||
|
|
||||||
def check_request_for_authorization_status(request):
|
def check_request_for_authorization_status(request):
|
||||||
|
|
|
@ -8,9 +8,9 @@ are added. And blueprint response middleware are executed in _reverse_ order.
|
||||||
On a valid request, it should print "1 2 3 6 5 4" to terminal
|
On a valid request, it should print "1 2 3 6 5 4" to terminal
|
||||||
"""
|
"""
|
||||||
|
|
||||||
app = Sanic(__name__)
|
app = Sanic("Example")
|
||||||
|
|
||||||
bp = Blueprint("bp_" + __name__)
|
bp = Blueprint("bp_example")
|
||||||
|
|
||||||
|
|
||||||
@bp.on_request
|
@bp.on_request
|
||||||
|
|
|
@ -2,10 +2,10 @@ from sanic import Blueprint, Sanic
|
||||||
from sanic.response import file, json
|
from sanic.response import file, json
|
||||||
|
|
||||||
|
|
||||||
app = Sanic(__name__)
|
app = Sanic("Example")
|
||||||
blueprint = Blueprint("name", url_prefix="/my_blueprint")
|
blueprint = Blueprint("bp_example", url_prefix="/my_blueprint")
|
||||||
blueprint2 = Blueprint("name2", url_prefix="/my_blueprint2")
|
blueprint2 = Blueprint("bp_example2", url_prefix="/my_blueprint2")
|
||||||
blueprint3 = Blueprint("name3", url_prefix="/my_blueprint3")
|
blueprint3 = Blueprint("bp_example3", url_prefix="/my_blueprint3")
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route("/foo")
|
@blueprint.route("/foo")
|
||||||
|
|
|
@ -3,7 +3,7 @@ from asyncio import sleep
|
||||||
from sanic import Sanic, response
|
from sanic import Sanic, response
|
||||||
|
|
||||||
|
|
||||||
app = Sanic(__name__, strict_slashes=True)
|
app = Sanic("DelayedResponseApp", strict_slashes=True)
|
||||||
|
|
||||||
|
|
||||||
@app.get("/")
|
@app.get("/")
|
||||||
|
|
|
@ -41,7 +41,7 @@ from sanic import Sanic
|
||||||
|
|
||||||
|
|
||||||
handler = CustomHandler()
|
handler = CustomHandler()
|
||||||
app = Sanic(__name__, error_handler=handler)
|
app = Sanic("Example", error_handler=handler)
|
||||||
|
|
||||||
|
|
||||||
@app.route("/")
|
@app.route("/")
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from sanic import Sanic
|
from sanic import Sanic, response
|
||||||
from sanic import response
|
|
||||||
|
|
||||||
app = Sanic(__name__)
|
|
||||||
|
app = Sanic("Example")
|
||||||
|
|
||||||
|
|
||||||
@app.route("/")
|
@app.route("/")
|
||||||
|
@ -9,5 +9,5 @@ async def test(request):
|
||||||
return response.json({"test": True})
|
return response.json({"test": True})
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
app.run(host="0.0.0.0", port=8000)
|
app.run(host="0.0.0.0", port=8000)
|
||||||
|
|
|
@ -6,7 +6,7 @@ from sanic import Sanic
|
||||||
from sanic.response import json
|
from sanic.response import json
|
||||||
|
|
||||||
|
|
||||||
app = Sanic(__name__)
|
app = Sanic("Example")
|
||||||
|
|
||||||
sem = None
|
sem = None
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@ LOG_SETTINGS = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
app = Sanic(__name__, log_config=LOG_SETTINGS)
|
app = Sanic("Example", log_config=LOG_SETTINGS)
|
||||||
|
|
||||||
|
|
||||||
@app.on_request
|
@app.on_request
|
||||||
|
|
|
@ -43,7 +43,7 @@ logdna = logging.getLogger(__name__)
|
||||||
logdna.setLevel(logging.INFO)
|
logdna.setLevel(logging.INFO)
|
||||||
logdna.addHandler(logdna_handler)
|
logdna.addHandler(logdna_handler)
|
||||||
|
|
||||||
app = Sanic(__name__)
|
app = Sanic("Example")
|
||||||
|
|
||||||
|
|
||||||
@app.middleware
|
@app.middleware
|
||||||
|
|
|
@ -2,27 +2,29 @@
|
||||||
Modify header or status in response
|
Modify header or status in response
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from sanic import Sanic
|
from sanic import Sanic, response
|
||||||
from sanic import response
|
|
||||||
|
|
||||||
app = Sanic(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/')
|
app = Sanic("Example")
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/")
|
||||||
def handle_request(request):
|
def handle_request(request):
|
||||||
return response.json(
|
return response.json(
|
||||||
{'message': 'Hello world!'},
|
{"message": "Hello world!"},
|
||||||
headers={'X-Served-By': 'sanic'},
|
headers={"X-Served-By": "sanic"},
|
||||||
status=200
|
status=200,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/unauthorized')
|
@app.route("/unauthorized")
|
||||||
def handle_request(request):
|
def handle_request(request):
|
||||||
return response.json(
|
return response.json(
|
||||||
{'message': 'You are not authorized'},
|
{"message": "You are not authorized"},
|
||||||
headers={'X-Served-By': 'sanic'},
|
headers={"X-Served-By": "sanic"},
|
||||||
status=404
|
status=404,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
app.run(host="0.0.0.0", port=8000, debug=True)
|
app.run(host="0.0.0.0", port=8000, debug=True)
|
||||||
|
|
|
@ -32,7 +32,7 @@ def test_port(worker_id):
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope="session")
|
||||||
def app():
|
def app():
|
||||||
app = Sanic()
|
app = Sanic("Example")
|
||||||
|
|
||||||
@app.route("/")
|
@app.route("/")
|
||||||
async def index(request):
|
async def index(request):
|
||||||
|
|
|
@ -8,7 +8,6 @@ from sanic.handlers import ErrorHandler
|
||||||
|
|
||||||
|
|
||||||
class RaygunExceptionReporter(ErrorHandler):
|
class RaygunExceptionReporter(ErrorHandler):
|
||||||
|
|
||||||
def __init__(self, raygun_api_key=None):
|
def __init__(self, raygun_api_key=None):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
if raygun_api_key is None:
|
if raygun_api_key is None:
|
||||||
|
@ -22,16 +21,13 @@ class RaygunExceptionReporter(ErrorHandler):
|
||||||
|
|
||||||
|
|
||||||
raygun_error_reporter = RaygunExceptionReporter()
|
raygun_error_reporter = RaygunExceptionReporter()
|
||||||
app = Sanic(__name__, error_handler=raygun_error_reporter)
|
app = Sanic("Example", error_handler=raygun_error_reporter)
|
||||||
|
|
||||||
|
|
||||||
@app.route("/raise")
|
@app.route("/raise")
|
||||||
async def test(request):
|
async def test(request):
|
||||||
raise SanicException('You Broke It!')
|
raise SanicException("You Broke It!")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
app.run(
|
app.run(host="0.0.0.0", port=getenv("PORT", 8080))
|
||||||
host="0.0.0.0",
|
|
||||||
port=getenv("PORT", 8080)
|
|
||||||
)
|
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
from sanic import Sanic
|
from sanic import Sanic, response
|
||||||
from sanic import response
|
|
||||||
|
|
||||||
app = Sanic(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/')
|
app = Sanic("Example")
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/")
|
||||||
def handle_request(request):
|
def handle_request(request):
|
||||||
return response.redirect('/redirect')
|
return response.redirect("/redirect")
|
||||||
|
|
||||||
|
|
||||||
@app.route('/redirect')
|
@app.route("/redirect")
|
||||||
async def test(request):
|
async def test(request):
|
||||||
return response.json({"Redirected": True})
|
return response.json({"Redirected": True})
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
app.run(host="0.0.0.0", port=8000)
|
app.run(host="0.0.0.0", port=8000)
|
|
@ -1,65 +1,63 @@
|
||||||
from sanic import Sanic
|
from sanic import Sanic
|
||||||
from sanic.views import CompositionView
|
|
||||||
from sanic.views import HTTPMethodView
|
|
||||||
from sanic.views import stream as stream_decorator
|
|
||||||
from sanic.blueprints import Blueprint
|
from sanic.blueprints import Blueprint
|
||||||
from sanic.response import stream, text
|
from sanic.response import stream, text
|
||||||
|
from sanic.views import HTTPMethodView
|
||||||
|
from sanic.views import stream as stream_decorator
|
||||||
|
|
||||||
bp = Blueprint('blueprint_request_stream')
|
|
||||||
app = Sanic('request_stream')
|
bp = Blueprint("bp_example")
|
||||||
|
app = Sanic("Example")
|
||||||
|
|
||||||
|
|
||||||
class SimpleView(HTTPMethodView):
|
class SimpleView(HTTPMethodView):
|
||||||
|
|
||||||
@stream_decorator
|
@stream_decorator
|
||||||
async def post(self, request):
|
async def post(self, request):
|
||||||
result = ''
|
result = ""
|
||||||
while True:
|
while True:
|
||||||
body = await request.stream.get()
|
body = await request.stream.get()
|
||||||
if body is None:
|
if body is None:
|
||||||
break
|
break
|
||||||
result += body.decode('utf-8')
|
result += body.decode("utf-8")
|
||||||
return text(result)
|
return text(result)
|
||||||
|
|
||||||
|
|
||||||
@app.post('/stream', stream=True)
|
@app.post("/stream", stream=True)
|
||||||
async def handler(request):
|
async def handler(request):
|
||||||
async def streaming(response):
|
async def streaming(response):
|
||||||
while True:
|
while True:
|
||||||
body = await request.stream.get()
|
body = await request.stream.get()
|
||||||
if body is None:
|
if body is None:
|
||||||
break
|
break
|
||||||
body = body.decode('utf-8').replace('1', 'A')
|
body = body.decode("utf-8").replace("1", "A")
|
||||||
await response.write(body)
|
await response.write(body)
|
||||||
|
|
||||||
return stream(streaming)
|
return stream(streaming)
|
||||||
|
|
||||||
|
|
||||||
@bp.put('/bp_stream', stream=True)
|
@bp.put("/bp_stream", stream=True)
|
||||||
async def bp_handler(request):
|
async def bp_handler(request):
|
||||||
result = ''
|
result = ""
|
||||||
while True:
|
while True:
|
||||||
body = await request.stream.get()
|
body = await request.stream.get()
|
||||||
if body is None:
|
if body is None:
|
||||||
break
|
break
|
||||||
result += body.decode('utf-8').replace('1', 'A')
|
result += body.decode("utf-8").replace("1", "A")
|
||||||
return text(result)
|
return text(result)
|
||||||
|
|
||||||
|
|
||||||
async def post_handler(request):
|
async def post_handler(request):
|
||||||
result = ''
|
result = ""
|
||||||
while True:
|
while True:
|
||||||
body = await request.stream.get()
|
body = await request.stream.get()
|
||||||
if body is None:
|
if body is None:
|
||||||
break
|
break
|
||||||
result += body.decode('utf-8')
|
result += body.decode("utf-8")
|
||||||
return text(result)
|
return text(result)
|
||||||
|
|
||||||
|
|
||||||
app.blueprint(bp)
|
app.blueprint(bp)
|
||||||
app.add_route(SimpleView.as_view(), '/method_view')
|
app.add_route(SimpleView.as_view(), "/method_view")
|
||||||
view = CompositionView()
|
|
||||||
view.add(['POST'], post_handler, stream=True)
|
|
||||||
app.add_route(view, '/composition_view')
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
app.run(host='0.0.0.0', port=8000)
|
app.run(host="0.0.0.0", port=8000)
|
||||||
|
|
|
@ -1,21 +1,23 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
from sanic import Sanic
|
|
||||||
from sanic import response
|
from sanic import Sanic, response
|
||||||
from sanic.config import Config
|
from sanic.config import Config
|
||||||
from sanic.exceptions import RequestTimeout
|
from sanic.exceptions import RequestTimeout
|
||||||
|
|
||||||
|
|
||||||
Config.REQUEST_TIMEOUT = 1
|
Config.REQUEST_TIMEOUT = 1
|
||||||
app = Sanic(__name__)
|
app = Sanic("Example")
|
||||||
|
|
||||||
|
|
||||||
@app.route('/')
|
@app.route("/")
|
||||||
async def test(request):
|
async def test(request):
|
||||||
await asyncio.sleep(3)
|
await asyncio.sleep(3)
|
||||||
return response.text('Hello, world!')
|
return response.text("Hello, world!")
|
||||||
|
|
||||||
|
|
||||||
@app.exception(RequestTimeout)
|
@app.exception(RequestTimeout)
|
||||||
def timeout(request, exception):
|
def timeout(request, exception):
|
||||||
return response.text('RequestTimeout from error_handler.', 408)
|
return response.text("RequestTimeout from error_handler.", 408)
|
||||||
|
|
||||||
app.run(host='0.0.0.0', port=8000)
|
|
||||||
|
app.run(host="0.0.0.0", port=8000)
|
||||||
|
|
|
@ -1,21 +1,22 @@
|
||||||
|
from os import getenv
|
||||||
|
|
||||||
import rollbar
|
import rollbar
|
||||||
|
|
||||||
from sanic.handlers import ErrorHandler
|
|
||||||
from sanic import Sanic
|
from sanic import Sanic
|
||||||
from sanic.exceptions import SanicException
|
from sanic.exceptions import SanicException
|
||||||
from os import getenv
|
from sanic.handlers import ErrorHandler
|
||||||
|
|
||||||
|
|
||||||
rollbar.init(getenv("ROLLBAR_API_KEY"))
|
rollbar.init(getenv("ROLLBAR_API_KEY"))
|
||||||
|
|
||||||
|
|
||||||
class RollbarExceptionHandler(ErrorHandler):
|
class RollbarExceptionHandler(ErrorHandler):
|
||||||
|
|
||||||
def default(self, request, exception):
|
def default(self, request, exception):
|
||||||
rollbar.report_message(str(exception))
|
rollbar.report_message(str(exception))
|
||||||
return super().default(request, exception)
|
return super().default(request, exception)
|
||||||
|
|
||||||
|
|
||||||
app = Sanic(__name__, error_handler=RollbarExceptionHandler())
|
app = Sanic("Example", error_handler=RollbarExceptionHandler())
|
||||||
|
|
||||||
|
|
||||||
@app.route("/raise")
|
@app.route("/raise")
|
||||||
|
@ -24,7 +25,4 @@ def create_error(request):
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
app.run(
|
app.run(host="0.0.0.0", port=getenv("PORT", 8080))
|
||||||
host="0.0.0.0",
|
|
||||||
port=getenv("PORT", 8080)
|
|
||||||
)
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ from pathlib import Path
|
||||||
from sanic import Sanic, response
|
from sanic import Sanic, response
|
||||||
|
|
||||||
|
|
||||||
app = Sanic(__name__)
|
app = Sanic("Example")
|
||||||
|
|
||||||
|
|
||||||
@app.route("/text")
|
@app.route("/text")
|
||||||
|
|
|
@ -5,7 +5,7 @@ import uvloop
|
||||||
from sanic import Sanic, response
|
from sanic import Sanic, response
|
||||||
|
|
||||||
|
|
||||||
app = Sanic(__name__)
|
app = Sanic("Example")
|
||||||
|
|
||||||
|
|
||||||
@app.route("/")
|
@app.route("/")
|
||||||
|
|
|
@ -8,7 +8,7 @@ from sanic import Sanic, response
|
||||||
from sanic.server import AsyncioServer
|
from sanic.server import AsyncioServer
|
||||||
|
|
||||||
|
|
||||||
app = Sanic(__name__)
|
app = Sanic("Example")
|
||||||
|
|
||||||
|
|
||||||
@app.before_server_start
|
@app.before_server_start
|
||||||
|
|
|
@ -6,20 +6,19 @@ from sentry_sdk.integrations.sanic import SanicIntegration
|
||||||
from sanic import Sanic
|
from sanic import Sanic
|
||||||
from sanic.response import json
|
from sanic.response import json
|
||||||
|
|
||||||
|
|
||||||
sentry_init(
|
sentry_init(
|
||||||
dsn=getenv("SENTRY_DSN"),
|
dsn=getenv("SENTRY_DSN"),
|
||||||
integrations=[SanicIntegration()],
|
integrations=[SanicIntegration()],
|
||||||
)
|
)
|
||||||
|
|
||||||
app = Sanic(__name__)
|
app = Sanic("Example")
|
||||||
|
|
||||||
|
|
||||||
# noinspection PyUnusedLocal
|
# noinspection PyUnusedLocal
|
||||||
@app.route("/working")
|
@app.route("/working")
|
||||||
async def working_path(request):
|
async def working_path(request):
|
||||||
return json({
|
return json({"response": "Working API Response"})
|
||||||
"response": "Working API Response"
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
# noinspection PyUnusedLocal
|
# noinspection PyUnusedLocal
|
||||||
|
@ -28,8 +27,5 @@ async def raise_error(request):
|
||||||
raise Exception("Testing Sentry Integration")
|
raise Exception("Testing Sentry Integration")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
app.run(
|
app.run(host="0.0.0.0", port=getenv("PORT", 8080))
|
||||||
host="0.0.0.0",
|
|
||||||
port=getenv("PORT", 8080)
|
|
||||||
)
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
from sanic import Sanic
|
from sanic import Sanic
|
||||||
|
|
||||||
|
|
||||||
app = Sanic(__name__)
|
app = Sanic("Example")
|
||||||
|
|
||||||
app.static("/", "./static")
|
app.static("/", "./static")
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
from sanic import Sanic
|
from sanic import Sanic
|
||||||
from sanic import response as res
|
from sanic import response as res
|
||||||
|
|
||||||
app = Sanic(__name__)
|
|
||||||
|
app = Sanic("Example")
|
||||||
|
|
||||||
|
|
||||||
@app.route("/")
|
@app.route("/")
|
||||||
async def test(req):
|
async def test(req):
|
||||||
return res.text("I\'m a teapot", status=418)
|
return res.text("I'm a teapot", status=418)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
app.run(host="0.0.0.0", port=8000)
|
app.run(host="0.0.0.0", port=8000)
|
||||||
|
|
|
@ -5,7 +5,7 @@ from sanic.exceptions import ServerError
|
||||||
from sanic.log import logger as log
|
from sanic.log import logger as log
|
||||||
|
|
||||||
|
|
||||||
app = Sanic(__name__)
|
app = Sanic("Example")
|
||||||
|
|
||||||
|
|
||||||
@app.route("/")
|
@app.route("/")
|
||||||
|
|
|
@ -4,7 +4,7 @@ import socket
|
||||||
from sanic import Sanic, response
|
from sanic import Sanic, response
|
||||||
|
|
||||||
|
|
||||||
app = Sanic(__name__)
|
app = Sanic("Example")
|
||||||
|
|
||||||
|
|
||||||
@app.route("/test")
|
@app.route("/test")
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from sanic import Sanic, response
|
from sanic import Sanic, response
|
||||||
|
|
||||||
|
|
||||||
app = Sanic(__name__)
|
app = Sanic("Example")
|
||||||
|
|
||||||
|
|
||||||
@app.route("/")
|
@app.route("/")
|
||||||
|
|
|
@ -8,7 +8,7 @@ from sanic.blueprints import Blueprint
|
||||||
# curl -H "Host: bp.example.com" localhost:8000/question
|
# curl -H "Host: bp.example.com" localhost:8000/question
|
||||||
# curl -H "Host: bp.example.com" localhost:8000/answer
|
# curl -H "Host: bp.example.com" localhost:8000/answer
|
||||||
|
|
||||||
app = Sanic(__name__)
|
app = Sanic("Example")
|
||||||
bp = Blueprint("bp", host="bp.example.com")
|
bp = Blueprint("bp", host="bp.example.com")
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ from sanic import Sanic
|
||||||
from sanic.response import redirect
|
from sanic.response import redirect
|
||||||
|
|
||||||
|
|
||||||
app = Sanic(__name__)
|
app = Sanic("Example")
|
||||||
|
|
||||||
|
|
||||||
app.static("index.html", "websocket.html")
|
app.static("index.html", "websocket.html")
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
__version__ = "21.12.0dev"
|
__version__ = "21.12.0"
|
||||||
|
|
77
sanic/app.py
77
sanic/app.py
|
@ -44,7 +44,7 @@ from typing import (
|
||||||
Union,
|
Union,
|
||||||
)
|
)
|
||||||
from urllib.parse import urlencode, urlunparse
|
from urllib.parse import urlencode, urlunparse
|
||||||
from warnings import filterwarnings, warn
|
from warnings import filterwarnings
|
||||||
|
|
||||||
from sanic_routing.exceptions import ( # type: ignore
|
from sanic_routing.exceptions import ( # type: ignore
|
||||||
FinalizationError,
|
FinalizationError,
|
||||||
|
@ -57,7 +57,7 @@ from sanic.application.logo import get_logo
|
||||||
from sanic.application.motd import MOTD
|
from sanic.application.motd import MOTD
|
||||||
from sanic.application.state import ApplicationState, Mode
|
from sanic.application.state import ApplicationState, Mode
|
||||||
from sanic.asgi import ASGIApp
|
from sanic.asgi import ASGIApp
|
||||||
from sanic.base import BaseSanic
|
from sanic.base.root import BaseSanic
|
||||||
from sanic.blueprint_group import BlueprintGroup
|
from sanic.blueprint_group import BlueprintGroup
|
||||||
from sanic.blueprints import Blueprint
|
from sanic.blueprints import Blueprint
|
||||||
from sanic.compat import OS_IS_WINDOWS, enable_windows_color_support
|
from sanic.compat import OS_IS_WINDOWS, enable_windows_color_support
|
||||||
|
@ -71,7 +71,13 @@ from sanic.exceptions import (
|
||||||
from sanic.handlers import ErrorHandler
|
from sanic.handlers import ErrorHandler
|
||||||
from sanic.helpers import _default
|
from sanic.helpers import _default
|
||||||
from sanic.http import Stage
|
from sanic.http import Stage
|
||||||
from sanic.log import LOGGING_CONFIG_DEFAULTS, Colors, error_logger, logger
|
from sanic.log import (
|
||||||
|
LOGGING_CONFIG_DEFAULTS,
|
||||||
|
Colors,
|
||||||
|
deprecation,
|
||||||
|
error_logger,
|
||||||
|
logger,
|
||||||
|
)
|
||||||
from sanic.mixins.listeners import ListenerEvent
|
from sanic.mixins.listeners import ListenerEvent
|
||||||
from sanic.models.futures import (
|
from sanic.models.futures import (
|
||||||
FutureException,
|
FutureException,
|
||||||
|
@ -85,7 +91,7 @@ from sanic.models.futures import (
|
||||||
from sanic.models.handler_types import ListenerType, MiddlewareType
|
from sanic.models.handler_types import ListenerType, MiddlewareType
|
||||||
from sanic.models.handler_types import Sanic as SanicVar
|
from sanic.models.handler_types import Sanic as SanicVar
|
||||||
from sanic.request import Request
|
from sanic.request import Request
|
||||||
from sanic.response import BaseHTTPResponse, HTTPResponse
|
from sanic.response import BaseHTTPResponse, HTTPResponse, ResponseStream
|
||||||
from sanic.router import Router
|
from sanic.router import Router
|
||||||
from sanic.server import AsyncioServer, HttpProtocol
|
from sanic.server import AsyncioServer, HttpProtocol
|
||||||
from sanic.server import Signal as ServerSignal
|
from sanic.server import Signal as ServerSignal
|
||||||
|
@ -114,8 +120,7 @@ class Sanic(BaseSanic, metaclass=TouchUpMeta):
|
||||||
"_run_response_middleware",
|
"_run_response_middleware",
|
||||||
"_run_request_middleware",
|
"_run_request_middleware",
|
||||||
)
|
)
|
||||||
__fake_slots__ = (
|
__slots__ = (
|
||||||
"_app_registry",
|
|
||||||
"_asgi_app",
|
"_asgi_app",
|
||||||
"_asgi_client",
|
"_asgi_client",
|
||||||
"_blueprint_order",
|
"_blueprint_order",
|
||||||
|
@ -131,19 +136,12 @@ class Sanic(BaseSanic, metaclass=TouchUpMeta):
|
||||||
"_task_registry",
|
"_task_registry",
|
||||||
"_test_client",
|
"_test_client",
|
||||||
"_test_manager",
|
"_test_manager",
|
||||||
"_uvloop_setting", # TODO: Remove in v22.6
|
|
||||||
"asgi",
|
|
||||||
"auto_reload",
|
|
||||||
"auto_reload",
|
|
||||||
"blueprints",
|
"blueprints",
|
||||||
"config",
|
"config",
|
||||||
"configure_logging",
|
"configure_logging",
|
||||||
"ctx",
|
"ctx",
|
||||||
"debug",
|
|
||||||
"error_handler",
|
"error_handler",
|
||||||
"go_fast",
|
"go_fast",
|
||||||
"is_running",
|
|
||||||
"is_stopping",
|
|
||||||
"listeners",
|
"listeners",
|
||||||
"name",
|
"name",
|
||||||
"named_request_middleware",
|
"named_request_middleware",
|
||||||
|
@ -155,13 +153,12 @@ class Sanic(BaseSanic, metaclass=TouchUpMeta):
|
||||||
"signal_router",
|
"signal_router",
|
||||||
"sock",
|
"sock",
|
||||||
"strict_slashes",
|
"strict_slashes",
|
||||||
"test_mode",
|
|
||||||
"websocket_enabled",
|
"websocket_enabled",
|
||||||
"websocket_tasks",
|
"websocket_tasks",
|
||||||
)
|
)
|
||||||
|
|
||||||
_app_registry: Dict[str, "Sanic"] = {}
|
_app_registry: Dict[str, "Sanic"] = {}
|
||||||
_uvloop_setting = None
|
_uvloop_setting = None # TODO: Remove in v22.6
|
||||||
test_mode = False
|
test_mode = False
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
|
@ -172,7 +169,6 @@ class Sanic(BaseSanic, metaclass=TouchUpMeta):
|
||||||
router: Optional[Router] = None,
|
router: Optional[Router] = None,
|
||||||
signal_router: Optional[SignalRouter] = None,
|
signal_router: Optional[SignalRouter] = None,
|
||||||
error_handler: Optional[ErrorHandler] = None,
|
error_handler: Optional[ErrorHandler] = None,
|
||||||
load_env: Union[bool, str] = True,
|
|
||||||
env_prefix: Optional[str] = SANIC_PREFIX,
|
env_prefix: Optional[str] = SANIC_PREFIX,
|
||||||
request_class: Optional[Type[Request]] = None,
|
request_class: Optional[Type[Request]] = None,
|
||||||
strict_slashes: bool = False,
|
strict_slashes: bool = False,
|
||||||
|
@ -188,17 +184,16 @@ class Sanic(BaseSanic, metaclass=TouchUpMeta):
|
||||||
dict_config = log_config or LOGGING_CONFIG_DEFAULTS
|
dict_config = log_config or LOGGING_CONFIG_DEFAULTS
|
||||||
logging.config.dictConfig(dict_config) # type: ignore
|
logging.config.dictConfig(dict_config) # type: ignore
|
||||||
|
|
||||||
if config and (load_env is not True or env_prefix != SANIC_PREFIX):
|
if config and env_prefix != SANIC_PREFIX:
|
||||||
raise SanicException(
|
raise SanicException(
|
||||||
"When instantiating Sanic with config, you cannot also pass "
|
"When instantiating Sanic with config, you cannot also pass "
|
||||||
"load_env or env_prefix"
|
"env_prefix"
|
||||||
)
|
)
|
||||||
|
|
||||||
self.config: Config = config or Config(
|
# First setup config
|
||||||
load_env=load_env,
|
self.config: Config = config or Config(env_prefix=env_prefix)
|
||||||
env_prefix=env_prefix,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
# Then we can do the rest
|
||||||
self._asgi_client: Any = None
|
self._asgi_client: Any = None
|
||||||
self._blueprint_order: List[Blueprint] = []
|
self._blueprint_order: List[Blueprint] = []
|
||||||
self._delayed_tasks: List[str] = []
|
self._delayed_tasks: List[str] = []
|
||||||
|
@ -231,6 +226,12 @@ class Sanic(BaseSanic, metaclass=TouchUpMeta):
|
||||||
self.go_fast = self.run
|
self.go_fast = self.run
|
||||||
|
|
||||||
if register is not None:
|
if register is not None:
|
||||||
|
deprecation(
|
||||||
|
"The register argument is deprecated and will stop working "
|
||||||
|
"in v22.6. After v22.6 all apps will be added to the Sanic "
|
||||||
|
"app registry.",
|
||||||
|
22.6,
|
||||||
|
)
|
||||||
self.config.REGISTER = register
|
self.config.REGISTER = register
|
||||||
if self.config.REGISTER:
|
if self.config.REGISTER:
|
||||||
self.__class__.register_app(self)
|
self.__class__.register_app(self)
|
||||||
|
@ -740,7 +741,7 @@ class Sanic(BaseSanic, metaclass=TouchUpMeta):
|
||||||
exception, request.name if request else None
|
exception, request.name if request else None
|
||||||
)
|
)
|
||||||
if handler:
|
if handler:
|
||||||
warn(
|
deprecation(
|
||||||
"An error occurred while handling the request after at "
|
"An error occurred while handling the request after at "
|
||||||
"least some part of the response was sent to the client. "
|
"least some part of the response was sent to the client. "
|
||||||
"Therefore, the response from your custom exception "
|
"Therefore, the response from your custom exception "
|
||||||
|
@ -755,7 +756,7 @@ class Sanic(BaseSanic, metaclass=TouchUpMeta):
|
||||||
"For further information, please see the docs: "
|
"For further information, please see the docs: "
|
||||||
"https://sanicframework.org/en/guide/advanced/"
|
"https://sanicframework.org/en/guide/advanced/"
|
||||||
"signals.html",
|
"signals.html",
|
||||||
DeprecationWarning,
|
22.6,
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
response = self.error_handler.response(request, exception)
|
response = self.error_handler.response(request, exception)
|
||||||
|
@ -808,6 +809,9 @@ class Sanic(BaseSanic, metaclass=TouchUpMeta):
|
||||||
else:
|
else:
|
||||||
if request.stream:
|
if request.stream:
|
||||||
response = request.stream.response
|
response = request.stream.response
|
||||||
|
|
||||||
|
# Marked for cleanup and DRY with handle_request/handle_exception
|
||||||
|
# when ResponseStream is no longer supporder
|
||||||
if isinstance(response, BaseHTTPResponse):
|
if isinstance(response, BaseHTTPResponse):
|
||||||
await self.dispatch(
|
await self.dispatch(
|
||||||
"http.lifecycle.response",
|
"http.lifecycle.response",
|
||||||
|
@ -818,6 +822,17 @@ class Sanic(BaseSanic, metaclass=TouchUpMeta):
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
await response.send(end_stream=True)
|
await response.send(end_stream=True)
|
||||||
|
elif isinstance(response, ResponseStream):
|
||||||
|
resp = await response(request)
|
||||||
|
await self.dispatch(
|
||||||
|
"http.lifecycle.response",
|
||||||
|
inline=True,
|
||||||
|
context={
|
||||||
|
"request": request,
|
||||||
|
"response": resp,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
await response.eof()
|
||||||
else:
|
else:
|
||||||
raise ServerError(
|
raise ServerError(
|
||||||
f"Invalid response type {response!r} (need HTTPResponse)"
|
f"Invalid response type {response!r} (need HTTPResponse)"
|
||||||
|
@ -921,7 +936,8 @@ class Sanic(BaseSanic, metaclass=TouchUpMeta):
|
||||||
elif not hasattr(handler, "is_websocket"):
|
elif not hasattr(handler, "is_websocket"):
|
||||||
response = request.stream.response # type: ignore
|
response = request.stream.response # type: ignore
|
||||||
|
|
||||||
# Make sure that response is finished / run StreamingHTTP callback
|
# Marked for cleanup and DRY with handle_request/handle_exception
|
||||||
|
# when ResponseStream is no longer supporder
|
||||||
if isinstance(response, BaseHTTPResponse):
|
if isinstance(response, BaseHTTPResponse):
|
||||||
await self.dispatch(
|
await self.dispatch(
|
||||||
"http.lifecycle.response",
|
"http.lifecycle.response",
|
||||||
|
@ -932,6 +948,17 @@ class Sanic(BaseSanic, metaclass=TouchUpMeta):
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
await response.send(end_stream=True)
|
await response.send(end_stream=True)
|
||||||
|
elif isinstance(response, ResponseStream):
|
||||||
|
resp = await response(request)
|
||||||
|
await self.dispatch(
|
||||||
|
"http.lifecycle.response",
|
||||||
|
inline=True,
|
||||||
|
context={
|
||||||
|
"request": request,
|
||||||
|
"response": resp,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
await response.eof()
|
||||||
else:
|
else:
|
||||||
if not hasattr(handler, "is_websocket"):
|
if not hasattr(handler, "is_websocket"):
|
||||||
raise ServerError(
|
raise ServerError(
|
||||||
|
|
0
sanic/base/__init__.py
Normal file
0
sanic/base/__init__.py
Normal file
6
sanic/base/meta.py
Normal file
6
sanic/base/meta.py
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
class SanicMeta(type):
|
||||||
|
@classmethod
|
||||||
|
def __prepare__(metaclass, name, bases, **kwds):
|
||||||
|
cls = super().__prepare__(metaclass, name, bases, **kwds)
|
||||||
|
cls["__slots__"] = ()
|
||||||
|
return cls
|
|
@ -1,8 +1,8 @@
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from typing import Any, Tuple
|
from typing import Any
|
||||||
from warnings import warn
|
|
||||||
|
|
||||||
|
from sanic.base.meta import SanicMeta
|
||||||
from sanic.exceptions import SanicException
|
from sanic.exceptions import SanicException
|
||||||
from sanic.mixins.exceptions import ExceptionMixin
|
from sanic.mixins.exceptions import ExceptionMixin
|
||||||
from sanic.mixins.listeners import ListenerMixin
|
from sanic.mixins.listeners import ListenerMixin
|
||||||
|
@ -20,8 +20,9 @@ class BaseSanic(
|
||||||
ListenerMixin,
|
ListenerMixin,
|
||||||
ExceptionMixin,
|
ExceptionMixin,
|
||||||
SignalMixin,
|
SignalMixin,
|
||||||
|
metaclass=SanicMeta,
|
||||||
):
|
):
|
||||||
__fake_slots__: Tuple[str, ...]
|
__slots__ = ("name",)
|
||||||
|
|
||||||
def __init__(self, name: str = None, *args: Any, **kwargs: Any) -> None:
|
def __init__(self, name: str = None, *args: Any, **kwargs: Any) -> None:
|
||||||
class_name = self.__class__.__name__
|
class_name = self.__class__.__name__
|
||||||
|
@ -33,11 +34,10 @@ class BaseSanic(
|
||||||
)
|
)
|
||||||
|
|
||||||
if not VALID_NAME.match(name):
|
if not VALID_NAME.match(name):
|
||||||
warn(
|
raise SanicException(
|
||||||
f"{class_name} instance named '{name}' uses a format that is"
|
f"{class_name} instance named '{name}' uses an invalid "
|
||||||
f"deprecated. Starting in version 21.12, {class_name} objects "
|
"format. Names must begin with a character and may only "
|
||||||
"must be named only using alphanumeric characters, _, or -.",
|
"contain alphanumeric characters, _, or -."
|
||||||
DeprecationWarning,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
self.name = name
|
self.name = name
|
||||||
|
@ -52,15 +52,12 @@ class BaseSanic(
|
||||||
return f'{self.__class__.__name__}(name="{self.name}")'
|
return f'{self.__class__.__name__}(name="{self.name}")'
|
||||||
|
|
||||||
def __setattr__(self, name: str, value: Any) -> None:
|
def __setattr__(self, name: str, value: Any) -> None:
|
||||||
# This is a temporary compat layer so we can raise a warning until
|
try:
|
||||||
# setting attributes on the app instance can be removed and deprecated
|
|
||||||
# with a proper implementation of __slots__
|
|
||||||
if name not in self.__fake_slots__:
|
|
||||||
warn(
|
|
||||||
f"Setting variables on {self.__class__.__name__} instances is "
|
|
||||||
"deprecated and will be removed in version 21.12. You should "
|
|
||||||
f"change your {self.__class__.__name__} instance to use "
|
|
||||||
f"instance.ctx.{name} instead.",
|
|
||||||
DeprecationWarning,
|
|
||||||
)
|
|
||||||
super().__setattr__(name, value)
|
super().__setattr__(name, value)
|
||||||
|
except AttributeError as e:
|
||||||
|
raise AttributeError(
|
||||||
|
f"Setting variables on {self.__class__.__name__} instances is "
|
||||||
|
"not allowed. You should change your "
|
||||||
|
f"{self.__class__.__name__} instance to use "
|
||||||
|
f"instance.ctx.{name} instead.",
|
||||||
|
) from e
|
|
@ -24,7 +24,7 @@ from typing import (
|
||||||
from sanic_routing.exceptions import NotFound # type: ignore
|
from sanic_routing.exceptions import NotFound # type: ignore
|
||||||
from sanic_routing.route import Route # type: ignore
|
from sanic_routing.route import Route # type: ignore
|
||||||
|
|
||||||
from sanic.base import BaseSanic
|
from sanic.base.root import BaseSanic
|
||||||
from sanic.blueprint_group import BlueprintGroup
|
from sanic.blueprint_group import BlueprintGroup
|
||||||
from sanic.exceptions import SanicException
|
from sanic.exceptions import SanicException
|
||||||
from sanic.helpers import Default, _default
|
from sanic.helpers import Default, _default
|
||||||
|
@ -85,7 +85,7 @@ class Blueprint(BaseSanic):
|
||||||
trailing */*
|
trailing */*
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__fake_slots__ = (
|
__slots__ = (
|
||||||
"_apps",
|
"_apps",
|
||||||
"_future_routes",
|
"_future_routes",
|
||||||
"_future_statics",
|
"_future_statics",
|
||||||
|
@ -98,7 +98,6 @@ class Blueprint(BaseSanic):
|
||||||
"host",
|
"host",
|
||||||
"listeners",
|
"listeners",
|
||||||
"middlewares",
|
"middlewares",
|
||||||
"name",
|
|
||||||
"routes",
|
"routes",
|
||||||
"statics",
|
"statics",
|
||||||
"strict_slashes",
|
"strict_slashes",
|
||||||
|
|
|
@ -4,12 +4,11 @@ from inspect import getmembers, isclass, isdatadescriptor
|
||||||
from os import environ
|
from os import environ
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Callable, Dict, Optional, Sequence, Union
|
from typing import Any, Callable, Dict, Optional, Sequence, Union
|
||||||
from warnings import warn
|
|
||||||
|
|
||||||
from sanic.errorpages import DEFAULT_FORMAT, check_error_format
|
from sanic.errorpages import DEFAULT_FORMAT, check_error_format
|
||||||
from sanic.helpers import Default, _default
|
from sanic.helpers import Default, _default
|
||||||
from sanic.http import Http
|
from sanic.http import Http
|
||||||
from sanic.log import error_logger
|
from sanic.log import deprecation, error_logger
|
||||||
from sanic.utils import load_module_from_file_location, str_to_bool
|
from sanic.utils import load_module_from_file_location, str_to_bool
|
||||||
|
|
||||||
|
|
||||||
|
@ -88,7 +87,6 @@ class Config(dict, metaclass=DescriptorMeta):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
defaults: Dict[str, Union[str, bool, int, float, None]] = None,
|
defaults: Dict[str, Union[str, bool, int, float, None]] = None,
|
||||||
load_env: Optional[Union[bool, str]] = True,
|
|
||||||
env_prefix: Optional[str] = SANIC_PREFIX,
|
env_prefix: Optional[str] = SANIC_PREFIX,
|
||||||
keep_alive: Optional[bool] = None,
|
keep_alive: Optional[bool] = None,
|
||||||
*,
|
*,
|
||||||
|
@ -110,15 +108,6 @@ class Config(dict, metaclass=DescriptorMeta):
|
||||||
if env_prefix != SANIC_PREFIX:
|
if env_prefix != SANIC_PREFIX:
|
||||||
if env_prefix:
|
if env_prefix:
|
||||||
self.load_environment_vars(env_prefix)
|
self.load_environment_vars(env_prefix)
|
||||||
elif load_env is not True:
|
|
||||||
if load_env:
|
|
||||||
self.load_environment_vars(prefix=load_env)
|
|
||||||
warn(
|
|
||||||
"Use of load_env is deprecated and will be removed in "
|
|
||||||
"21.12. Modify the configuration prefix by passing "
|
|
||||||
"env_prefix instead.",
|
|
||||||
DeprecationWarning,
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
self.load_environment_vars(SANIC_PREFIX)
|
self.load_environment_vars(SANIC_PREFIX)
|
||||||
|
|
||||||
|
@ -161,10 +150,10 @@ class Config(dict, metaclass=DescriptorMeta):
|
||||||
self._configure_header_size()
|
self._configure_header_size()
|
||||||
elif attr == "LOGO":
|
elif attr == "LOGO":
|
||||||
self._LOGO = value
|
self._LOGO = value
|
||||||
warn(
|
deprecation(
|
||||||
"Setting the config.LOGO is deprecated and will no longer "
|
"Setting the config.LOGO is deprecated and will no longer "
|
||||||
"be supported starting in v22.6.",
|
"be supported starting in v22.6.",
|
||||||
DeprecationWarning,
|
22.6,
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
|
@ -244,25 +244,3 @@ class InvalidSignal(SanicException):
|
||||||
class WebsocketClosed(SanicException):
|
class WebsocketClosed(SanicException):
|
||||||
quiet = True
|
quiet = True
|
||||||
message = "Client has closed the websocket connection"
|
message = "Client has closed the websocket connection"
|
||||||
|
|
||||||
|
|
||||||
def abort(status_code: int, message: Optional[Union[str, bytes]] = None):
|
|
||||||
"""
|
|
||||||
Raise an exception based on SanicException. Returns the HTTP response
|
|
||||||
message appropriate for the given status code, unless provided.
|
|
||||||
|
|
||||||
STATUS_CODES from sanic.helpers for the given status code.
|
|
||||||
|
|
||||||
:param status_code: The HTTP status code to return.
|
|
||||||
:param message: The HTTP response body. Defaults to the messages in
|
|
||||||
"""
|
|
||||||
import warnings
|
|
||||||
|
|
||||||
warnings.warn(
|
|
||||||
"sanic.exceptions.abort has been marked as deprecated, and will be "
|
|
||||||
"removed in release 21.12.\n To migrate your code, simply replace "
|
|
||||||
"abort(status_code, msg) with raise SanicException(msg, status_code), "
|
|
||||||
"or even better, raise an appropriate SanicException subclass."
|
|
||||||
)
|
|
||||||
|
|
||||||
raise SanicException(message=message, status_code=status_code)
|
|
||||||
|
|
|
@ -2,7 +2,6 @@ from __future__ import annotations
|
||||||
|
|
||||||
from inspect import signature
|
from inspect import signature
|
||||||
from typing import Dict, List, Optional, Tuple, Type, Union
|
from typing import Dict, List, Optional, Tuple, Type, Union
|
||||||
from warnings import warn
|
|
||||||
|
|
||||||
from sanic.config import Config
|
from sanic.config import Config
|
||||||
from sanic.errorpages import (
|
from sanic.errorpages import (
|
||||||
|
@ -18,7 +17,7 @@ from sanic.exceptions import (
|
||||||
SanicException,
|
SanicException,
|
||||||
)
|
)
|
||||||
from sanic.helpers import Default, _default
|
from sanic.helpers import Default, _default
|
||||||
from sanic.log import error_logger
|
from sanic.log import deprecation, error_logger
|
||||||
from sanic.models.handler_types import RouteHandler
|
from sanic.models.handler_types import RouteHandler
|
||||||
from sanic.response import text
|
from sanic.response import text
|
||||||
|
|
||||||
|
@ -71,12 +70,12 @@ class ErrorHandler:
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _warn_fallback_deprecation():
|
def _warn_fallback_deprecation():
|
||||||
warn(
|
deprecation(
|
||||||
"Setting the ErrorHandler fallback value directly is "
|
"Setting the ErrorHandler fallback value directly is "
|
||||||
"deprecated and no longer supported. This feature will "
|
"deprecated and no longer supported. This feature will "
|
||||||
"be removed in v22.6. Instead, use "
|
"be removed in v22.6. Instead, use "
|
||||||
"app.config.FALLBACK_ERROR_FORMAT.",
|
"app.config.FALLBACK_ERROR_FORMAT.",
|
||||||
DeprecationWarning,
|
22.6,
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -100,19 +99,19 @@ class ErrorHandler:
|
||||||
config: Optional[Config] = None,
|
config: Optional[Config] = None,
|
||||||
):
|
):
|
||||||
if fallback:
|
if fallback:
|
||||||
warn(
|
deprecation(
|
||||||
"Setting the ErrorHandler fallback value via finalize() "
|
"Setting the ErrorHandler fallback value via finalize() "
|
||||||
"is deprecated and no longer supported. This feature will "
|
"is deprecated and no longer supported. This feature will "
|
||||||
"be removed in v22.6. Instead, use "
|
"be removed in v22.6. Instead, use "
|
||||||
"app.config.FALLBACK_ERROR_FORMAT.",
|
"app.config.FALLBACK_ERROR_FORMAT.",
|
||||||
DeprecationWarning,
|
22.6,
|
||||||
)
|
)
|
||||||
|
|
||||||
if config is None:
|
if config is None:
|
||||||
warn(
|
deprecation(
|
||||||
"Starting in v22.3, config will be a required argument "
|
"Starting in v22.3, config will be a required argument "
|
||||||
"for ErrorHandler.finalize().",
|
"for ErrorHandler.finalize().",
|
||||||
DeprecationWarning,
|
22.3,
|
||||||
)
|
)
|
||||||
|
|
||||||
if fallback and fallback != DEFAULT_FORMAT:
|
if fallback and fallback != DEFAULT_FORMAT:
|
||||||
|
@ -131,7 +130,7 @@ class ErrorHandler:
|
||||||
|
|
||||||
sig = signature(error_handler.lookup)
|
sig = signature(error_handler.lookup)
|
||||||
if len(sig.parameters) == 1:
|
if len(sig.parameters) == 1:
|
||||||
warn(
|
deprecation(
|
||||||
"You are using a deprecated error handler. The lookup "
|
"You are using a deprecated error handler. The lookup "
|
||||||
"method should accept two positional parameters: "
|
"method should accept two positional parameters: "
|
||||||
"(exception, route_name: Optional[str]). "
|
"(exception, route_name: Optional[str]). "
|
||||||
|
@ -139,7 +138,7 @@ class ErrorHandler:
|
||||||
"specific exceptions will not work properly. Beginning "
|
"specific exceptions will not work properly. Beginning "
|
||||||
"in v22.3, the legacy style lookup method will not "
|
"in v22.3, the legacy style lookup method will not "
|
||||||
"work at all.",
|
"work at all.",
|
||||||
DeprecationWarning,
|
22.3,
|
||||||
)
|
)
|
||||||
legacy_lookup = error_handler._legacy_lookup
|
legacy_lookup = error_handler._legacy_lookup
|
||||||
error_handler._lookup = legacy_lookup # type: ignore
|
error_handler._lookup = legacy_lookup # type: ignore
|
||||||
|
|
|
@ -3,6 +3,7 @@ import sys
|
||||||
|
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import Any, Dict
|
from typing import Any, Dict
|
||||||
|
from warnings import warn
|
||||||
|
|
||||||
|
|
||||||
LOGGING_CONFIG_DEFAULTS: Dict[str, Any] = dict(
|
LOGGING_CONFIG_DEFAULTS: Dict[str, Any] = dict(
|
||||||
|
@ -78,3 +79,11 @@ access_logger = logging.getLogger("sanic.access")
|
||||||
"""
|
"""
|
||||||
Logger used by Sanic for access logging
|
Logger used by Sanic for access logging
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def deprecation(message: str, version: float):
|
||||||
|
version_info = f"[DEPRECATION v{version}] "
|
||||||
|
if sys.stdout.isatty():
|
||||||
|
version_info = f"{Colors.RED}{version_info}"
|
||||||
|
message = f"{Colors.YELLOW}{message}{Colors.END}"
|
||||||
|
warn(version_info + message, DeprecationWarning)
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
from typing import Set
|
from typing import Set
|
||||||
|
|
||||||
|
from sanic.base.meta import SanicMeta
|
||||||
from sanic.models.futures import FutureException
|
from sanic.models.futures import FutureException
|
||||||
|
|
||||||
|
|
||||||
class ExceptionMixin:
|
class ExceptionMixin(metaclass=SanicMeta):
|
||||||
def __init__(self, *args, **kwargs) -> None:
|
def __init__(self, *args, **kwargs) -> None:
|
||||||
self._future_exceptions: Set[FutureException] = set()
|
self._future_exceptions: Set[FutureException] = set()
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ from enum import Enum, auto
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from typing import List, Optional, Union
|
from typing import List, Optional, Union
|
||||||
|
|
||||||
|
from sanic.base.meta import SanicMeta
|
||||||
from sanic.models.futures import FutureListener
|
from sanic.models.futures import FutureListener
|
||||||
from sanic.models.handler_types import ListenerType, Sanic
|
from sanic.models.handler_types import ListenerType, Sanic
|
||||||
|
|
||||||
|
@ -18,7 +19,7 @@ class ListenerEvent(str, Enum):
|
||||||
MAIN_PROCESS_STOP = auto()
|
MAIN_PROCESS_STOP = auto()
|
||||||
|
|
||||||
|
|
||||||
class ListenerMixin:
|
class ListenerMixin(metaclass=SanicMeta):
|
||||||
def __init__(self, *args, **kwargs) -> None:
|
def __init__(self, *args, **kwargs) -> None:
|
||||||
self._future_listeners: List[FutureListener] = []
|
self._future_listeners: List[FutureListener] = []
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
|
from sanic.base.meta import SanicMeta
|
||||||
from sanic.models.futures import FutureMiddleware
|
from sanic.models.futures import FutureMiddleware
|
||||||
|
|
||||||
|
|
||||||
class MiddlewareMixin:
|
class MiddlewareMixin(metaclass=SanicMeta):
|
||||||
def __init__(self, *args, **kwargs) -> None:
|
def __init__(self, *args, **kwargs) -> None:
|
||||||
self._future_middleware: List[FutureMiddleware] = []
|
self._future_middleware: List[FutureMiddleware] = []
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from ast import NodeVisitor, Return, parse
|
from ast import NodeVisitor, Return, parse
|
||||||
|
from contextlib import suppress
|
||||||
from functools import partial, wraps
|
from functools import partial, wraps
|
||||||
from inspect import getsource, signature
|
from inspect import getsource, signature
|
||||||
from mimetypes import guess_type
|
from mimetypes import guess_type
|
||||||
|
@ -12,6 +13,7 @@ from urllib.parse import unquote
|
||||||
|
|
||||||
from sanic_routing.route import Route # type: ignore
|
from sanic_routing.route import Route # type: ignore
|
||||||
|
|
||||||
|
from sanic.base.meta import SanicMeta
|
||||||
from sanic.compat import stat_async
|
from sanic.compat import stat_async
|
||||||
from sanic.constants import DEFAULT_HTTP_CONTENT_TYPE, HTTP_METHODS
|
from sanic.constants import DEFAULT_HTTP_CONTENT_TYPE, HTTP_METHODS
|
||||||
from sanic.errorpages import RESPONSE_MAPPING
|
from sanic.errorpages import RESPONSE_MAPPING
|
||||||
|
@ -22,12 +24,11 @@ from sanic.exceptions import (
|
||||||
InvalidUsage,
|
InvalidUsage,
|
||||||
)
|
)
|
||||||
from sanic.handlers import ContentRangeHandler
|
from sanic.handlers import ContentRangeHandler
|
||||||
from sanic.log import error_logger
|
from sanic.log import deprecation, error_logger
|
||||||
from sanic.models.futures import FutureRoute, FutureStatic
|
from sanic.models.futures import FutureRoute, FutureStatic
|
||||||
from sanic.models.handler_types import RouteHandler
|
from sanic.models.handler_types import RouteHandler
|
||||||
from sanic.response import HTTPResponse, file, file_stream
|
from sanic.response import HTTPResponse, file, file_stream
|
||||||
from sanic.types import HashableDict
|
from sanic.types import HashableDict
|
||||||
from sanic.views import CompositionView
|
|
||||||
|
|
||||||
|
|
||||||
RouteWrapper = Callable[
|
RouteWrapper = Callable[
|
||||||
|
@ -43,7 +44,7 @@ RESTRICTED_ROUTE_CONTEXT = (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class RouteMixin:
|
class RouteMixin(metaclass=SanicMeta):
|
||||||
name: str
|
name: str
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs) -> None:
|
def __init__(self, *args, **kwargs) -> None:
|
||||||
|
@ -253,14 +254,6 @@ class RouteMixin:
|
||||||
if hasattr(_handler, "is_stream"):
|
if hasattr(_handler, "is_stream"):
|
||||||
stream = True
|
stream = True
|
||||||
|
|
||||||
# handle composition view differently
|
|
||||||
if isinstance(handler, CompositionView):
|
|
||||||
methods = handler.handlers.keys()
|
|
||||||
for _handler in handler.handlers.values():
|
|
||||||
if hasattr(_handler, "is_stream"):
|
|
||||||
stream = True
|
|
||||||
break
|
|
||||||
|
|
||||||
if strict_slashes is None:
|
if strict_slashes is None:
|
||||||
strict_slashes = self.strict_slashes
|
strict_slashes = self.strict_slashes
|
||||||
|
|
||||||
|
@ -982,19 +975,16 @@ class RouteMixin:
|
||||||
|
|
||||||
return route
|
return route
|
||||||
|
|
||||||
def _determine_error_format(self, handler) -> Optional[str]:
|
def _determine_error_format(self, handler) -> str:
|
||||||
if not isinstance(handler, CompositionView):
|
with suppress(OSError, TypeError):
|
||||||
try:
|
|
||||||
src = dedent(getsource(handler))
|
src = dedent(getsource(handler))
|
||||||
tree = parse(src)
|
tree = parse(src)
|
||||||
http_response_types = self._get_response_types(tree)
|
http_response_types = self._get_response_types(tree)
|
||||||
|
|
||||||
if len(http_response_types) == 1:
|
if len(http_response_types) == 1:
|
||||||
return next(iter(http_response_types))
|
return next(iter(http_response_types))
|
||||||
except (OSError, TypeError):
|
|
||||||
...
|
|
||||||
|
|
||||||
return None
|
return ""
|
||||||
|
|
||||||
def _get_response_types(self, node):
|
def _get_response_types(self, node):
|
||||||
types = set()
|
types = set()
|
||||||
|
@ -1003,7 +993,18 @@ class RouteMixin:
|
||||||
def visit_Return(self, node: Return) -> Any:
|
def visit_Return(self, node: Return) -> Any:
|
||||||
nonlocal types
|
nonlocal types
|
||||||
|
|
||||||
try:
|
with suppress(AttributeError):
|
||||||
|
if node.value.func.id == "stream": # type: ignore
|
||||||
|
deprecation(
|
||||||
|
"The sanic.response.stream method has been "
|
||||||
|
"deprecated and will be removed in v22.6. Please "
|
||||||
|
"upgrade your application to use the new style "
|
||||||
|
"streaming pattern. See "
|
||||||
|
"https://sanicframework.org/en/guide/advanced/"
|
||||||
|
"streaming.html#response-streaming for more "
|
||||||
|
"information.",
|
||||||
|
22.6,
|
||||||
|
)
|
||||||
checks = [node.value.func.id] # type: ignore
|
checks = [node.value.func.id] # type: ignore
|
||||||
if node.value.keywords: # type: ignore
|
if node.value.keywords: # type: ignore
|
||||||
checks += [
|
checks += [
|
||||||
|
@ -1015,8 +1016,6 @@ class RouteMixin:
|
||||||
for check in checks:
|
for check in checks:
|
||||||
if check in RESPONSE_MAPPING:
|
if check in RESPONSE_MAPPING:
|
||||||
types.add(RESPONSE_MAPPING[check])
|
types.add(RESPONSE_MAPPING[check])
|
||||||
except AttributeError:
|
|
||||||
...
|
|
||||||
|
|
||||||
HttpResponseVisitor().visit(node)
|
HttpResponseVisitor().visit(node)
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import Any, Callable, Dict, Optional, Set, Union
|
from typing import Any, Callable, Dict, Optional, Set, Union
|
||||||
|
|
||||||
|
from sanic.base.meta import SanicMeta
|
||||||
from sanic.models.futures import FutureSignal
|
from sanic.models.futures import FutureSignal
|
||||||
from sanic.models.handler_types import SignalHandler
|
from sanic.models.handler_types import SignalHandler
|
||||||
from sanic.signals import Signal
|
from sanic.signals import Signal
|
||||||
from sanic.types import HashableDict
|
from sanic.types import HashableDict
|
||||||
|
|
||||||
|
|
||||||
class SignalMixin:
|
class SignalMixin(metaclass=SanicMeta):
|
||||||
def __init__(self, *args, **kwargs) -> None:
|
def __init__(self, *args, **kwargs) -> None:
|
||||||
self._future_signals: Set[FutureSignal] = set()
|
self._future_signals: Set[FutureSignal] = set()
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from mimetypes import guess_type
|
from mimetypes import guess_type
|
||||||
from os import path
|
from os import path
|
||||||
|
@ -12,10 +14,10 @@ from typing import (
|
||||||
Iterator,
|
Iterator,
|
||||||
Optional,
|
Optional,
|
||||||
Tuple,
|
Tuple,
|
||||||
|
TypeVar,
|
||||||
Union,
|
Union,
|
||||||
)
|
)
|
||||||
from urllib.parse import quote_plus
|
from urllib.parse import quote_plus
|
||||||
from warnings import warn
|
|
||||||
|
|
||||||
from sanic.compat import Header, open_async
|
from sanic.compat import Header, open_async
|
||||||
from sanic.constants import DEFAULT_HTTP_CONTENT_TYPE
|
from sanic.constants import DEFAULT_HTTP_CONTENT_TYPE
|
||||||
|
@ -28,6 +30,10 @@ from sanic.models.protocol_types import HTMLProtocol, Range
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from sanic.asgi import ASGIApp
|
from sanic.asgi import ASGIApp
|
||||||
|
from sanic.request import Request
|
||||||
|
else:
|
||||||
|
Request = TypeVar("Request")
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from ujson import dumps as json_dumps
|
from ujson import dumps as json_dumps
|
||||||
|
@ -136,95 +142,6 @@ class BaseHTTPResponse:
|
||||||
await self.stream.send(data, end_stream=end_stream)
|
await self.stream.send(data, end_stream=end_stream)
|
||||||
|
|
||||||
|
|
||||||
StreamingFunction = Callable[[BaseHTTPResponse], Coroutine[Any, Any, None]]
|
|
||||||
|
|
||||||
|
|
||||||
class StreamingHTTPResponse(BaseHTTPResponse):
|
|
||||||
"""
|
|
||||||
Old style streaming response where you pass a streaming function:
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
async def sample_streaming_fn(response):
|
|
||||||
await response.write("foo")
|
|
||||||
await asyncio.sleep(1)
|
|
||||||
await response.write("bar")
|
|
||||||
await asyncio.sleep(1)
|
|
||||||
|
|
||||||
@app.post("/")
|
|
||||||
async def test(request):
|
|
||||||
return stream(sample_streaming_fn)
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
|
|
||||||
**Deprecated** and set for removal in v21.12. You can now achieve the
|
|
||||||
same functionality without a callback.
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
@app.post("/")
|
|
||||||
async def test(request):
|
|
||||||
response = await request.respond()
|
|
||||||
await response.send("foo", False)
|
|
||||||
await asyncio.sleep(1)
|
|
||||||
await response.send("bar", False)
|
|
||||||
await asyncio.sleep(1)
|
|
||||||
await response.send("", True)
|
|
||||||
return response
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
__slots__ = (
|
|
||||||
"streaming_fn",
|
|
||||||
"status",
|
|
||||||
"content_type",
|
|
||||||
"headers",
|
|
||||||
"_cookies",
|
|
||||||
)
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
streaming_fn: StreamingFunction,
|
|
||||||
status: int = 200,
|
|
||||||
headers: Optional[Union[Header, Dict[str, str]]] = None,
|
|
||||||
content_type: str = "text/plain; charset=utf-8",
|
|
||||||
ignore_deprecation_notice: bool = False,
|
|
||||||
):
|
|
||||||
if not ignore_deprecation_notice:
|
|
||||||
warn(
|
|
||||||
"Use of the StreamingHTTPResponse is deprecated in v21.6, and "
|
|
||||||
"will be removed in v21.12. Please upgrade your streaming "
|
|
||||||
"response implementation. You can learn more here: "
|
|
||||||
"https://sanicframework.org/en/guide/advanced/streaming.html"
|
|
||||||
"#response-streaming. If you use the builtin stream() or "
|
|
||||||
"file_stream() methods, this upgrade will be be done for you."
|
|
||||||
)
|
|
||||||
|
|
||||||
super().__init__()
|
|
||||||
|
|
||||||
self.content_type = content_type
|
|
||||||
self.streaming_fn = streaming_fn
|
|
||||||
self.status = status
|
|
||||||
self.headers = Header(headers or {})
|
|
||||||
self._cookies = None
|
|
||||||
|
|
||||||
async def write(self, data):
|
|
||||||
"""Writes a chunk of data to the streaming response.
|
|
||||||
|
|
||||||
:param data: str or bytes-ish data to be written.
|
|
||||||
"""
|
|
||||||
await super().send(self._encode_body(data))
|
|
||||||
|
|
||||||
async def send(self, *args, **kwargs):
|
|
||||||
if self.streaming_fn is not None:
|
|
||||||
await self.streaming_fn(self)
|
|
||||||
self.streaming_fn = None
|
|
||||||
await super().send(*args, **kwargs)
|
|
||||||
|
|
||||||
async def eof(self):
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
|
|
||||||
class HTTPResponse(BaseHTTPResponse):
|
class HTTPResponse(BaseHTTPResponse):
|
||||||
"""
|
"""
|
||||||
HTTP response to be sent back to the client.
|
HTTP response to be sent back to the client.
|
||||||
|
@ -419,6 +336,109 @@ async def file(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def redirect(
|
||||||
|
to: str,
|
||||||
|
headers: Optional[Dict[str, str]] = None,
|
||||||
|
status: int = 302,
|
||||||
|
content_type: str = "text/html; charset=utf-8",
|
||||||
|
) -> HTTPResponse:
|
||||||
|
"""
|
||||||
|
Abort execution and cause a 302 redirect (by default) by setting a
|
||||||
|
Location header.
|
||||||
|
|
||||||
|
:param to: path or fully qualified URL to redirect to
|
||||||
|
:param headers: optional dict of headers to include in the new request
|
||||||
|
:param status: status code (int) of the new request, defaults to 302
|
||||||
|
:param content_type: the content type (string) of the response
|
||||||
|
"""
|
||||||
|
headers = headers or {}
|
||||||
|
|
||||||
|
# URL Quote the URL before redirecting
|
||||||
|
safe_to = quote_plus(to, safe=":/%#?&=@[]!$&'()*+,;")
|
||||||
|
|
||||||
|
# According to RFC 7231, a relative URI is now permitted.
|
||||||
|
headers["Location"] = safe_to
|
||||||
|
|
||||||
|
return HTTPResponse(
|
||||||
|
status=status, headers=headers, content_type=content_type
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ResponseStream:
|
||||||
|
"""
|
||||||
|
ResponseStream is a compat layer to bridge the gap after the deprecation
|
||||||
|
of StreamingHTTPResponse. In v22.6 it will be removed when:
|
||||||
|
- stream is removed
|
||||||
|
- file_stream is moved to new style streaming
|
||||||
|
- file and file_stream are combined into a single API
|
||||||
|
"""
|
||||||
|
|
||||||
|
__slots__ = (
|
||||||
|
"_cookies",
|
||||||
|
"content_type",
|
||||||
|
"headers",
|
||||||
|
"request",
|
||||||
|
"response",
|
||||||
|
"status",
|
||||||
|
"streaming_fn",
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
streaming_fn: Callable[
|
||||||
|
[Union[BaseHTTPResponse, ResponseStream]],
|
||||||
|
Coroutine[Any, Any, None],
|
||||||
|
],
|
||||||
|
status: int = 200,
|
||||||
|
headers: Optional[Union[Header, Dict[str, str]]] = None,
|
||||||
|
content_type: Optional[str] = None,
|
||||||
|
):
|
||||||
|
self.streaming_fn = streaming_fn
|
||||||
|
self.status = status
|
||||||
|
self.headers = headers or Header()
|
||||||
|
self.content_type = content_type
|
||||||
|
self.request: Optional[Request] = None
|
||||||
|
self._cookies: Optional[CookieJar] = None
|
||||||
|
|
||||||
|
async def write(self, message: str):
|
||||||
|
await self.response.send(message)
|
||||||
|
|
||||||
|
async def stream(self) -> HTTPResponse:
|
||||||
|
if not self.request:
|
||||||
|
raise ServerError("Attempted response to unknown request")
|
||||||
|
self.response = await self.request.respond(
|
||||||
|
headers=self.headers,
|
||||||
|
status=self.status,
|
||||||
|
content_type=self.content_type,
|
||||||
|
)
|
||||||
|
await self.streaming_fn(self)
|
||||||
|
return self.response
|
||||||
|
|
||||||
|
async def eof(self) -> None:
|
||||||
|
await self.response.eof()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def cookies(self) -> CookieJar:
|
||||||
|
if self._cookies is None:
|
||||||
|
self._cookies = CookieJar(self.headers)
|
||||||
|
return self._cookies
|
||||||
|
|
||||||
|
@property
|
||||||
|
def processed_headers(self):
|
||||||
|
return self.response.processed_headers
|
||||||
|
|
||||||
|
@property
|
||||||
|
def body(self):
|
||||||
|
return self.response.body
|
||||||
|
|
||||||
|
def __call__(self, request: Request) -> ResponseStream:
|
||||||
|
self.request = request
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __await__(self):
|
||||||
|
return self.stream().__await__()
|
||||||
|
|
||||||
|
|
||||||
async def file_stream(
|
async def file_stream(
|
||||||
location: Union[str, PurePath],
|
location: Union[str, PurePath],
|
||||||
status: int = 200,
|
status: int = 200,
|
||||||
|
@ -427,7 +447,7 @@ async def file_stream(
|
||||||
headers: Optional[Dict[str, str]] = None,
|
headers: Optional[Dict[str, str]] = None,
|
||||||
filename: Optional[str] = None,
|
filename: Optional[str] = None,
|
||||||
_range: Optional[Range] = None,
|
_range: Optional[Range] = None,
|
||||||
) -> StreamingHTTPResponse:
|
) -> ResponseStream:
|
||||||
"""Return a streaming response object with file data.
|
"""Return a streaming response object with file data.
|
||||||
|
|
||||||
:param location: Location of file on system.
|
:param location: Location of file on system.
|
||||||
|
@ -435,7 +455,6 @@ async def file_stream(
|
||||||
:param mime_type: Specific mime_type.
|
:param mime_type: Specific mime_type.
|
||||||
:param headers: Custom Headers.
|
:param headers: Custom Headers.
|
||||||
:param filename: Override filename.
|
:param filename: Override filename.
|
||||||
:param chunked: Deprecated
|
|
||||||
:param _range:
|
:param _range:
|
||||||
"""
|
"""
|
||||||
headers = headers or {}
|
headers = headers or {}
|
||||||
|
@ -471,23 +490,24 @@ async def file_stream(
|
||||||
break
|
break
|
||||||
await response.write(content)
|
await response.write(content)
|
||||||
|
|
||||||
return StreamingHTTPResponse(
|
return ResponseStream(
|
||||||
streaming_fn=_streaming_fn,
|
streaming_fn=_streaming_fn,
|
||||||
status=status,
|
status=status,
|
||||||
headers=headers,
|
headers=headers,
|
||||||
content_type=mime_type,
|
content_type=mime_type,
|
||||||
ignore_deprecation_notice=True,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def stream(
|
def stream(
|
||||||
streaming_fn: StreamingFunction,
|
streaming_fn: Callable[
|
||||||
|
[Union[BaseHTTPResponse, ResponseStream]], Coroutine[Any, Any, None]
|
||||||
|
],
|
||||||
status: int = 200,
|
status: int = 200,
|
||||||
headers: Optional[Dict[str, str]] = None,
|
headers: Optional[Dict[str, str]] = None,
|
||||||
content_type: str = "text/plain; charset=utf-8",
|
content_type: str = "text/plain; charset=utf-8",
|
||||||
):
|
) -> ResponseStream:
|
||||||
"""Accepts an coroutine `streaming_fn` which can be used to
|
"""Accepts a coroutine `streaming_fn` which can be used to
|
||||||
write chunks to a streaming response. Returns a `StreamingHTTPResponse`.
|
write chunks to a streaming response. Returns a `ResponseStream`.
|
||||||
|
|
||||||
Example usage::
|
Example usage::
|
||||||
|
|
||||||
|
@ -501,42 +521,13 @@ def stream(
|
||||||
|
|
||||||
:param streaming_fn: A coroutine accepts a response and
|
:param streaming_fn: A coroutine accepts a response and
|
||||||
writes content to that response.
|
writes content to that response.
|
||||||
:param mime_type: Specific mime_type.
|
:param status: HTTP status.
|
||||||
|
:param content_type: Specific content_type.
|
||||||
:param headers: Custom Headers.
|
:param headers: Custom Headers.
|
||||||
:param chunked: Deprecated
|
|
||||||
"""
|
"""
|
||||||
return StreamingHTTPResponse(
|
return ResponseStream(
|
||||||
streaming_fn,
|
streaming_fn,
|
||||||
headers=headers,
|
headers=headers,
|
||||||
content_type=content_type,
|
content_type=content_type,
|
||||||
status=status,
|
status=status,
|
||||||
ignore_deprecation_notice=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def redirect(
|
|
||||||
to: str,
|
|
||||||
headers: Optional[Dict[str, str]] = None,
|
|
||||||
status: int = 302,
|
|
||||||
content_type: str = "text/html; charset=utf-8",
|
|
||||||
) -> HTTPResponse:
|
|
||||||
"""
|
|
||||||
Abort execution and cause a 302 redirect (by default) by setting a
|
|
||||||
Location header.
|
|
||||||
|
|
||||||
:param to: path or fully qualified URL to redirect to
|
|
||||||
:param headers: optional dict of headers to include in the new request
|
|
||||||
:param status: status code (int) of the new request, defaults to 302
|
|
||||||
:param content_type: the content type (string) of the response
|
|
||||||
"""
|
|
||||||
headers = headers or {}
|
|
||||||
|
|
||||||
# URL Quote the URL before redirecting
|
|
||||||
safe_to = quote_plus(to, safe=":/%#?&=@[]!$&'()*+,;")
|
|
||||||
|
|
||||||
# According to RFC 7231, a relative URI is now permitted.
|
|
||||||
headers["Location"] = safe_to
|
|
||||||
|
|
||||||
return HTTPResponse(
|
|
||||||
status=status, headers=headers, content_type=content_type
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -3,9 +3,9 @@ from __future__ import annotations
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
from warnings import warn
|
|
||||||
|
|
||||||
from sanic.exceptions import SanicException
|
from sanic.exceptions import SanicException
|
||||||
|
from sanic.log import deprecation
|
||||||
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
@ -37,10 +37,10 @@ class AsyncioServer:
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def init(self):
|
def init(self):
|
||||||
warn(
|
deprecation(
|
||||||
"AsyncioServer.init has been deprecated and will be removed "
|
"AsyncioServer.init has been deprecated and will be removed "
|
||||||
"in v22.6. Use Sanic.state.is_started instead.",
|
"in v22.6. Use Sanic.state.is_started instead.",
|
||||||
DeprecationWarning,
|
22.6,
|
||||||
)
|
)
|
||||||
return self.app.state.is_started
|
return self.app.state.is_started
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
from typing import TYPE_CHECKING, Optional, Sequence, cast
|
from typing import TYPE_CHECKING, Optional, Sequence, cast
|
||||||
from warnings import warn
|
|
||||||
|
|
||||||
from websockets.connection import CLOSED, CLOSING, OPEN
|
from websockets.connection import CLOSED, CLOSING, OPEN
|
||||||
from websockets.server import ServerConnection
|
from websockets.server import ServerConnection
|
||||||
from websockets.typing import Subprotocol
|
from websockets.typing import Subprotocol
|
||||||
|
|
||||||
from sanic.exceptions import ServerError
|
from sanic.exceptions import ServerError
|
||||||
from sanic.log import error_logger
|
from sanic.log import deprecation, error_logger
|
||||||
from sanic.server import HttpProtocol
|
from sanic.server import HttpProtocol
|
||||||
|
|
||||||
from ..websockets.impl import WebsocketImplProtocol
|
from ..websockets.impl import WebsocketImplProtocol
|
||||||
|
@ -17,6 +16,14 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
|
|
||||||
class WebSocketProtocol(HttpProtocol):
|
class WebSocketProtocol(HttpProtocol):
|
||||||
|
__slots__ = (
|
||||||
|
"websocket",
|
||||||
|
"websocket_timeout",
|
||||||
|
"websocket_max_size",
|
||||||
|
"websocket_ping_interval",
|
||||||
|
"websocket_ping_timeout",
|
||||||
|
)
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
*args,
|
*args,
|
||||||
|
@ -35,24 +42,24 @@ class WebSocketProtocol(HttpProtocol):
|
||||||
self.websocket_max_size = websocket_max_size
|
self.websocket_max_size = websocket_max_size
|
||||||
if websocket_max_queue is not None and websocket_max_queue > 0:
|
if websocket_max_queue is not None and websocket_max_queue > 0:
|
||||||
# TODO: Reminder remove this warning in v22.3
|
# TODO: Reminder remove this warning in v22.3
|
||||||
warn(
|
deprecation(
|
||||||
"Websocket no longer uses queueing, so websocket_max_queue"
|
"Websocket no longer uses queueing, so websocket_max_queue"
|
||||||
" is no longer required.",
|
" is no longer required.",
|
||||||
DeprecationWarning,
|
22.3,
|
||||||
)
|
)
|
||||||
if websocket_read_limit is not None and websocket_read_limit > 0:
|
if websocket_read_limit is not None and websocket_read_limit > 0:
|
||||||
# TODO: Reminder remove this warning in v22.3
|
# TODO: Reminder remove this warning in v22.3
|
||||||
warn(
|
deprecation(
|
||||||
"Websocket no longer uses read buffers, so "
|
"Websocket no longer uses read buffers, so "
|
||||||
"websocket_read_limit is not required.",
|
"websocket_read_limit is not required.",
|
||||||
DeprecationWarning,
|
22.3,
|
||||||
)
|
)
|
||||||
if websocket_write_limit is not None and websocket_write_limit > 0:
|
if websocket_write_limit is not None and websocket_write_limit > 0:
|
||||||
# TODO: Reminder remove this warning in v22.3
|
# TODO: Reminder remove this warning in v22.3
|
||||||
warn(
|
deprecation(
|
||||||
"Websocket no longer uses write buffers, so "
|
"Websocket no longer uses write buffers, so "
|
||||||
"websocket_write_limit is not required.",
|
"websocket_write_limit is not required.",
|
||||||
DeprecationWarning,
|
22.3,
|
||||||
)
|
)
|
||||||
self.websocket_ping_interval = websocket_ping_interval
|
self.websocket_ping_interval = websocket_ping_interval
|
||||||
self.websocket_ping_timeout = websocket_ping_timeout
|
self.websocket_ping_timeout = websocket_ping_timeout
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
|
from sanic.base.meta import SanicMeta
|
||||||
from sanic.exceptions import SanicException
|
from sanic.exceptions import SanicException
|
||||||
|
|
||||||
from .service import TouchUp
|
from .service import TouchUp
|
||||||
|
|
||||||
|
|
||||||
class TouchUpMeta(type):
|
class TouchUpMeta(SanicMeta):
|
||||||
def __new__(cls, name, bases, attrs, **kwargs):
|
def __new__(cls, name, bases, attrs, **kwargs):
|
||||||
gen_class = super().__new__(cls, name, bases, attrs, **kwargs)
|
gen_class = super().__new__(cls, name, bases, attrs, **kwargs)
|
||||||
|
|
||||||
|
|
|
@ -9,10 +9,7 @@ from typing import (
|
||||||
Optional,
|
Optional,
|
||||||
Union,
|
Union,
|
||||||
)
|
)
|
||||||
from warnings import warn
|
|
||||||
|
|
||||||
from sanic.constants import HTTP_METHODS
|
|
||||||
from sanic.exceptions import InvalidUsage
|
|
||||||
from sanic.models.handler_types import RouteHandler
|
from sanic.models.handler_types import RouteHandler
|
||||||
|
|
||||||
|
|
||||||
|
@ -136,48 +133,3 @@ class HTTPMethodView:
|
||||||
def stream(func):
|
def stream(func):
|
||||||
func.is_stream = True
|
func.is_stream = True
|
||||||
return func
|
return func
|
||||||
|
|
||||||
|
|
||||||
class CompositionView:
|
|
||||||
"""Simple method-function mapped view for the sanic.
|
|
||||||
You can add handler functions to methods (get, post, put, patch, delete)
|
|
||||||
for every HTTP method you want to support.
|
|
||||||
|
|
||||||
For example:
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
view = CompositionView()
|
|
||||||
view.add(['GET'], lambda request: text('I am get method'))
|
|
||||||
view.add(['POST', 'PUT'], lambda request: text('I am post/put method'))
|
|
||||||
|
|
||||||
If someone tries to use a non-implemented method, there will be a
|
|
||||||
405 response.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.handlers = {}
|
|
||||||
self.name = self.__class__.__name__
|
|
||||||
warn(
|
|
||||||
"CompositionView has been deprecated and will be removed in "
|
|
||||||
"v21.12. Please update your view to HTTPMethodView.",
|
|
||||||
DeprecationWarning,
|
|
||||||
)
|
|
||||||
|
|
||||||
def __name__(self):
|
|
||||||
return self.name
|
|
||||||
|
|
||||||
def add(self, methods, handler, stream=False):
|
|
||||||
if stream:
|
|
||||||
handler.is_stream = stream
|
|
||||||
for method in methods:
|
|
||||||
if method not in HTTP_METHODS:
|
|
||||||
raise InvalidUsage(f"{method} is not a valid HTTP method.")
|
|
||||||
|
|
||||||
if method in self.handlers:
|
|
||||||
raise InvalidUsage(f"Method {method} is already registered.")
|
|
||||||
self.handlers[method] = handler
|
|
||||||
|
|
||||||
def __call__(self, request, *args, **kwargs):
|
|
||||||
handler = self.handlers[request.method.upper()]
|
|
||||||
return handler(request, *args, **kwargs)
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import json
|
import json
|
||||||
import logging
|
|
||||||
|
|
||||||
from sanic import Sanic, text
|
from sanic import Sanic, text
|
||||||
from sanic.log import LOGGING_CONFIG_DEFAULTS, logger
|
from sanic.log import LOGGING_CONFIG_DEFAULTS, logger
|
||||||
|
@ -9,7 +8,7 @@ LOGGING_CONFIG = {**LOGGING_CONFIG_DEFAULTS}
|
||||||
LOGGING_CONFIG["formatters"]["generic"]["format"] = "%(message)s"
|
LOGGING_CONFIG["formatters"]["generic"]["format"] = "%(message)s"
|
||||||
LOGGING_CONFIG["loggers"]["sanic.root"]["level"] = "DEBUG"
|
LOGGING_CONFIG["loggers"]["sanic.root"]["level"] = "DEBUG"
|
||||||
|
|
||||||
app = Sanic(__name__, log_config=LOGGING_CONFIG)
|
app = Sanic("FakeServer", log_config=LOGGING_CONFIG)
|
||||||
|
|
||||||
|
|
||||||
@app.get("/")
|
@app.get("/")
|
||||||
|
|
|
@ -8,14 +8,15 @@ from os import environ
|
||||||
from unittest.mock import Mock, patch
|
from unittest.mock import Mock, patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
import sanic
|
import sanic
|
||||||
|
|
||||||
from sanic import Sanic
|
from sanic import Sanic
|
||||||
from sanic.compat import OS_IS_WINDOWS, UVLOOP_INSTALLED
|
from sanic.compat import OS_IS_WINDOWS
|
||||||
from sanic.config import Config
|
from sanic.config import Config
|
||||||
from sanic.exceptions import SanicException
|
from sanic.exceptions import SanicException
|
||||||
from sanic.response import text
|
|
||||||
from sanic.helpers import _default
|
from sanic.helpers import _default
|
||||||
|
from sanic.response import text
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(autouse=True)
|
@pytest.fixture(autouse=True)
|
||||||
|
@ -392,6 +393,22 @@ def test_app_no_registry():
|
||||||
Sanic.get_app("no-register")
|
Sanic.get_app("no-register")
|
||||||
|
|
||||||
|
|
||||||
|
def test_app_no_registry_deprecation_message():
|
||||||
|
with pytest.warns(DeprecationWarning) as records:
|
||||||
|
Sanic("no-register", register=False)
|
||||||
|
Sanic("yes-register", register=True)
|
||||||
|
|
||||||
|
message = (
|
||||||
|
"[DEPRECATION v22.6] The register argument is deprecated and will "
|
||||||
|
"stop working in v22.6. After v22.6 all apps will be added to the "
|
||||||
|
"Sanic app registry."
|
||||||
|
)
|
||||||
|
|
||||||
|
assert len(records) == 2
|
||||||
|
for record in records:
|
||||||
|
assert record.message.args[0] == message
|
||||||
|
|
||||||
|
|
||||||
def test_app_no_registry_env():
|
def test_app_no_registry_env():
|
||||||
environ["SANIC_REGISTER"] = "False"
|
environ["SANIC_REGISTER"] = "False"
|
||||||
Sanic("no-register")
|
Sanic("no-register")
|
||||||
|
@ -403,15 +420,12 @@ def test_app_no_registry_env():
|
||||||
|
|
||||||
|
|
||||||
def test_app_set_attribute_warning(app):
|
def test_app_set_attribute_warning(app):
|
||||||
with pytest.warns(DeprecationWarning) as record:
|
message = (
|
||||||
app.foo = 1
|
"Setting variables on Sanic instances is not allowed. You should "
|
||||||
|
"change your Sanic instance to use instance.ctx.foo instead."
|
||||||
assert len(record) == 1
|
|
||||||
assert record[0].message.args[0] == (
|
|
||||||
"Setting variables on Sanic instances is deprecated "
|
|
||||||
"and will be removed in version 21.12. You should change your "
|
|
||||||
"Sanic instance to use instance.ctx.foo instead."
|
|
||||||
)
|
)
|
||||||
|
with pytest.raises(AttributeError, match=message):
|
||||||
|
app.foo = 1
|
||||||
|
|
||||||
|
|
||||||
def test_app_set_context(app):
|
def test_app_set_context(app):
|
||||||
|
@ -433,15 +447,7 @@ def test_bad_custom_config():
|
||||||
SanicException,
|
SanicException,
|
||||||
match=(
|
match=(
|
||||||
"When instantiating Sanic with config, you cannot also pass "
|
"When instantiating Sanic with config, you cannot also pass "
|
||||||
"load_env or env_prefix"
|
"env_prefix"
|
||||||
),
|
|
||||||
):
|
|
||||||
Sanic("test", config=1, load_env=1)
|
|
||||||
with pytest.raises(
|
|
||||||
SanicException,
|
|
||||||
match=(
|
|
||||||
"When instantiating Sanic with config, you cannot also pass "
|
|
||||||
"load_env or env_prefix"
|
|
||||||
),
|
),
|
||||||
):
|
):
|
||||||
Sanic("test", config=1, env_prefix=1)
|
Sanic("test", config=1, env_prefix=1)
|
||||||
|
@ -494,11 +500,7 @@ def test_uvloop_config(app, monkeypatch):
|
||||||
|
|
||||||
|
|
||||||
def test_uvloop_cannot_never_called_with_create_server(caplog, monkeypatch):
|
def test_uvloop_cannot_never_called_with_create_server(caplog, monkeypatch):
|
||||||
apps = (
|
apps = (Sanic("default-uvloop"), Sanic("no-uvloop"), Sanic("yes-uvloop"))
|
||||||
Sanic("default-uvloop"),
|
|
||||||
Sanic("no-uvloop"),
|
|
||||||
Sanic("yes-uvloop")
|
|
||||||
)
|
|
||||||
|
|
||||||
apps[1].config.USE_UVLOOP = False
|
apps[1].config.USE_UVLOOP = False
|
||||||
apps[2].config.USE_UVLOOP = True
|
apps[2].config.USE_UVLOOP = True
|
||||||
|
@ -512,7 +514,7 @@ def test_uvloop_cannot_never_called_with_create_server(caplog, monkeypatch):
|
||||||
for app in apps:
|
for app in apps:
|
||||||
srv_coro = app.create_server(
|
srv_coro = app.create_server(
|
||||||
return_asyncio_server=True,
|
return_asyncio_server=True,
|
||||||
asyncio_server_kwargs=dict(start_serving=False)
|
asyncio_server_kwargs=dict(start_serving=False),
|
||||||
)
|
)
|
||||||
loop.run_until_complete(srv_coro)
|
loop.run_until_complete(srv_coro)
|
||||||
|
|
||||||
|
@ -547,7 +549,7 @@ def test_multiple_uvloop_configs_display_warning(caplog):
|
||||||
for app in (default_uvloop, no_uvloop, yes_uvloop):
|
for app in (default_uvloop, no_uvloop, yes_uvloop):
|
||||||
srv_coro = app.create_server(
|
srv_coro = app.create_server(
|
||||||
return_asyncio_server=True,
|
return_asyncio_server=True,
|
||||||
asyncio_server_kwargs=dict(start_serving=False)
|
asyncio_server_kwargs=dict(start_serving=False),
|
||||||
)
|
)
|
||||||
srv = loop.run_until_complete(srv_coro)
|
srv = loop.run_until_complete(srv_coro)
|
||||||
loop.run_until_complete(srv.startup())
|
loop.run_until_complete(srv.startup())
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from sanic import Blueprint, Sanic
|
from sanic import Blueprint, Sanic
|
||||||
|
from sanic.exceptions import SanicException
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
@ -79,24 +80,18 @@ def test_names_okay(name):
|
||||||
)
|
)
|
||||||
def test_names_not_okay(name):
|
def test_names_not_okay(name):
|
||||||
app_message = (
|
app_message = (
|
||||||
f"Sanic instance named '{name}' uses a format that isdeprecated. "
|
f"Sanic instance named '{name}' uses an invalid format. Names must "
|
||||||
"Starting in version 21.12, Sanic objects must be named only using "
|
"begin with a character and may only contain alphanumeric "
|
||||||
"alphanumeric characters, _, or -."
|
"characters, _, or -."
|
||||||
)
|
)
|
||||||
bp_message = (
|
bp_message = (
|
||||||
f"Blueprint instance named '{name}' uses a format that isdeprecated. "
|
f"Blueprint instance named '{name}' uses an invalid format. Names "
|
||||||
"Starting in version 21.12, Blueprint objects must be named only using "
|
"must begin with a character and may only contain alphanumeric "
|
||||||
"alphanumeric characters, _, or -."
|
"characters, _, or -."
|
||||||
)
|
)
|
||||||
|
|
||||||
with pytest.warns(DeprecationWarning) as app_e:
|
with pytest.raises(SanicException, match=app_message):
|
||||||
app = Sanic(name)
|
Sanic(name)
|
||||||
|
|
||||||
with pytest.warns(DeprecationWarning) as bp_e:
|
with pytest.raises(SanicException, match=bp_message):
|
||||||
bp = Blueprint(name)
|
Blueprint(name)
|
||||||
|
|
||||||
assert app.name == name
|
|
||||||
assert bp.name == name
|
|
||||||
|
|
||||||
assert app_e[0].message.args[0] == app_message
|
|
||||||
assert bp_e[0].message.args[0] == bp_message
|
|
||||||
|
|
|
@ -15,7 +15,6 @@ from sanic.exceptions import (
|
||||||
)
|
)
|
||||||
from sanic.request import Request
|
from sanic.request import Request
|
||||||
from sanic.response import json, text
|
from sanic.response import json, text
|
||||||
from sanic.views import CompositionView
|
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------ #
|
# ------------------------------------------------------------ #
|
||||||
|
@ -833,7 +832,7 @@ def test_static_blueprint_name(static_file_directory, file_name):
|
||||||
|
|
||||||
@pytest.mark.parametrize("file_name", ["test.file"])
|
@pytest.mark.parametrize("file_name", ["test.file"])
|
||||||
def test_static_blueprintp_mw(app: Sanic, static_file_directory, file_name):
|
def test_static_blueprintp_mw(app: Sanic, static_file_directory, file_name):
|
||||||
current_file = inspect.getfile(inspect.currentframe())
|
current_file = inspect.getfile(inspect.currentframe()) # type: ignore
|
||||||
with open(current_file, "rb") as file:
|
with open(current_file, "rb") as file:
|
||||||
file.read()
|
file.read()
|
||||||
|
|
||||||
|
@ -862,31 +861,6 @@ def test_static_blueprintp_mw(app: Sanic, static_file_directory, file_name):
|
||||||
assert triggered is True
|
assert triggered is True
|
||||||
|
|
||||||
|
|
||||||
def test_route_handler_add(app: Sanic):
|
|
||||||
view = CompositionView()
|
|
||||||
|
|
||||||
async def get_handler(request):
|
|
||||||
return json({"response": "OK"})
|
|
||||||
|
|
||||||
view.add(["GET"], get_handler, stream=False)
|
|
||||||
|
|
||||||
async def default_handler(request):
|
|
||||||
return text("OK")
|
|
||||||
|
|
||||||
bp = Blueprint(name="handler", url_prefix="/handler")
|
|
||||||
bp.add_route(default_handler, uri="/default/", strict_slashes=True)
|
|
||||||
|
|
||||||
bp.add_route(view, uri="/view", name="test")
|
|
||||||
|
|
||||||
app.blueprint(bp)
|
|
||||||
|
|
||||||
_, response = app.test_client.get("/handler/default/")
|
|
||||||
assert response.text == "OK"
|
|
||||||
|
|
||||||
_, response = app.test_client.get("/handler/view")
|
|
||||||
assert response.json["response"] == "OK"
|
|
||||||
|
|
||||||
|
|
||||||
def test_websocket_route(app: Sanic):
|
def test_websocket_route(app: Sanic):
|
||||||
event = asyncio.Event()
|
event = asyncio.Event()
|
||||||
|
|
||||||
|
@ -1079,15 +1053,12 @@ def test_blueprint_registered_multiple_apps():
|
||||||
|
|
||||||
def test_bp_set_attribute_warning():
|
def test_bp_set_attribute_warning():
|
||||||
bp = Blueprint("bp")
|
bp = Blueprint("bp")
|
||||||
with pytest.warns(DeprecationWarning) as record:
|
message = (
|
||||||
bp.foo = 1
|
"Setting variables on Blueprint instances is not allowed. You should "
|
||||||
|
"change your Blueprint instance to use instance.ctx.foo instead."
|
||||||
assert len(record) == 1
|
|
||||||
assert record[0].message.args[0] == (
|
|
||||||
"Setting variables on Blueprint instances is deprecated "
|
|
||||||
"and will be removed in version 21.12. You should change your "
|
|
||||||
"Blueprint instance to use instance.ctx.foo instead."
|
|
||||||
)
|
)
|
||||||
|
with pytest.raises(AttributeError, match=message):
|
||||||
|
bp.foo = 1
|
||||||
|
|
||||||
|
|
||||||
def test_early_registration(app):
|
def test_early_registration(app):
|
||||||
|
|
|
@ -81,26 +81,6 @@ def test_auto_bool_env_prefix():
|
||||||
del environ["SANIC_TEST_ANSWER"]
|
del environ["SANIC_TEST_ANSWER"]
|
||||||
|
|
||||||
|
|
||||||
def test_dont_load_env():
|
|
||||||
environ["SANIC_TEST_ANSWER"] = "42"
|
|
||||||
app = Sanic(name=__name__, load_env=False)
|
|
||||||
assert getattr(app.config, "TEST_ANSWER", None) is None
|
|
||||||
del environ["SANIC_TEST_ANSWER"]
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("load_env", [None, False, "", "MYAPP_"])
|
|
||||||
def test_load_env_deprecation(load_env):
|
|
||||||
with pytest.warns(DeprecationWarning, match=r"21\.12"):
|
|
||||||
_ = Sanic(name=__name__, load_env=load_env)
|
|
||||||
|
|
||||||
|
|
||||||
def test_load_env_prefix():
|
|
||||||
environ["MYAPP_TEST_ANSWER"] = "42"
|
|
||||||
app = Sanic(name=__name__, load_env="MYAPP_")
|
|
||||||
assert app.config.TEST_ANSWER == 42
|
|
||||||
del environ["MYAPP_TEST_ANSWER"]
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("env_prefix", [None, ""])
|
@pytest.mark.parametrize("env_prefix", [None, ""])
|
||||||
def test_empty_load_env_prefix(env_prefix):
|
def test_empty_load_env_prefix(env_prefix):
|
||||||
environ["SANIC_TEST_ANSWER"] = "42"
|
environ["SANIC_TEST_ANSWER"] = "42"
|
||||||
|
@ -109,20 +89,6 @@ def test_empty_load_env_prefix(env_prefix):
|
||||||
del environ["SANIC_TEST_ANSWER"]
|
del environ["SANIC_TEST_ANSWER"]
|
||||||
|
|
||||||
|
|
||||||
def test_load_env_prefix_float_values():
|
|
||||||
environ["MYAPP_TEST_ROI"] = "2.3"
|
|
||||||
app = Sanic(name=__name__, load_env="MYAPP_")
|
|
||||||
assert app.config.TEST_ROI == 2.3
|
|
||||||
del environ["MYAPP_TEST_ROI"]
|
|
||||||
|
|
||||||
|
|
||||||
def test_load_env_prefix_string_value():
|
|
||||||
environ["MYAPP_TEST_TOKEN"] = "somerandomtesttoken"
|
|
||||||
app = Sanic(name=__name__, load_env="MYAPP_")
|
|
||||||
assert app.config.TEST_TOKEN == "somerandomtesttoken"
|
|
||||||
del environ["MYAPP_TEST_TOKEN"]
|
|
||||||
|
|
||||||
|
|
||||||
def test_env_prefix():
|
def test_env_prefix():
|
||||||
environ["MYAPP_TEST_ANSWER"] = "42"
|
environ["MYAPP_TEST_ANSWER"] = "42"
|
||||||
app = Sanic(name=__name__, env_prefix="MYAPP_")
|
app = Sanic(name=__name__, env_prefix="MYAPP_")
|
||||||
|
|
9
tests/test_deprecation.py
Normal file
9
tests/test_deprecation.py
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from sanic.log import deprecation
|
||||||
|
|
||||||
|
|
||||||
|
def test_deprecation():
|
||||||
|
message = r"\[DEPRECATION v9\.9\] hello"
|
||||||
|
with pytest.warns(DeprecationWarning, match=message):
|
||||||
|
deprecation("hello", 9.9)
|
|
@ -13,7 +13,6 @@ from sanic.exceptions import (
|
||||||
SanicException,
|
SanicException,
|
||||||
ServerError,
|
ServerError,
|
||||||
Unauthorized,
|
Unauthorized,
|
||||||
abort,
|
|
||||||
)
|
)
|
||||||
from sanic.response import text
|
from sanic.response import text
|
||||||
|
|
||||||
|
@ -88,10 +87,6 @@ def exception_app():
|
||||||
def handler_500_error(request):
|
def handler_500_error(request):
|
||||||
raise SanicException(status_code=500)
|
raise SanicException(status_code=500)
|
||||||
|
|
||||||
@app.route("/old_abort")
|
|
||||||
def handler_old_abort_error(request):
|
|
||||||
abort(500)
|
|
||||||
|
|
||||||
@app.route("/abort/message")
|
@app.route("/abort/message")
|
||||||
def handler_abort_message(request):
|
def handler_abort_message(request):
|
||||||
raise SanicException(message="Custom Message", status_code=500)
|
raise SanicException(message="Custom Message", status_code=500)
|
||||||
|
@ -239,11 +234,6 @@ def test_sanic_exception(exception_app):
|
||||||
assert response.status == 500
|
assert response.status == 500
|
||||||
assert "Custom Message" in response.text
|
assert "Custom Message" in response.text
|
||||||
|
|
||||||
with warnings.catch_warnings(record=True) as w:
|
|
||||||
request, response = exception_app.test_client.get("/old_abort")
|
|
||||||
assert response.status == 500
|
|
||||||
assert len(w) == 1 and "deprecated" in w[0].message.args[0]
|
|
||||||
|
|
||||||
|
|
||||||
def test_custom_exception_default_message(exception_app):
|
def test_custom_exception_default_message(exception_app):
|
||||||
class TeaError(SanicException):
|
class TeaError(SanicException):
|
||||||
|
@ -262,7 +252,7 @@ def test_custom_exception_default_message(exception_app):
|
||||||
|
|
||||||
|
|
||||||
def test_exception_in_ws_logged(caplog):
|
def test_exception_in_ws_logged(caplog):
|
||||||
app = Sanic(__file__)
|
app = Sanic(__name__)
|
||||||
|
|
||||||
@app.websocket("/feed")
|
@app.websocket("/feed")
|
||||||
async def feed(request, ws):
|
async def feed(request, ws):
|
||||||
|
|
|
@ -226,11 +226,12 @@ def test_single_arg_exception_handler_notice(
|
||||||
exception_handler_app.error_handler = CustomErrorHandler()
|
exception_handler_app.error_handler = CustomErrorHandler()
|
||||||
|
|
||||||
message = (
|
message = (
|
||||||
"You are using a deprecated error handler. The lookup method should "
|
"[DEPRECATION v22.3] You are using a deprecated error handler. The "
|
||||||
"accept two positional parameters: (exception, route_name: "
|
"lookup method should accept two positional parameters: (exception, "
|
||||||
"Optional[str]). Until you upgrade your ErrorHandler.lookup, "
|
"route_name: Optional[str]). Until you upgrade your "
|
||||||
"Blueprint specific exceptions will not work properly. Beginning in "
|
"ErrorHandler.lookup, Blueprint specific exceptions will not work "
|
||||||
"v22.3, the legacy style lookup method will not work at all."
|
"properly. Beginning in v22.3, the legacy style lookup method will "
|
||||||
|
"not work at all."
|
||||||
)
|
)
|
||||||
with pytest.warns(DeprecationWarning) as record:
|
with pytest.warns(DeprecationWarning) as record:
|
||||||
_, response = exception_handler_app.test_client.get("/1")
|
_, response = exception_handler_app.test_client.get("/1")
|
||||||
|
|
|
@ -45,7 +45,7 @@ def default_back_to_ujson():
|
||||||
|
|
||||||
|
|
||||||
def test_change_encoder():
|
def test_change_encoder():
|
||||||
Sanic("...", dumps=sdumps)
|
Sanic("Test", dumps=sdumps)
|
||||||
assert BaseHTTPResponse._dumps == sdumps
|
assert BaseHTTPResponse._dumps == sdumps
|
||||||
|
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ def test_change_encoder_to_some_custom():
|
||||||
def my_custom_encoder():
|
def my_custom_encoder():
|
||||||
return "foo"
|
return "foo"
|
||||||
|
|
||||||
Sanic("...", dumps=my_custom_encoder)
|
Sanic("Test", dumps=my_custom_encoder)
|
||||||
assert BaseHTTPResponse._dumps == my_custom_encoder
|
assert BaseHTTPResponse._dumps == my_custom_encoder
|
||||||
|
|
||||||
|
|
||||||
|
@ -68,7 +68,7 @@ def test_json_response_ujson(payload):
|
||||||
):
|
):
|
||||||
json(payload, dumps=sdumps)
|
json(payload, dumps=sdumps)
|
||||||
|
|
||||||
Sanic("...", dumps=sdumps)
|
Sanic("Test", dumps=sdumps)
|
||||||
with pytest.raises(
|
with pytest.raises(
|
||||||
TypeError, match="Object of type Foo is not JSON serializable"
|
TypeError, match="Object of type Foo is not JSON serializable"
|
||||||
):
|
):
|
||||||
|
@ -87,6 +87,6 @@ def test_json_response_json():
|
||||||
response = json(too_big_for_ujson, dumps=sdumps)
|
response = json(too_big_for_ujson, dumps=sdumps)
|
||||||
assert sys.getsizeof(response.body) == 54
|
assert sys.getsizeof(response.body) == 54
|
||||||
|
|
||||||
Sanic("...", dumps=sdumps)
|
Sanic("Test", dumps=sdumps)
|
||||||
response = json(too_big_for_ujson)
|
response = json(too_big_for_ujson)
|
||||||
assert sys.getsizeof(response.body) == 54
|
assert sys.getsizeof(response.body) == 54
|
||||||
|
|
|
@ -8,13 +8,13 @@ from sanic.response import stream, text
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_request_cancel_when_connection_lost(app):
|
async def test_request_cancel_when_connection_lost(app):
|
||||||
app.still_serving_cancelled_request = False
|
app.ctx.still_serving_cancelled_request = False
|
||||||
|
|
||||||
@app.get("/")
|
@app.get("/")
|
||||||
async def handler(request):
|
async def handler(request):
|
||||||
await asyncio.sleep(1.0)
|
await asyncio.sleep(1.0)
|
||||||
# at this point client is already disconnected
|
# at this point client is already disconnected
|
||||||
app.still_serving_cancelled_request = True
|
app.ctx.still_serving_cancelled_request = True
|
||||||
return text("OK")
|
return text("OK")
|
||||||
|
|
||||||
# schedule client call
|
# schedule client call
|
||||||
|
@ -32,12 +32,12 @@ async def test_request_cancel_when_connection_lost(app):
|
||||||
# Wait for server and check if it's still serving the cancelled request
|
# Wait for server and check if it's still serving the cancelled request
|
||||||
await asyncio.sleep(1.0)
|
await asyncio.sleep(1.0)
|
||||||
|
|
||||||
assert app.still_serving_cancelled_request is False
|
assert app.ctx.still_serving_cancelled_request is False
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_stream_request_cancel_when_conn_lost(app):
|
async def test_stream_request_cancel_when_conn_lost(app):
|
||||||
app.still_serving_cancelled_request = False
|
app.ctx.still_serving_cancelled_request = False
|
||||||
|
|
||||||
@app.post("/post/<id>", stream=True)
|
@app.post("/post/<id>", stream=True)
|
||||||
async def post(request, id):
|
async def post(request, id):
|
||||||
|
@ -52,7 +52,7 @@ async def test_stream_request_cancel_when_conn_lost(app):
|
||||||
|
|
||||||
await asyncio.sleep(1.0)
|
await asyncio.sleep(1.0)
|
||||||
# at this point client is already disconnected
|
# at this point client is already disconnected
|
||||||
app.still_serving_cancelled_request = True
|
app.ctx.still_serving_cancelled_request = True
|
||||||
|
|
||||||
return stream(streaming)
|
return stream(streaming)
|
||||||
|
|
||||||
|
@ -71,4 +71,4 @@ async def test_stream_request_cancel_when_conn_lost(app):
|
||||||
# Wait for server and check if it's still serving the cancelled request
|
# Wait for server and check if it's still serving the cancelled request
|
||||||
await asyncio.sleep(1.0)
|
await asyncio.sleep(1.0)
|
||||||
|
|
||||||
assert app.still_serving_cancelled_request is False
|
assert app.ctx.still_serving_cancelled_request is False
|
||||||
|
|
|
@ -68,11 +68,11 @@ def test_app_injection(app):
|
||||||
|
|
||||||
@app.listener("after_server_start")
|
@app.listener("after_server_start")
|
||||||
async def inject_data(app, loop):
|
async def inject_data(app, loop):
|
||||||
app.injected = expected
|
app.ctx.injected = expected
|
||||||
|
|
||||||
@app.get("/")
|
@app.get("/")
|
||||||
async def handler(request):
|
async def handler(request):
|
||||||
return json({"injected": request.app.injected})
|
return json({"injected": request.app.ctx.injected})
|
||||||
|
|
||||||
request, response = app.test_client.get("/")
|
request, response = app.test_client.get("/")
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ import pytest
|
||||||
from sanic import Sanic
|
from sanic import Sanic
|
||||||
from sanic.blueprints import Blueprint
|
from sanic.blueprints import Blueprint
|
||||||
from sanic.response import json, text
|
from sanic.response import json, text
|
||||||
from sanic.views import CompositionView, HTTPMethodView
|
from sanic.views import HTTPMethodView
|
||||||
from sanic.views import stream as stream_decorator
|
from sanic.views import stream as stream_decorator
|
||||||
|
|
||||||
|
|
||||||
|
@ -423,33 +423,6 @@ def test_request_stream_blueprint(app):
|
||||||
assert response.text == data
|
assert response.text == data
|
||||||
|
|
||||||
|
|
||||||
def test_request_stream_composition_view(app):
|
|
||||||
def get_handler(request):
|
|
||||||
return text("OK")
|
|
||||||
|
|
||||||
async def post_handler(request):
|
|
||||||
result = ""
|
|
||||||
while True:
|
|
||||||
body = await request.stream.read()
|
|
||||||
if body is None:
|
|
||||||
break
|
|
||||||
result += body.decode("utf-8")
|
|
||||||
return text(result)
|
|
||||||
|
|
||||||
view = CompositionView()
|
|
||||||
view.add(["GET"], get_handler)
|
|
||||||
view.add(["POST"], post_handler, stream=True)
|
|
||||||
app.add_route(view, "/composition_view")
|
|
||||||
|
|
||||||
request, response = app.test_client.get("/composition_view")
|
|
||||||
assert response.status == 200
|
|
||||||
assert response.text == "OK"
|
|
||||||
|
|
||||||
request, response = app.test_client.post("/composition_view", data=data)
|
|
||||||
assert response.status == 200
|
|
||||||
assert response.text == data
|
|
||||||
|
|
||||||
|
|
||||||
def test_request_stream(app):
|
def test_request_stream(app):
|
||||||
"""test for complex application"""
|
"""test for complex application"""
|
||||||
bp = Blueprint("test_blueprint_request_stream")
|
bp = Blueprint("test_blueprint_request_stream")
|
||||||
|
@ -510,14 +483,8 @@ def test_request_stream(app):
|
||||||
|
|
||||||
app.add_route(SimpleView.as_view(), "/method_view")
|
app.add_route(SimpleView.as_view(), "/method_view")
|
||||||
|
|
||||||
view = CompositionView()
|
|
||||||
view.add(["GET"], get_handler)
|
|
||||||
view.add(["POST"], post_handler, stream=True)
|
|
||||||
|
|
||||||
app.blueprint(bp)
|
app.blueprint(bp)
|
||||||
|
|
||||||
app.add_route(view, "/composition_view")
|
|
||||||
|
|
||||||
request, response = app.test_client.get("/method_view")
|
request, response = app.test_client.get("/method_view")
|
||||||
assert response.status == 200
|
assert response.status == 200
|
||||||
assert response.text == "OK"
|
assert response.text == "OK"
|
||||||
|
@ -526,14 +493,6 @@ def test_request_stream(app):
|
||||||
assert response.status == 200
|
assert response.status == 200
|
||||||
assert response.text == data
|
assert response.text == data
|
||||||
|
|
||||||
request, response = app.test_client.get("/composition_view")
|
|
||||||
assert response.status == 200
|
|
||||||
assert response.text == "OK"
|
|
||||||
|
|
||||||
request, response = app.test_client.post("/composition_view", data=data)
|
|
||||||
assert response.status == 200
|
|
||||||
assert response.text == data
|
|
||||||
|
|
||||||
request, response = app.test_client.get("/get")
|
request, response = app.test_client.get("/get")
|
||||||
assert response.status == 200
|
assert response.status == 200
|
||||||
assert response.text == "OK"
|
assert response.text == "OK"
|
||||||
|
|
|
@ -15,6 +15,8 @@ from aiofiles import os as async_os
|
||||||
from pytest import LogCaptureFixture
|
from pytest import LogCaptureFixture
|
||||||
|
|
||||||
from sanic import Request, Sanic
|
from sanic import Request, Sanic
|
||||||
|
from sanic.compat import Header
|
||||||
|
from sanic.cookies import CookieJar
|
||||||
from sanic.response import (
|
from sanic.response import (
|
||||||
HTTPResponse,
|
HTTPResponse,
|
||||||
empty,
|
empty,
|
||||||
|
@ -277,7 +279,7 @@ def test_non_chunked_streaming_returns_correct_content(
|
||||||
assert response.text == "foo,bar"
|
assert response.text == "foo,bar"
|
||||||
|
|
||||||
|
|
||||||
def test_stream_response_with_cookies(app):
|
def test_stream_response_with_cookies_legacy(app):
|
||||||
@app.route("/")
|
@app.route("/")
|
||||||
async def test(request: Request):
|
async def test(request: Request):
|
||||||
response = stream(sample_streaming_fn, content_type="text/csv")
|
response = stream(sample_streaming_fn, content_type="text/csv")
|
||||||
|
@ -289,6 +291,25 @@ def test_stream_response_with_cookies(app):
|
||||||
assert response.cookies["test"] == "pass"
|
assert response.cookies["test"] == "pass"
|
||||||
|
|
||||||
|
|
||||||
|
def test_stream_response_with_cookies(app):
|
||||||
|
@app.route("/")
|
||||||
|
async def test(request: Request):
|
||||||
|
headers = Header()
|
||||||
|
cookies = CookieJar(headers)
|
||||||
|
cookies["test"] = "modified"
|
||||||
|
cookies["test"] = "pass"
|
||||||
|
response = await request.respond(
|
||||||
|
content_type="text/csv", headers=headers
|
||||||
|
)
|
||||||
|
|
||||||
|
await response.send("foo,")
|
||||||
|
await asyncio.sleep(0.001)
|
||||||
|
await response.send("bar")
|
||||||
|
|
||||||
|
request, response = app.test_client.get("/")
|
||||||
|
assert response.cookies["test"] == "pass"
|
||||||
|
|
||||||
|
|
||||||
def test_stream_response_without_cookies(app):
|
def test_stream_response_without_cookies(app):
|
||||||
@app.route("/")
|
@app.route("/")
|
||||||
async def test(request: Request):
|
async def test(request: Request):
|
||||||
|
@ -561,37 +582,37 @@ def test_multiple_responses(
|
||||||
message_in_records: Callable[[List[LogRecord], str], bool],
|
message_in_records: Callable[[List[LogRecord], str], bool],
|
||||||
):
|
):
|
||||||
@app.route("/1")
|
@app.route("/1")
|
||||||
async def handler(request: Request):
|
async def handler1(request: Request):
|
||||||
response = await request.respond()
|
response = await request.respond()
|
||||||
await response.send("foo")
|
await response.send("foo")
|
||||||
response = await request.respond()
|
response = await request.respond()
|
||||||
|
|
||||||
@app.route("/2")
|
@app.route("/2")
|
||||||
async def handler(request: Request):
|
async def handler2(request: Request):
|
||||||
response = await request.respond()
|
response = await request.respond()
|
||||||
response = await request.respond()
|
response = await request.respond()
|
||||||
await response.send("foo")
|
await response.send("foo")
|
||||||
|
|
||||||
@app.get("/3")
|
@app.get("/3")
|
||||||
async def handler(request: Request):
|
async def handler3(request: Request):
|
||||||
response = await request.respond()
|
response = await request.respond()
|
||||||
await response.send("foo,")
|
await response.send("foo,")
|
||||||
response = await request.respond()
|
response = await request.respond()
|
||||||
await response.send("bar")
|
await response.send("bar")
|
||||||
|
|
||||||
@app.get("/4")
|
@app.get("/4")
|
||||||
async def handler(request: Request):
|
async def handler4(request: Request):
|
||||||
response = await request.respond(headers={"one": "one"})
|
response = await request.respond(headers={"one": "one"})
|
||||||
return json({"foo": "bar"}, headers={"one": "two"})
|
return json({"foo": "bar"}, headers={"one": "two"})
|
||||||
|
|
||||||
@app.get("/5")
|
@app.get("/5")
|
||||||
async def handler(request: Request):
|
async def handler5(request: Request):
|
||||||
response = await request.respond(headers={"one": "one"})
|
response = await request.respond(headers={"one": "one"})
|
||||||
await response.send("foo")
|
await response.send("foo")
|
||||||
return json({"foo": "bar"}, headers={"one": "two"})
|
return json({"foo": "bar"}, headers={"one": "two"})
|
||||||
|
|
||||||
@app.get("/6")
|
@app.get("/6")
|
||||||
async def handler(request: Request):
|
async def handler6(request: Request):
|
||||||
response = await request.respond(headers={"one": "one"})
|
response = await request.respond(headers={"one": "one"})
|
||||||
await response.send("foo, ")
|
await response.send("foo, ")
|
||||||
json_response = json({"foo": "bar"}, headers={"one": "two"})
|
json_response = json({"foo": "bar"}, headers={"one": "two"})
|
||||||
|
|
|
@ -101,7 +101,7 @@ async def test_trigger_before_events_create_server(app):
|
||||||
|
|
||||||
@app.listener("before_server_start")
|
@app.listener("before_server_start")
|
||||||
async def init_db(app, loop):
|
async def init_db(app, loop):
|
||||||
app.db = MySanicDb()
|
app.ctx.db = MySanicDb()
|
||||||
|
|
||||||
srv = await app.create_server(
|
srv = await app.create_server(
|
||||||
debug=True, return_asyncio_server=True, port=PORT
|
debug=True, return_asyncio_server=True, port=PORT
|
||||||
|
@ -109,8 +109,8 @@ async def test_trigger_before_events_create_server(app):
|
||||||
await srv.startup()
|
await srv.startup()
|
||||||
await srv.before_start()
|
await srv.before_start()
|
||||||
|
|
||||||
assert hasattr(app, "db")
|
assert hasattr(app.ctx, "db")
|
||||||
assert isinstance(app.db, MySanicDb)
|
assert isinstance(app.ctx.db, MySanicDb)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
|
@ -122,9 +122,9 @@ async def test_trigger_before_events_create_server_missing_event(app):
|
||||||
|
|
||||||
@app.listener
|
@app.listener
|
||||||
async def init_db(app, loop):
|
async def init_db(app, loop):
|
||||||
app.db = MySanicDb()
|
app.ctx.db = MySanicDb()
|
||||||
|
|
||||||
assert not hasattr(app, "db")
|
assert not hasattr(app.ctx, "db")
|
||||||
|
|
||||||
|
|
||||||
def test_create_server_trigger_events(app):
|
def test_create_server_trigger_events(app):
|
||||||
|
|
|
@ -76,8 +76,11 @@ async def test_purge_tasks(app: Sanic):
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skipif(sys.version_info < (3, 8), reason="Not supported in 3.7")
|
@pytest.mark.skipif(sys.version_info < (3, 8), reason="Not supported in 3.7")
|
||||||
def test_shutdown_tasks_on_app_stop(app: Sanic):
|
def test_shutdown_tasks_on_app_stop():
|
||||||
app.shutdown_tasks = Mock()
|
class TestSanic(Sanic):
|
||||||
|
shutdown_tasks = Mock()
|
||||||
|
|
||||||
|
app = TestSanic("Test")
|
||||||
|
|
||||||
@app.route("/")
|
@app.route("/")
|
||||||
async def handler(_):
|
async def handler(_):
|
||||||
|
|
|
@ -15,12 +15,12 @@ from sanic import Sanic
|
||||||
from sanic.response import text
|
from sanic.response import text
|
||||||
|
|
||||||
|
|
||||||
httpx_version = tuple(
|
|
||||||
map(int, httpx.__version__.strip(ascii_lowercase).split("."))
|
|
||||||
)
|
|
||||||
pytestmark = pytest.mark.skipif(os.name != "posix", reason="UNIX only")
|
pytestmark = pytest.mark.skipif(os.name != "posix", reason="UNIX only")
|
||||||
SOCKPATH = "/tmp/sanictest.sock"
|
SOCKPATH = "/tmp/sanictest.sock"
|
||||||
SOCKPATH2 = "/tmp/sanictest2.sock"
|
SOCKPATH2 = "/tmp/sanictest2.sock"
|
||||||
|
httpx_version = tuple(
|
||||||
|
map(int, httpx.__version__.strip(ascii_lowercase).split("."))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(autouse=True)
|
@pytest.fixture(autouse=True)
|
||||||
|
@ -222,7 +222,10 @@ async def test_zero_downtime():
|
||||||
processes = [spawn()]
|
processes = [spawn()]
|
||||||
while not os.path.exists(SOCKPATH):
|
while not os.path.exists(SOCKPATH):
|
||||||
if processes[0].poll() is not None:
|
if processes[0].poll() is not None:
|
||||||
raise Exception("Worker did not start properly")
|
raise Exception(
|
||||||
|
"Worker did not start properly. "
|
||||||
|
f"stderr: {processes[0].stderr.read()}"
|
||||||
|
)
|
||||||
await asyncio.sleep(0.0001)
|
await asyncio.sleep(0.0001)
|
||||||
ino = os.stat(SOCKPATH).st_ino
|
ino = os.stat(SOCKPATH).st_ino
|
||||||
task = asyncio.get_event_loop().create_task(client())
|
task = asyncio.get_event_loop().create_task(client())
|
||||||
|
|
|
@ -19,7 +19,7 @@ def test_route(app, handler):
|
||||||
|
|
||||||
|
|
||||||
def test_bp(app, handler):
|
def test_bp(app, handler):
|
||||||
bp = Blueprint(__file__, version=1)
|
bp = Blueprint(__name__, version=1)
|
||||||
bp.route("/")(handler)
|
bp.route("/")(handler)
|
||||||
app.blueprint(bp)
|
app.blueprint(bp)
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ def test_bp(app, handler):
|
||||||
|
|
||||||
|
|
||||||
def test_bp_use_route(app, handler):
|
def test_bp_use_route(app, handler):
|
||||||
bp = Blueprint(__file__, version=1)
|
bp = Blueprint(__name__, version=1)
|
||||||
bp.route("/", version=1.1)(handler)
|
bp.route("/", version=1.1)(handler)
|
||||||
app.blueprint(bp)
|
app.blueprint(bp)
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ def test_bp_use_route(app, handler):
|
||||||
|
|
||||||
|
|
||||||
def test_bp_group(app, handler):
|
def test_bp_group(app, handler):
|
||||||
bp = Blueprint(__file__)
|
bp = Blueprint(__name__)
|
||||||
bp.route("/")(handler)
|
bp.route("/")(handler)
|
||||||
group = Blueprint.group(bp, version=1)
|
group = Blueprint.group(bp, version=1)
|
||||||
app.blueprint(group)
|
app.blueprint(group)
|
||||||
|
@ -47,7 +47,7 @@ def test_bp_group(app, handler):
|
||||||
|
|
||||||
|
|
||||||
def test_bp_group_use_bp(app, handler):
|
def test_bp_group_use_bp(app, handler):
|
||||||
bp = Blueprint(__file__, version=1.1)
|
bp = Blueprint(__name__, version=1.1)
|
||||||
bp.route("/")(handler)
|
bp.route("/")(handler)
|
||||||
group = Blueprint.group(bp, version=1)
|
group = Blueprint.group(bp, version=1)
|
||||||
app.blueprint(group)
|
app.blueprint(group)
|
||||||
|
@ -57,7 +57,7 @@ def test_bp_group_use_bp(app, handler):
|
||||||
|
|
||||||
|
|
||||||
def test_bp_group_use_registration(app, handler):
|
def test_bp_group_use_registration(app, handler):
|
||||||
bp = Blueprint(__file__, version=1.1)
|
bp = Blueprint(__name__, version=1.1)
|
||||||
bp.route("/")(handler)
|
bp.route("/")(handler)
|
||||||
group = Blueprint.group(bp, version=1)
|
group = Blueprint.group(bp, version=1)
|
||||||
app.blueprint(group, version=1.2)
|
app.blueprint(group, version=1.2)
|
||||||
|
@ -67,7 +67,7 @@ def test_bp_group_use_registration(app, handler):
|
||||||
|
|
||||||
|
|
||||||
def test_bp_group_use_route(app, handler):
|
def test_bp_group_use_route(app, handler):
|
||||||
bp = Blueprint(__file__, version=1.1)
|
bp = Blueprint(__name__, version=1.1)
|
||||||
bp.route("/", version=1.3)(handler)
|
bp.route("/", version=1.3)(handler)
|
||||||
group = Blueprint.group(bp, version=1)
|
group = Blueprint.group(bp, version=1)
|
||||||
app.blueprint(group, version=1.2)
|
app.blueprint(group, version=1.2)
|
||||||
|
@ -84,7 +84,7 @@ def test_version_prefix_route(app, handler):
|
||||||
|
|
||||||
|
|
||||||
def test_version_prefix_bp(app, handler):
|
def test_version_prefix_bp(app, handler):
|
||||||
bp = Blueprint(__file__, version=1, version_prefix="/api/v")
|
bp = Blueprint(__name__, version=1, version_prefix="/api/v")
|
||||||
bp.route("/")(handler)
|
bp.route("/")(handler)
|
||||||
app.blueprint(bp)
|
app.blueprint(bp)
|
||||||
|
|
||||||
|
@ -93,7 +93,7 @@ def test_version_prefix_bp(app, handler):
|
||||||
|
|
||||||
|
|
||||||
def test_version_prefix_bp_use_route(app, handler):
|
def test_version_prefix_bp_use_route(app, handler):
|
||||||
bp = Blueprint(__file__, version=1, version_prefix="/ignore/v")
|
bp = Blueprint(__name__, version=1, version_prefix="/ignore/v")
|
||||||
bp.route("/", version=1.1, version_prefix="/api/v")(handler)
|
bp.route("/", version=1.1, version_prefix="/api/v")(handler)
|
||||||
app.blueprint(bp)
|
app.blueprint(bp)
|
||||||
|
|
||||||
|
@ -102,7 +102,7 @@ def test_version_prefix_bp_use_route(app, handler):
|
||||||
|
|
||||||
|
|
||||||
def test_version_prefix_bp_group(app, handler):
|
def test_version_prefix_bp_group(app, handler):
|
||||||
bp = Blueprint(__file__)
|
bp = Blueprint(__name__)
|
||||||
bp.route("/")(handler)
|
bp.route("/")(handler)
|
||||||
group = Blueprint.group(bp, version=1, version_prefix="/api/v")
|
group = Blueprint.group(bp, version=1, version_prefix="/api/v")
|
||||||
app.blueprint(group)
|
app.blueprint(group)
|
||||||
|
@ -112,7 +112,7 @@ def test_version_prefix_bp_group(app, handler):
|
||||||
|
|
||||||
|
|
||||||
def test_version_prefix_bp_group_use_bp(app, handler):
|
def test_version_prefix_bp_group_use_bp(app, handler):
|
||||||
bp = Blueprint(__file__, version=1.1, version_prefix="/api/v")
|
bp = Blueprint(__name__, version=1.1, version_prefix="/api/v")
|
||||||
bp.route("/")(handler)
|
bp.route("/")(handler)
|
||||||
group = Blueprint.group(bp, version=1, version_prefix="/ignore/v")
|
group = Blueprint.group(bp, version=1, version_prefix="/ignore/v")
|
||||||
app.blueprint(group)
|
app.blueprint(group)
|
||||||
|
@ -122,7 +122,7 @@ def test_version_prefix_bp_group_use_bp(app, handler):
|
||||||
|
|
||||||
|
|
||||||
def test_version_prefix_bp_group_use_registration(app, handler):
|
def test_version_prefix_bp_group_use_registration(app, handler):
|
||||||
bp = Blueprint(__file__, version=1.1, version_prefix="/alsoignore/v")
|
bp = Blueprint(__name__, version=1.1, version_prefix="/alsoignore/v")
|
||||||
bp.route("/")(handler)
|
bp.route("/")(handler)
|
||||||
group = Blueprint.group(bp, version=1, version_prefix="/ignore/v")
|
group = Blueprint.group(bp, version=1, version_prefix="/ignore/v")
|
||||||
app.blueprint(group, version=1.2, version_prefix="/api/v")
|
app.blueprint(group, version=1.2, version_prefix="/api/v")
|
||||||
|
@ -132,7 +132,7 @@ def test_version_prefix_bp_group_use_registration(app, handler):
|
||||||
|
|
||||||
|
|
||||||
def test_version_prefix_bp_group_use_route(app, handler):
|
def test_version_prefix_bp_group_use_route(app, handler):
|
||||||
bp = Blueprint(__file__, version=1.1, version_prefix="/alsoignore/v")
|
bp = Blueprint(__name__, version=1.1, version_prefix="/alsoignore/v")
|
||||||
bp.route("/", version=1.3, version_prefix="/api/v")(handler)
|
bp.route("/", version=1.3, version_prefix="/api/v")(handler)
|
||||||
group = Blueprint.group(bp, version=1, version_prefix="/ignore/v")
|
group = Blueprint.group(bp, version=1, version_prefix="/ignore/v")
|
||||||
app.blueprint(group, version=1.2, version_prefix="/stillignoring/v")
|
app.blueprint(group, version=1.2, version_prefix="/stillignoring/v")
|
||||||
|
|
|
@ -5,7 +5,7 @@ from sanic.constants import HTTP_METHODS
|
||||||
from sanic.exceptions import InvalidUsage
|
from sanic.exceptions import InvalidUsage
|
||||||
from sanic.request import Request
|
from sanic.request import Request
|
||||||
from sanic.response import HTTPResponse, text
|
from sanic.response import HTTPResponse, text
|
||||||
from sanic.views import CompositionView, HTTPMethodView
|
from sanic.views import HTTPMethodView
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("method", HTTP_METHODS)
|
@pytest.mark.parametrize("method", HTTP_METHODS)
|
||||||
|
@ -225,81 +225,3 @@ def test_with_decorator(app):
|
||||||
request, response = app.test_client.get("/")
|
request, response = app.test_client.get("/")
|
||||||
assert response.text == "I am get method"
|
assert response.text == "I am get method"
|
||||||
assert results[0] == 1
|
assert results[0] == 1
|
||||||
|
|
||||||
|
|
||||||
def test_composition_view_rejects_incorrect_methods():
|
|
||||||
def foo(request):
|
|
||||||
return text("Foo")
|
|
||||||
|
|
||||||
view = CompositionView()
|
|
||||||
|
|
||||||
with pytest.raises(InvalidUsage) as e:
|
|
||||||
view.add(["GET", "FOO"], foo)
|
|
||||||
|
|
||||||
assert str(e.value) == "FOO is not a valid HTTP method."
|
|
||||||
|
|
||||||
|
|
||||||
def test_composition_view_rejects_duplicate_methods():
|
|
||||||
def foo(request):
|
|
||||||
return text("Foo")
|
|
||||||
|
|
||||||
view = CompositionView()
|
|
||||||
|
|
||||||
with pytest.raises(InvalidUsage) as e:
|
|
||||||
view.add(["GET", "POST", "GET"], foo)
|
|
||||||
|
|
||||||
assert str(e.value) == "Method GET is already registered."
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("method", HTTP_METHODS)
|
|
||||||
def test_composition_view_runs_methods_as_expected(app, method):
|
|
||||||
view = CompositionView()
|
|
||||||
|
|
||||||
def first(request):
|
|
||||||
return text("first method")
|
|
||||||
|
|
||||||
view.add(["GET", "POST", "PUT"], first)
|
|
||||||
view.add(["DELETE", "PATCH"], lambda x: text("second method"))
|
|
||||||
|
|
||||||
app.add_route(view, "/")
|
|
||||||
|
|
||||||
if method in ["GET", "POST", "PUT"]:
|
|
||||||
request, response = getattr(app.test_client, method.lower())("/")
|
|
||||||
assert response.status == 200
|
|
||||||
assert response.text == "first method"
|
|
||||||
|
|
||||||
response = view(request)
|
|
||||||
assert response.body.decode() == "first method"
|
|
||||||
|
|
||||||
if method in ["DELETE", "PATCH"]:
|
|
||||||
request, response = getattr(app.test_client, method.lower())("/")
|
|
||||||
assert response.text == "second method"
|
|
||||||
|
|
||||||
response = view(request)
|
|
||||||
assert response.body.decode() == "second method"
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("method", HTTP_METHODS)
|
|
||||||
def test_composition_view_rejects_invalid_methods(app, method):
|
|
||||||
view = CompositionView()
|
|
||||||
view.add(["GET", "POST", "PUT"], lambda x: text("first method"))
|
|
||||||
|
|
||||||
app.add_route(view, "/")
|
|
||||||
|
|
||||||
if method in ["GET", "POST", "PUT"]:
|
|
||||||
request, response = getattr(app.test_client, method.lower())("/")
|
|
||||||
assert response.status == 200
|
|
||||||
assert response.text == "first method"
|
|
||||||
|
|
||||||
if method in ["DELETE", "PATCH"]:
|
|
||||||
request, response = getattr(app.test_client, method.lower())("/")
|
|
||||||
assert response.status == 405
|
|
||||||
|
|
||||||
|
|
||||||
def test_composition_view_deprecation():
|
|
||||||
message = (
|
|
||||||
"CompositionView has been deprecated and will be removed in v21.12. "
|
|
||||||
"Please update your view to HTTPMethodView."
|
|
||||||
)
|
|
||||||
with pytest.warns(DeprecationWarning, match=message):
|
|
||||||
CompositionView()
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user