Resolve merge conflict

This commit is contained in:
Adam Hopkins 2021-02-16 11:36:06 +02:00
commit 9e23e75fb4
15 changed files with 553 additions and 337 deletions

View File

@ -1,3 +1,31 @@
Version 20.12.2
===============
Dependencies
************
*
`#2026 <https://github.com/sanic-org/sanic/pull/2026>`_
Fix uvloop to 0.14 because 0.15 drops Python 3.6 support
*
`#2029 <https://github.com/sanic-org/sanic/pull/2029>`_
Remove old chardet requirement, add in hard multidict requirement
Version 19.12.5
===============
Dependencies
************
*
`#2025 <https://github.com/sanic-org/sanic/pull/2025>`_
Fix uvloop to 0.14 because 0.15 drops Python 3.6 support
*
`#2027 <https://github.com/sanic-org/sanic/pull/2027>`_
Remove old chardet requirement, add in hard multidict requirement
Version 20.12.0 Version 20.12.0
=============== ===============
@ -5,83 +33,93 @@ Features
******** ********
* *
`#1945 <https://github.com/huge-success/sanic/pull/1945>`_ `#1993 <https://github.com/sanic-org/sanic/pull/1993>`_
Add disable app registry
Version 20.12.0
===============
Features
********
*
`#1945 <https://github.com/sanic-org/sanic/pull/1945>`_
Static route more verbose if file not found Static route more verbose if file not found
* *
`#1954 <https://github.com/huge-success/sanic/pull/1954>`_ `#1954 <https://github.com/sanic-org/sanic/pull/1954>`_
Fix static routes registration on a blueprint Fix static routes registration on a blueprint
* *
`#1961 <https://github.com/huge-success/sanic/pull/1961>`_ `#1961 <https://github.com/sanic-org/sanic/pull/1961>`_
Add Python 3.9 support Add Python 3.9 support
* *
`#1962 <https://github.com/huge-success/sanic/pull/1962>`_ `#1962 <https://github.com/sanic-org/sanic/pull/1962>`_
Sanic CLI upgrade Sanic CLI upgrade
* *
`#1967 <https://github.com/huge-success/sanic/pull/1967>`_ `#1967 <https://github.com/sanic-org/sanic/pull/1967>`_
Update aiofile version requirements Update aiofile version requirements
* *
`#1969 <https://github.com/huge-success/sanic/pull/1969>`_ `#1969 <https://github.com/sanic-org/sanic/pull/1969>`_
Update multidict version requirements Update multidict version requirements
* *
`#1970 <https://github.com/huge-success/sanic/pull/1970>`_ `#1970 <https://github.com/sanic-org/sanic/pull/1970>`_
Add py.typed file Add py.typed file
* *
`#1972 <https://github.com/huge-success/sanic/pull/1972>`_ `#1972 <https://github.com/sanic-org/sanic/pull/1972>`_
Speed optimization in request handler Speed optimization in request handler
* *
`#1979 <https://github.com/huge-success/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/huge-success/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/huge-success/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/huge-success/sanic/pull/1956>`_ `#1956 <https://github.com/sanic-org/sanic/pull/1956>`_
Fix load module test Fix load module test
* *
`#1973 <https://github.com/huge-success/sanic/pull/1973>`_ `#1973 <https://github.com/sanic-org/sanic/pull/1973>`_
Transition Travis from .org to .com Transition Travis from .org to .com
* *
`#1986 <https://github.com/huge-success/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/huge-success/sanic/pull/1951>`_ `#1951 <https://github.com/sanic-org/sanic/pull/1951>`_
Documentation improvements Documentation improvements
* *
`#1983 <https://github.com/huge-success/sanic/pull/1983>`_ `#1983 <https://github.com/sanic-org/sanic/pull/1983>`_
Remove duplicate contents in testing.rst Remove duplicate contents in testing.rst
* *
`#1984 <https://github.com/huge-success/sanic/pull/1984>`_ `#1984 <https://github.com/sanic-org/sanic/pull/1984>`_
Fix typo in routing.rst Fix typo in routing.rst
@ -92,10 +130,10 @@ Bugfixes
******** ********
* *
`#1954 <https://github.com/huge-success/sanic/pull/1954>`_ `#1954 <https://github.com/sanic-org/sanic/pull/1954>`_
Fix static route registration on blueprints Fix static route registration on blueprints
* *
`#1957 <https://github.com/huge-success/sanic/pull/1957>`_ `#1957 <https://github.com/sanic-org/sanic/pull/1957>`_
Removes duplicate headers in ASGI streaming body Removes duplicate headers in ASGI streaming body
@ -106,7 +144,7 @@ Bugfixes
******** ********
* *
`#1959 <https://github.com/huge-success/sanic/pull/1959>`_ `#1959 <https://github.com/sanic-org/sanic/pull/1959>`_
Removes duplicate headers in ASGI streaming body Removes duplicate headers in ASGI streaming body
@ -118,65 +156,65 @@ Features
******** ********
* *
`#1887 <https://github.com/huge-success/sanic/pull/1887>`_ `#1887 <https://github.com/sanic-org/sanic/pull/1887>`_
Pass subprotocols in websockets (both sanic server and ASGI) Pass subprotocols in websockets (both sanic server and ASGI)
* *
`#1894 <https://github.com/huge-success/sanic/pull/1894>`_ `#1894 <https://github.com/sanic-org/sanic/pull/1894>`_
Automatically set ``test_mode`` flag on app instance Automatically set ``test_mode`` flag on app instance
* *
`#1903 <https://github.com/huge-success/sanic/pull/1903>`_ `#1903 <https://github.com/sanic-org/sanic/pull/1903>`_
Add new unified method for updating app values Add new unified method for updating app values
* *
`#1906 <https://github.com/huge-success/sanic/pull/1906>`_, `#1906 <https://github.com/sanic-org/sanic/pull/1906>`_,
`#1909 <https://github.com/huge-success/sanic/pull/1909>`_ `#1909 <https://github.com/sanic-org/sanic/pull/1909>`_
Adds WEBSOCKET_PING_TIMEOUT and WEBSOCKET_PING_INTERVAL configuration values Adds WEBSOCKET_PING_TIMEOUT and WEBSOCKET_PING_INTERVAL configuration values
* *
`#1935 <https://github.com/huge-success/sanic/pull/1935>`_ `#1935 <https://github.com/sanic-org/sanic/pull/1935>`_
httpx version dependency updated, it is slated for removal as a dependency in v20.12 httpx version dependency updated, it is slated for removal as a dependency in v20.12
* *
`#1937 <https://github.com/huge-success/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/huge-success/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/huge-success/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/huge-success/sanic/pull/1890>`_, `#1890 <https://github.com/sanic-org/sanic/pull/1890>`_,
`#1891 <https://github.com/huge-success/sanic/pull/1891>`_ `#1891 <https://github.com/sanic-org/sanic/pull/1891>`_
Update isort calls to be compatible with new API Update isort calls to be compatible with new API
* *
`#1893 <https://github.com/huge-success/sanic/pull/1893>`_ `#1893 <https://github.com/sanic-org/sanic/pull/1893>`_
Remove version section from setup.cfg Remove version section from setup.cfg
* *
`#1924 <https://github.com/huge-success/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/huge-success/sanic/pull/1922>`_ `#1922 <https://github.com/sanic-org/sanic/pull/1922>`_
Add explicit ASGI compliance to the README Add explicit ASGI compliance to the README
@ -187,7 +225,7 @@ Bugfixes
******** ********
* *
`#1884 <https://github.com/huge-success/sanic/pull/1884>`_ `#1884 <https://github.com/sanic-org/sanic/pull/1884>`_
Revert change to multiprocessing mode Revert change to multiprocessing mode
@ -198,7 +236,7 @@ Features
******** ********
* *
`#1641 <https://github.com/huge-success/sanic/pull/1641>`_ `#1641 <https://github.com/sanic-org/sanic/pull/1641>`_
Socket binding implemented properly for IPv6 and UNIX sockets Socket binding implemented properly for IPv6 and UNIX sockets
@ -209,60 +247,60 @@ Features
******** ********
* *
`#1760 <https://github.com/huge-success/sanic/pull/1760>`_ `#1760 <https://github.com/sanic-org/sanic/pull/1760>`_
Add version parameter to websocket routes Add version parameter to websocket routes
* *
`#1866 <https://github.com/huge-success/sanic/pull/1866>`_ `#1866 <https://github.com/sanic-org/sanic/pull/1866>`_
Add ``sanic`` as an entry point command Add ``sanic`` as an entry point command
* *
`#1880 <https://github.com/huge-success/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/huge-success/sanic/pull/1776>`_ `#1776 <https://github.com/sanic-org/sanic/pull/1776>`_
Bug fix for host parameter issue with lists Bug fix for host parameter issue with lists
* *
`#1842 <https://github.com/huge-success/sanic/pull/1842>`_ `#1842 <https://github.com/sanic-org/sanic/pull/1842>`_
Fix static _handler pickling error Fix static _handler pickling error
* *
`#1827 <https://github.com/huge-success/sanic/pull/1827>`_ `#1827 <https://github.com/sanic-org/sanic/pull/1827>`_
Fix reloader on OSX py38 and Windows Fix reloader on OSX py38 and Windows
* *
`#1848 <https://github.com/huge-success/sanic/pull/1848>`_ `#1848 <https://github.com/sanic-org/sanic/pull/1848>`_
Reverse named_response_middlware execution order, to match normal response middleware execution order Reverse named_response_middlware execution order, to match normal response middleware execution order
* *
`#1853 <https://github.com/huge-success/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/huge-success/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/huge-success/sanic/pull/1852>`_ `#1852 <https://github.com/sanic-org/sanic/pull/1852>`_
Fix naming of CI test env on Python nightlies Fix naming of CI test env on Python nightlies
* *
`#1857 <https://github.com/huge-success/sanic/pull/1857>`_ `#1857 <https://github.com/sanic-org/sanic/pull/1857>`_
Adjust websockets version to setup.py Adjust websockets version to setup.py
* *
`#1869 <https://github.com/huge-success/sanic/pull/1869>`_ `#1869 <https://github.com/sanic-org/sanic/pull/1869>`_
Wrap run()'s "protocol" type annotation in Optional[] Wrap run()'s "protocol" type annotation in Optional[]
@ -270,11 +308,11 @@ Improved Documentation
********************** **********************
* *
`#1846 <https://github.com/huge-success/sanic/pull/1846>`_ `#1846 <https://github.com/sanic-org/sanic/pull/1846>`_
Update docs to clarify response middleware execution order Update docs to clarify response middleware execution order
* *
`#1865 <https://github.com/huge-success/sanic/pull/1865>`_ `#1865 <https://github.com/sanic-org/sanic/pull/1865>`_
Fixing rst format issue that was hiding documentation Fixing rst format issue that was hiding documentation
@ -291,127 +329,127 @@ Features
******** ********
* *
`#1762 <https://github.com/huge-success/sanic/pull/1762>`_ `#1762 <https://github.com/sanic-org/sanic/pull/1762>`_
Add ``srv.start_serving()`` and ``srv.serve_forever()`` to ``AsyncioServer`` Add ``srv.start_serving()`` and ``srv.serve_forever()`` to ``AsyncioServer``
* *
`#1767 <https://github.com/huge-success/sanic/pull/1767>`_ `#1767 <https://github.com/sanic-org/sanic/pull/1767>`_
Make Sanic usable on ``hypercorn -k trio myweb.app`` Make Sanic usable on ``hypercorn -k trio myweb.app``
* *
`#1768 <https://github.com/huge-success/sanic/pull/1768>`_ `#1768 <https://github.com/sanic-org/sanic/pull/1768>`_
No tracebacks on normal errors and prettier error pages No tracebacks on normal errors and prettier error pages
* *
`#1769 <https://github.com/huge-success/sanic/pull/1769>`_ `#1769 <https://github.com/sanic-org/sanic/pull/1769>`_
Code cleanup in file responses Code cleanup in file responses
* *
`#1793 <https://github.com/huge-success/sanic/pull/1793>`_ and `#1793 <https://github.com/sanic-org/sanic/pull/1793>`_ and
`#1819 <https://github.com/huge-success/sanic/pull/1819>`_ `#1819 <https://github.com/sanic-org/sanic/pull/1819>`_
Upgrade ``str.format()`` to f-strings Upgrade ``str.format()`` to f-strings
* *
`#1798 <https://github.com/huge-success/sanic/pull/1798>`_ `#1798 <https://github.com/sanic-org/sanic/pull/1798>`_
Allow multiple workers on MacOS with Python 3.8 Allow multiple workers on MacOS with Python 3.8
* *
`#1820 <https://github.com/huge-success/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/huge-success/sanic/pull/1748>`_ `#1748 <https://github.com/sanic-org/sanic/pull/1748>`_
Remove loop argument in ``asyncio.Event`` in Python 3.8 Remove loop argument in ``asyncio.Event`` in Python 3.8
* *
`#1764 <https://github.com/huge-success/sanic/pull/1764>`_ `#1764 <https://github.com/sanic-org/sanic/pull/1764>`_
Allow route decorators to stack up again Allow route decorators to stack up again
* *
`#1789 <https://github.com/huge-success/sanic/pull/1789>`_ `#1789 <https://github.com/sanic-org/sanic/pull/1789>`_
Fix tests using hosts yielding incorrect ``url_for`` Fix tests using hosts yielding incorrect ``url_for``
* *
`#1808 <https://github.com/huge-success/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/huge-success/sanic/pull/1800>`_ `#1800 <https://github.com/sanic-org/sanic/pull/1800>`_
Begin deprecation in way of first-class streaming, removal of ``body_init``, ``body_push``, and ``body_finish`` Begin deprecation in way of first-class streaming, removal of ``body_init``, ``body_push``, and ``body_finish``
* *
`#1801 <https://github.com/huge-success/sanic/pull/1801>`_ `#1801 <https://github.com/sanic-org/sanic/pull/1801>`_
Complete deprecation from `#1666 <https://github.com/huge-success/sanic/pull/1666>`_ of dictionary context on ``request`` objects. Complete deprecation from `#1666 <https://github.com/sanic-org/sanic/pull/1666>`_ of dictionary context on ``request`` objects.
* *
`#1807 <https://github.com/huge-success/sanic/pull/1807>`_ `#1807 <https://github.com/sanic-org/sanic/pull/1807>`_
Remove server config args that can be read directly from app Remove server config args that can be read directly from app
* *
`#1818 <https://github.com/huge-success/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/huge-success/sanic/pull/1794>`_ `#1794 <https://github.com/sanic-org/sanic/pull/1794>`_
Bump ``httpx`` to 0.11.1 Bump ``httpx`` to 0.11.1
* *
`#1806 <https://github.com/huge-success/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/huge-success/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/huge-success/sanic/pull/1755>`_ `#1755 <https://github.com/sanic-org/sanic/pull/1755>`_
Usage of ``response.empty()`` Usage of ``response.empty()``
* *
`#1778 <https://github.com/huge-success/sanic/pull/1778>`_ `#1778 <https://github.com/sanic-org/sanic/pull/1778>`_
Update README Update README
* *
`#1783 <https://github.com/huge-success/sanic/pull/1783>`_ `#1783 <https://github.com/sanic-org/sanic/pull/1783>`_
Fix typo Fix typo
* *
`#1784 <https://github.com/huge-success/sanic/pull/1784>`_ `#1784 <https://github.com/sanic-org/sanic/pull/1784>`_
Corrected changelog for docs move of MD to RST (`#1691 <https://github.com/huge-success/sanic/pull/1691>`_) Corrected changelog for docs move of MD to RST (`#1691 <https://github.com/sanic-org/sanic/pull/1691>`_)
* *
`#1803 <https://github.com/huge-success/sanic/pull/1803>`_ `#1803 <https://github.com/sanic-org/sanic/pull/1803>`_
Update config docs to match DEFAULT_CONFIG Update config docs to match DEFAULT_CONFIG
* *
`#1814 <https://github.com/huge-success/sanic/pull/1814>`_ `#1814 <https://github.com/sanic-org/sanic/pull/1814>`_
Update getting_started.rst Update getting_started.rst
* *
`#1821 <https://github.com/huge-success/sanic/pull/1821>`_ `#1821 <https://github.com/sanic-org/sanic/pull/1821>`_
Update to deployment Update to deployment
* *
`#1822 <https://github.com/huge-success/sanic/pull/1822>`_ `#1822 <https://github.com/sanic-org/sanic/pull/1822>`_
Update docs with changes done in 20.3 Update docs with changes done in 20.3
* *
`#1834 <https://github.com/huge-success/sanic/pull/1834>`_ `#1834 <https://github.com/sanic-org/sanic/pull/1834>`_
Order of listeners Order of listeners
@ -431,11 +469,11 @@ Bugfixes
- If you register a middleware via :code:`@blueprint.middleware` then it will apply only to the routes defined by the blueprint. - If you register a middleware via :code:`@blueprint.middleware` then it will apply only to the routes defined by the blueprint.
- If you register a middleware via :code:`@blueprint_group.middleware` then it will apply to all blueprint based routes that are part of the group. - If you register a middleware via :code:`@blueprint_group.middleware` then it will apply to all blueprint based routes that are part of the group.
- If you define a middleware via :code:`@app.middleware` then it will be applied on all available routes (`#37 <https://github.com/huge-success/sanic/issues/37>`__) - If you define a middleware via :code:`@app.middleware` then it will be applied on all available routes (`#37 <https://github.com/sanic-org/sanic/issues/37>`__)
- Fix `url_for` behavior with missing SERVER_NAME - Fix `url_for` behavior with missing SERVER_NAME
If the `SERVER_NAME` was missing in the `app.config` entity, the `url_for` on the `request` and `app` were failing If the `SERVER_NAME` was missing in the `app.config` entity, the `url_for` on the `request` and `app` were failing
due to an `AttributeError`. This fix makes the availability of `SERVER_NAME` on our `app.config` an optional behavior. (`#1707 <https://github.com/huge-success/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
@ -444,10 +482,10 @@ Improved Documentation
- Move docs from MD to RST - Move docs from MD to RST
Moved all docs from markdown to restructured text like the rest of the docs to unify the scheme and make it easier in Moved all docs from markdown to restructured text like the rest of the docs to unify the scheme and make it easier in
the future to update documentation. (`#1691 <https://github.com/huge-success/sanic/issues/1691>`__) the future to update documentation. (`#1691 <https://github.com/sanic-org/sanic/issues/1691>`__)
- Fix documentation for `get` and `getlist` of the `request.args` - Fix documentation for `get` and `getlist` of the `request.args`
Add additional example for showing the usage of `getlist` and fix the documentation string for `request.args` behavior (`#1704 <https://github.com/huge-success/sanic/issues/1704>`__) Add additional example for showing the usage of `getlist` and fix the documentation string for `request.args` behavior (`#1704 <https://github.com/sanic-org/sanic/issues/1704>`__)
Version 19.6.3 Version 19.6.3
@ -459,7 +497,7 @@ Features
- Enable Towncrier Support - Enable Towncrier Support
As part of this feature, `towncrier` is being introduced as a mechanism to partially automate the process As part of this feature, `towncrier` is being introduced as a mechanism to partially automate the process
of generating and managing change logs as part of each of pull requests. (`#1631 <https://github.com/huge-success/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
@ -470,7 +508,7 @@ Improved Documentation
- Enable having a single common `CHANGELOG` file for both GitHub page and documentation - Enable having a single common `CHANGELOG` file for both GitHub page and documentation
- Fix Sphinix deprecation warnings - Fix Sphinix deprecation warnings
- Fix documentation warnings due to invalid `rst` indentation - Fix documentation warnings due to invalid `rst` indentation
- Enable common contribution guidelines file across GitHub and documentation via `CONTRIBUTING.rst` (`#1631 <https://github.com/huge-success/sanic/issues/1631>`__) - Enable common contribution guidelines file across GitHub and documentation via `CONTRIBUTING.rst` (`#1631 <https://github.com/sanic-org/sanic/issues/1631>`__)
Version 19.6.2 Version 19.6.2
@ -480,16 +518,16 @@ Features
******** ********
* *
`#1562 <https://github.com/huge-success/sanic/pull/1562>`_ `#1562 <https://github.com/sanic-org/sanic/pull/1562>`_
Remove ``aiohttp`` dependency and create new ``SanicTestClient`` based upon Remove ``aiohttp`` dependency and create new ``SanicTestClient`` based upon
`requests-async <https://github.com/encode/requests-async>`_ `requests-async <https://github.com/encode/requests-async>`_
* *
`#1475 <https://github.com/huge-success/sanic/pull/1475>`_ `#1475 <https://github.com/sanic-org/sanic/pull/1475>`_
Added ASGI support (Beta) Added ASGI support (Beta)
* *
`#1436 <https://github.com/huge-success/sanic/pull/1436>`_ `#1436 <https://github.com/sanic-org/sanic/pull/1436>`_
Add Configure support from object string Add Configure support from object string
@ -497,34 +535,34 @@ Bugfixes
******** ********
* *
`#1587 <https://github.com/huge-success/sanic/pull/1587>`_ `#1587 <https://github.com/sanic-org/sanic/pull/1587>`_
Add missing handle for Expect header. Add missing handle for Expect header.
* *
`#1560 <https://github.com/huge-success/sanic/pull/1560>`_ `#1560 <https://github.com/sanic-org/sanic/pull/1560>`_
Allow to disable Transfer-Encoding: chunked. Allow to disable Transfer-Encoding: chunked.
* *
`#1558 <https://github.com/huge-success/sanic/pull/1558>`_ `#1558 <https://github.com/sanic-org/sanic/pull/1558>`_
Fix graceful shutdown. Fix graceful shutdown.
* *
`#1594 <https://github.com/huge-success/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/huge-success/sanic/pull/1544>`_ `#1544 <https://github.com/sanic-org/sanic/pull/1544>`_
Drop dependency on distutil Drop dependency on distutil
* *
`#1562 <https://github.com/huge-success/sanic/pull/1562>`_ `#1562 <https://github.com/sanic-org/sanic/pull/1562>`_
Drop support for Python 3.5 Drop support for Python 3.5
* *
`#1568 <https://github.com/huge-success/sanic/pull/1568>`_ `#1568 <https://github.com/sanic-org/sanic/pull/1568>`_
Deprecate route removal. Deprecate route removal.
.. warning:: .. warning::
@ -541,39 +579,39 @@ Features
******** ********
* *
`#1497 <https://github.com/huge-success/sanic/pull/1497>`_ `#1497 <https://github.com/sanic-org/sanic/pull/1497>`_
Add support for zero-length and RFC 5987 encoded filename for Add support for zero-length and RFC 5987 encoded filename for
multipart/form-data requests. multipart/form-data requests.
* *
`#1484 <https://github.com/huge-success/sanic/pull/1484>`_ `#1484 <https://github.com/sanic-org/sanic/pull/1484>`_
The type of ``expires`` attribute of ``sanic.cookies.Cookie`` is now The type of ``expires`` attribute of ``sanic.cookies.Cookie`` is now
enforced to be of type ``datetime``. enforced to be of type ``datetime``.
* *
`#1482 <https://github.com/huge-success/sanic/pull/1482>`_ `#1482 <https://github.com/sanic-org/sanic/pull/1482>`_
Add support for the ``stream`` parameter of ``sanic.Sanic.add_route()`` Add support for the ``stream`` parameter of ``sanic.Sanic.add_route()``
available to ``sanic.Blueprint.add_route()``. available to ``sanic.Blueprint.add_route()``.
* *
`#1481 <https://github.com/huge-success/sanic/pull/1481>`_ `#1481 <https://github.com/sanic-org/sanic/pull/1481>`_
Accept negative values for route parameters with type ``int`` or ``number``. Accept negative values for route parameters with type ``int`` or ``number``.
* *
`#1476 <https://github.com/huge-success/sanic/pull/1476>`_ `#1476 <https://github.com/sanic-org/sanic/pull/1476>`_
Deprecated the use of ``sanic.request.Request.raw_args`` - it has a Deprecated the use of ``sanic.request.Request.raw_args`` - it has a
fundamental flaw in which is drops repeated query string parameters. fundamental flaw in which is drops repeated query string parameters.
Added ``sanic.request.Request.query_args`` as a replacement for the Added ``sanic.request.Request.query_args`` as a replacement for the
original use-case. original use-case.
* *
`#1472 <https://github.com/huge-success/sanic/pull/1472>`_ `#1472 <https://github.com/sanic-org/sanic/pull/1472>`_
Remove an unwanted ``None`` check in Request class ``repr`` implementation. Remove an unwanted ``None`` check in Request class ``repr`` implementation.
This changes the default ``repr`` of a Request from ``<Request>`` to This changes the default ``repr`` of a Request from ``<Request>`` to
``<Request: None />`` ``<Request: None />``
* *
`#1470 <https://github.com/huge-success/sanic/pull/1470>`_ `#1470 <https://github.com/sanic-org/sanic/pull/1470>`_
Added 2 new parameters to ``sanic.app.Sanic.create_server``\ : Added 2 new parameters to ``sanic.app.Sanic.create_server``\ :
@ -584,21 +622,21 @@ Features
This is a breaking change. This is a breaking change.
* *
`#1499 <https://github.com/huge-success/sanic/pull/1499>`_ `#1499 <https://github.com/sanic-org/sanic/pull/1499>`_
Added a set of test cases that test and benchmark route resolution. Added a set of test cases that test and benchmark route resolution.
* *
`#1457 <https://github.com/huge-success/sanic/pull/1457>`_ `#1457 <https://github.com/sanic-org/sanic/pull/1457>`_
The type of the ``"max-age"`` value in a ``sanic.cookies.Cookie`` is now The type of the ``"max-age"`` value in a ``sanic.cookies.Cookie`` is now
enforced to be an integer. Non-integer values are replaced with ``0``. enforced to be an integer. Non-integer values are replaced with ``0``.
* *
`#1445 <https://github.com/huge-success/sanic/pull/1445>`_ `#1445 <https://github.com/sanic-org/sanic/pull/1445>`_
Added the ``endpoint`` attribute to an incoming ``request``\ , containing the Added the ``endpoint`` attribute to an incoming ``request``\ , containing the
name of the handler function. name of the handler function.
* *
`#1423 <https://github.com/huge-success/sanic/pull/1423>`_ `#1423 <https://github.com/sanic-org/sanic/pull/1423>`_
Improved request streaming. ``request.stream`` is now a bounded-size buffer Improved request streaming. ``request.stream`` is now a bounded-size buffer
instead of an unbounded queue. Callers must now call instead of an unbounded queue. Callers must now call
``await request.stream.read()`` instead of ``await request.stream.get()`` ``await request.stream.read()`` instead of ``await request.stream.get()``
@ -611,33 +649,33 @@ Bugfixes
* *
`#1502 <https://github.com/huge-success/sanic/pull/1502>`_ `#1502 <https://github.com/sanic-org/sanic/pull/1502>`_
Sanic was prefetching ``time.time()`` and updating it once per second to Sanic was prefetching ``time.time()`` and updating it once per second to
avoid excessive ``time.time()`` calls. The implementation was observed to avoid excessive ``time.time()`` calls. The implementation was observed to
cause memory leaks in some cases. The benefit of the prefetch appeared cause memory leaks in some cases. The benefit of the prefetch appeared
to negligible, so this has been removed. Fixes to negligible, so this has been removed. Fixes
`#1500 <https://github.com/huge-success/sanic/pull/1500>`_ `#1500 <https://github.com/sanic-org/sanic/pull/1500>`_
* *
`#1501 <https://github.com/huge-success/sanic/pull/1501>`_ `#1501 <https://github.com/sanic-org/sanic/pull/1501>`_
Fix a bug in the auto-reloader when the process was launched as a module Fix a bug in the auto-reloader when the process was launched as a module
i.e. ``python -m init0.mod1`` where the sanic server is started i.e. ``python -m init0.mod1`` where the sanic server is started
in ``init0/mod1.py`` with ``debug`` enabled and imports another module in in ``init0/mod1.py`` with ``debug`` enabled and imports another module in
``init0``. ``init0``.
* *
`#1376 <https://github.com/huge-success/sanic/pull/1376>`_ `#1376 <https://github.com/sanic-org/sanic/pull/1376>`_
Allow sanic test client to bind to a random port by specifying Allow sanic test client to bind to a random port by specifying
``port=None`` when constructing a ``SanicTestClient`` ``port=None`` when constructing a ``SanicTestClient``
* *
`#1399 <https://github.com/huge-success/sanic/pull/1399>`_ `#1399 <https://github.com/sanic-org/sanic/pull/1399>`_
Added the ability to specify middleware on a blueprint group, so that all Added the ability to specify middleware on a blueprint group, so that all
routes produced from the blueprints in the group have the middleware routes produced from the blueprints in the group have the middleware
applied. applied.
* *
`#1442 <https://github.com/huge-success/sanic/pull/1442>`_ `#1442 <https://github.com/sanic-org/sanic/pull/1442>`_
Allow the the use the ``SANIC_ACCESS_LOG`` environment variable to Allow the the use the ``SANIC_ACCESS_LOG`` environment variable to
enable/disable the access log when not explicitly passed to ``app.run()``. enable/disable the access log when not explicitly passed to ``app.run()``.
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
@ -646,29 +684,29 @@ Bugfixes
Developer infrastructure Developer infrastructure
************************ ************************
* `#1529 <https://github.com/huge-success/sanic/pull/1529>`_ Update project PyPI credentials * `#1529 <https://github.com/sanic-org/sanic/pull/1529>`_ Update project PyPI credentials
* `#1515 <https://github.com/huge-success/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)
* `#1490 <https://github.com/huge-success/sanic/pull/1490>`_ Fix python version in doc build * `#1490 <https://github.com/sanic-org/sanic/pull/1490>`_ Fix python version in doc build
* `#1478 <https://github.com/huge-success/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/huge-success/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/huge-success/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/huge-success/sanic/pull/1510>`_ fix typo in Asyncio example * `#1510 <https://github.com/sanic-org/sanic/pull/1510>`_ fix typo in Asyncio example
* `#1486 <https://github.com/huge-success/sanic/pull/1486>`_ Documentation typo * `#1486 <https://github.com/sanic-org/sanic/pull/1486>`_ Documentation typo
* `#1477 <https://github.com/huge-success/sanic/pull/1477>`_ Fix grammar in README.md * `#1477 <https://github.com/sanic-org/sanic/pull/1477>`_ Fix grammar in README.md
* `#1489 <https://github.com/huge-success/sanic/pull/1489>`_ Added "databases" to the extensions list * `#1489 <https://github.com/sanic-org/sanic/pull/1489>`_ Added "databases" to the extensions list
* `#1483 <https://github.com/huge-success/sanic/pull/1483>`_ Add sanic-zipkin to extensions list * `#1483 <https://github.com/sanic-org/sanic/pull/1483>`_ Add sanic-zipkin to extensions list
* `#1487 <https://github.com/huge-success/sanic/pull/1487>`_ Removed link to deleted repo, Sanic-OAuth, from the extensions list * `#1487 <https://github.com/sanic-org/sanic/pull/1487>`_ Removed link to deleted repo, Sanic-OAuth, from the extensions list
* `#1460 <https://github.com/huge-success/sanic/pull/1460>`_ 18.12 changelog * `#1460 <https://github.com/sanic-org/sanic/pull/1460>`_ 18.12 changelog
* `#1449 <https://github.com/huge-success/sanic/pull/1449>`_ Add example of amending request object * `#1449 <https://github.com/sanic-org/sanic/pull/1449>`_ Add example of amending request object
* `#1446 <https://github.com/huge-success/sanic/pull/1446>`_ Update README * `#1446 <https://github.com/sanic-org/sanic/pull/1446>`_ Update README
* `#1444 <https://github.com/huge-success/sanic/pull/1444>`_ Update README * `#1444 <https://github.com/sanic-org/sanic/pull/1444>`_ Update README
* `#1443 <https://github.com/huge-success/sanic/pull/1443>`_ Update README, including new logo * `#1443 <https://github.com/sanic-org/sanic/pull/1443>`_ Update README, including new logo
* `#1440 <https://github.com/huge-success/sanic/pull/1440>`_ fix minor type and pip install instruction mismatch * `#1440 <https://github.com/sanic-org/sanic/pull/1440>`_ fix minor type and pip install instruction mismatch
* `#1424 <https://github.com/huge-success/sanic/pull/1424>`_ Documentation Enhancements * `#1424 <https://github.com/sanic-org/sanic/pull/1424>`_ Documentation Enhancements
Note: 19.3.0 was skipped for packagement purposes and not released on PyPI Note: 19.3.0 was skipped for packagement purposes and not released on PyPI
@ -725,7 +763,7 @@ Version 0.8
* Changes: * Changes:
* Ownership changed to org 'huge-success' * Ownership changed to org 'sanic-org'
0.8.0 0.8.0
***** *****

View File

@ -61,7 +61,6 @@ from sanic.server import (
serve_multiple, serve_multiple,
trigger_events, trigger_events,
) )
from sanic.static import register as static_register
from sanic.websocket import ConnectionClosed, WebSocketProtocol from sanic.websocket import ConnectionClosed, WebSocketProtocol
@ -282,7 +281,7 @@ class Sanic(BaseSanic):
return self.router.add(**params) return self.router.add(**params)
def _apply_static(self, static: FutureStatic) -> Route: def _apply_static(self, static: FutureStatic) -> Route:
return static_register(self, static) return self._register_static(static)
def _apply_middleware( def _apply_middleware(
self, self,

View File

@ -34,7 +34,7 @@ class Header(CIMultiDict):
use_trio = argv[0].endswith("hypercorn") and "trio" in argv use_trio = argv[0].endswith("hypercorn") and "trio" in argv
if use_trio: if use_trio: # pragma: no cover
import trio # type: ignore import trio # type: ignore
def stat_async(path): def stat_async(path):

View File

@ -8,7 +8,7 @@ class ExceptionMixin:
self._future_exceptions: Set[FutureException] = set() self._future_exceptions: Set[FutureException] = set()
def _apply_exception_handler(self, handler: FutureException): def _apply_exception_handler(self, handler: FutureException):
raise NotImplementedError raise NotImplementedError # noqa
def exception(self, *exceptions, apply=True): def exception(self, *exceptions, apply=True):
""" """

View File

@ -20,7 +20,7 @@ class ListenerMixin:
self._future_listeners: List[FutureListener] = list() self._future_listeners: List[FutureListener] = list()
def _apply_listener(self, listener: FutureListener): def _apply_listener(self, listener: FutureListener):
raise NotImplementedError raise NotImplementedError # noqa
def listener( def listener(
self, self,

View File

@ -9,7 +9,7 @@ class MiddlewareMixin:
self._future_middleware: List[FutureMiddleware] = list() self._future_middleware: List[FutureMiddleware] = list()
def _apply_middleware(self, middleware: FutureMiddleware): def _apply_middleware(self, middleware: FutureMiddleware):
raise NotImplementedError raise NotImplementedError # noqa
def middleware( def middleware(
self, middleware_or_request, attach_to="request", apply=True self, middleware_or_request, attach_to="request", apply=True
@ -45,8 +45,14 @@ class MiddlewareMixin:
register_middleware, attach_to=middleware_or_request register_middleware, attach_to=middleware_or_request
) )
def on_request(self, middleware): def on_request(self, middleware=None):
return self.middleware(middleware, "request") if callable(middleware):
return self.middleware(middleware, "request")
else:
return partial(self.middleware, attach_to="request")
def on_response(self, middleware): def on_response(self, middleware=None):
return self.middleware(middleware, "response") if callable(middleware):
return self.middleware(middleware, "response")
else:
return partial(self.middleware, attach_to="response")

View File

@ -1,11 +1,27 @@
from functools import partial, wraps
from inspect import signature from inspect import signature
from mimetypes import guess_type
from os import path
from pathlib import PurePath from pathlib import PurePath
from re import sub
from time import gmtime, strftime
from typing import Iterable, List, Optional, Set, Union from typing import Iterable, List, Optional, Set, Union
from urllib.parse import unquote
from sanic_routing.route import Route # type: ignore from sanic_routing.route import Route # type: ignore
from sanic.compat import stat_async
from sanic.constants import HTTP_METHODS from sanic.constants import HTTP_METHODS
from sanic.exceptions import (
ContentRangeError,
FileNotFound,
HeaderNotFound,
InvalidUsage,
)
from sanic.handlers import ContentRangeHandler
from sanic.log import error_logger
from sanic.models.futures import FutureRoute, FutureStatic from sanic.models.futures import FutureRoute, FutureStatic
from sanic.response import HTTPResponse, file, file_stream
from sanic.views import CompositionView from sanic.views import CompositionView
@ -17,10 +33,10 @@ class RouteMixin:
self.strict_slashes = False self.strict_slashes = False
def _apply_route(self, route: FutureRoute) -> Route: def _apply_route(self, route: FutureRoute) -> Route:
raise NotImplementedError raise NotImplementedError # noqa
def _apply_static(self, static: FutureStatic) -> Route: def _apply_static(self, static: FutureStatic) -> Route:
raise NotImplementedError raise NotImplementedError # noqa
def route( def route(
self, self,
@ -595,10 +611,191 @@ class RouteMixin:
else: else:
break break
if not name: if not name: # noq
raise Exception("...") raise ValueError("Could not generate a name for handler")
if not name.startswith(f"{self.name}."): if not name.startswith(f"{self.name}."):
name = f"{self.name}.{name}" name = f"{self.name}.{name}"
return name return name
async def _static_request_handler(
self,
file_or_directory,
use_modified_since,
use_content_range,
stream_large_files,
request,
content_type=None,
file_uri=None,
):
# Using this to determine if the URL is trying to break out of the path
# served. os.path.realpath seems to be very slow
if file_uri and "../" in file_uri:
raise InvalidUsage("Invalid URL")
# Merge served directory and requested file if provided
# Strip all / that in the beginning of the URL to help prevent python
# from herping a derp and treating the uri as an absolute path
root_path = file_path = file_or_directory
if file_uri:
file_path = path.join(
file_or_directory, sub("^[/]*", "", file_uri)
)
# URL decode the path sent by the browser otherwise we won't be able to
# match filenames which got encoded (filenames with spaces etc)
file_path = path.abspath(unquote(file_path))
if not file_path.startswith(path.abspath(unquote(root_path))):
error_logger.exception(
f"File not found: path={file_or_directory}, "
f"relative_url={file_uri}"
)
raise FileNotFound(
"File not found", path=file_or_directory, relative_url=file_uri
)
try:
headers = {}
# Check if the client has been sent this file before
# and it has not been modified since
stats = None
if use_modified_since:
stats = await stat_async(file_path)
modified_since = strftime(
"%a, %d %b %Y %H:%M:%S GMT", gmtime(stats.st_mtime)
)
if request.headers.get("If-Modified-Since") == modified_since:
return HTTPResponse(status=304)
headers["Last-Modified"] = modified_since
_range = None
if use_content_range:
_range = None
if not stats:
stats = await stat_async(file_path)
headers["Accept-Ranges"] = "bytes"
headers["Content-Length"] = str(stats.st_size)
if request.method != "HEAD":
try:
_range = ContentRangeHandler(request, stats)
except HeaderNotFound:
pass
else:
del headers["Content-Length"]
for key, value in _range.headers.items():
headers[key] = value
if "content-type" not in headers:
content_type = (
content_type
or guess_type(file_path)[0]
or "application/octet-stream"
)
if "charset=" not in content_type and (
content_type.startswith("text/")
or content_type == "application/javascript"
):
content_type += "; charset=utf-8"
headers["Content-Type"] = content_type
if request.method == "HEAD":
return HTTPResponse(headers=headers)
else:
if stream_large_files:
if type(stream_large_files) == int:
threshold = stream_large_files
else:
threshold = 1024 * 1024
if not stats:
stats = await stat_async(file_path)
if stats.st_size >= threshold:
return await file_stream(
file_path, headers=headers, _range=_range
)
return await file(file_path, headers=headers, _range=_range)
except ContentRangeError:
raise
except Exception:
error_logger.exception(
f"File not found: path={file_or_directory}, "
f"relative_url={file_uri}"
)
raise FileNotFound(
"File not found", path=file_or_directory, relative_url=file_uri
)
def _register_static(
self,
static: FutureStatic,
):
# TODO: Though sanic is not a file server, I feel like we should
# at least make a good effort here. Modified-since is nice, but
# we could also look into etags, expires, and caching
"""
Register a static directory handler with Sanic by adding a route to the
router and registering a handler.
:param app: Sanic
:param file_or_directory: File or directory path to serve from
:type file_or_directory: Union[str,bytes,Path]
:param uri: URL to serve from
:type uri: str
:param pattern: regular expression used to match files in the URL
:param use_modified_since: If true, send file modified time, and return
not modified if the browser's matches the
server's
:param use_content_range: If true, process header for range requests
and sends the file part that is requested
:param stream_large_files: If true, use the file_stream() handler
rather than the file() handler to send the file
If this is an integer, this represents the
threshold size to switch to file_stream()
:param name: user defined name used for url_for
:type name: str
:param content_type: user defined content type for header
:return: registered static routes
:rtype: List[sanic.router.Route]
"""
if isinstance(static.file_or_directory, bytes):
file_or_directory = static.file_or_directory.decode("utf-8")
elif isinstance(static.file_or_directory, PurePath):
file_or_directory = str(static.file_or_directory)
elif not isinstance(static.file_or_directory, str):
raise ValueError("Invalid file path string.")
else:
file_or_directory = static.file_or_directory
uri = static.uri
name = static.name
# If we're not trying to match a file directly,
# serve from the folder
if not path.isfile(file_or_directory):
uri += "/<file_uri>"
# special prefix for static files
# if not static.name.startswith("_static_"):
# name = f"_static_{static.name}"
_handler = wraps(self._static_request_handler)(
partial(
self._static_request_handler,
file_or_directory,
static.use_modified_since,
static.use_content_range,
static.stream_large_files,
content_type=static.content_type,
)
)
route, _ = self.route(
uri=uri,
methods=["GET", "HEAD"],
name=name,
host=static.host,
strict_slashes=static.strict_slashes,
static=True,
)(_handler)
return route

View File

@ -1,186 +0,0 @@
from functools import partial, wraps
from mimetypes import guess_type
from os import path
from pathlib import PurePath
from re import sub
from time import gmtime, strftime
from urllib.parse import unquote
from sanic.compat import stat_async
from sanic.exceptions import (
ContentRangeError,
FileNotFound,
HeaderNotFound,
InvalidUsage,
)
from sanic.handlers import ContentRangeHandler
from sanic.log import error_logger
from sanic.models.futures import FutureStatic
from sanic.response import HTTPResponse, file, file_stream
async def _static_request_handler(
file_or_directory,
use_modified_since,
use_content_range,
stream_large_files,
request,
content_type=None,
file_uri=None,
):
# Using this to determine if the URL is trying to break out of the path
# served. os.path.realpath seems to be very slow
if file_uri and "../" in file_uri:
raise InvalidUsage("Invalid URL")
# Merge served directory and requested file if provided
# Strip all / that in the beginning of the URL to help prevent python
# from herping a derp and treating the uri as an absolute path
root_path = file_path = file_or_directory
if file_uri:
file_path = path.join(file_or_directory, sub("^[/]*", "", file_uri))
# URL decode the path sent by the browser otherwise we won't be able to
# match filenames which got encoded (filenames with spaces etc)
file_path = path.abspath(unquote(file_path))
if not file_path.startswith(path.abspath(unquote(root_path))):
error_logger.exception(
f"File not found: path={file_or_directory}, "
f"relative_url={file_uri}"
)
raise FileNotFound(
"File not found", path=file_or_directory, relative_url=file_uri
)
try:
headers = {}
# Check if the client has been sent this file before
# and it has not been modified since
stats = None
if use_modified_since:
stats = await stat_async(file_path)
modified_since = strftime(
"%a, %d %b %Y %H:%M:%S GMT", gmtime(stats.st_mtime)
)
if request.headers.get("If-Modified-Since") == modified_since:
return HTTPResponse(status=304)
headers["Last-Modified"] = modified_since
_range = None
if use_content_range:
_range = None
if not stats:
stats = await stat_async(file_path)
headers["Accept-Ranges"] = "bytes"
headers["Content-Length"] = str(stats.st_size)
if request.method != "HEAD":
try:
_range = ContentRangeHandler(request, stats)
except HeaderNotFound:
pass
else:
del headers["Content-Length"]
for key, value in _range.headers.items():
headers[key] = value
headers["Content-Type"] = (
content_type or guess_type(file_path)[0] or "text/plain"
)
if request.method == "HEAD":
return HTTPResponse(headers=headers)
else:
if stream_large_files:
if type(stream_large_files) == int:
threshold = stream_large_files
else:
threshold = 1024 * 1024
if not stats:
stats = await stat_async(file_path)
if stats.st_size >= threshold:
return await file_stream(
file_path, headers=headers, _range=_range
)
return await file(file_path, headers=headers, _range=_range)
except ContentRangeError:
raise
except Exception:
error_logger.exception(
f"File not found: path={file_or_directory}, "
f"relative_url={file_uri}"
)
raise FileNotFound(
"File not found", path=file_or_directory, relative_url=file_uri
)
def register(
app,
static: FutureStatic,
):
# TODO: Though sanic is not a file server, I feel like we should at least
# make a good effort here. Modified-since is nice, but we could
# also look into etags, expires, and caching
"""
Register a static directory handler with Sanic by adding a route to the
router and registering a handler.
:param app: Sanic
:param file_or_directory: File or directory path to serve from
:type file_or_directory: Union[str,bytes,Path]
:param uri: URL to serve from
:type uri: str
:param pattern: regular expression used to match files in the URL
:param use_modified_since: If true, send file modified time, and return
not modified if the browser's matches the
server's
:param use_content_range: If true, process header for range requests
and sends the file part that is requested
:param stream_large_files: If true, use the file_stream() handler rather
than the file() handler to send the file
If this is an integer, this represents the
threshold size to switch to file_stream()
:param name: user defined name used for url_for
:type name: str
:param content_type: user defined content type for header
:return: registered static routes
:rtype: List[sanic.router.Route]
"""
if isinstance(static.file_or_directory, bytes):
file_or_directory = static.file_or_directory.decode("utf-8")
elif isinstance(static.file_or_directory, PurePath):
file_or_directory = str(static.file_or_directory)
elif not isinstance(static.file_or_directory, str):
raise ValueError("Invalid file path string.")
else:
file_or_directory = static.file_or_directory
uri = static.uri
name = static.name
# If we're not trying to match a file directly,
# serve from the folder
if not path.isfile(file_or_directory):
uri += "/<file_uri>"
# special prefix for static files
# if not static.name.startswith("_static_"):
# name = f"_static_{static.name}"
_handler = wraps(_static_request_handler)(
partial(
_static_request_handler,
file_or_directory,
static.use_modified_since,
static.use_content_range,
static.stream_large_files,
content_type=static.content_type,
)
)
route, _ = app.route(
uri=uri,
methods=["GET", "HEAD"],
name=name,
host=static.host,
strict_slashes=static.strict_slashes,
static=True,
)(_handler)
return route

View File

@ -44,7 +44,7 @@ def str_to_bool(val: str) -> bool:
def load_module_from_file_location( def load_module_from_file_location(
location: Union[bytes, str, Path], encoding: str = "utf8", *args, **kwargs location: Union[bytes, str, Path], encoding: str = "utf8", *args, **kwargs
): ): # noqa
"""Returns loaded module provided as a file path. """Returns loaded module provided as a file path.
:param args: :param args:

View File

@ -57,7 +57,8 @@ setup_kwargs = {
"author": "Sanic Community", "author": "Sanic Community",
"author_email": "admhpkns@gmail.com", "author_email": "admhpkns@gmail.com",
"description": ( "description": (
"A web server and web framework that's written to go fast. Build fast. Run fast." "A web server and web framework that's written to go fast. "
"Build fast. Run fast."
), ),
"long_description": long_description, "long_description": long_description,
"packages": ["sanic"], "packages": ["sanic"],
@ -80,7 +81,7 @@ env_dependency = (
'; sys_platform != "win32" ' 'and implementation_name == "cpython"' '; sys_platform != "win32" ' 'and implementation_name == "cpython"'
) )
ujson = "ujson>=1.35" + env_dependency ujson = "ujson>=1.35" + env_dependency
uvloop = "uvloop>=0.5.3" + env_dependency uvloop = "uvloop>=0.5.3,<0.15.0" + env_dependency
requirements = [ requirements = [
"sanic-routing", "sanic-routing",

View File

@ -110,6 +110,11 @@ def test_bp_group(app: Sanic):
global MIDDLEWARE_INVOKE_COUNTER global MIDDLEWARE_INVOKE_COUNTER
MIDDLEWARE_INVOKE_COUNTER["request"] += 1 MIDDLEWARE_INVOKE_COUNTER["request"] += 1
@blueprint_group_1.middleware
def blueprint_group_1_middleware_not_called(request):
global MIDDLEWARE_INVOKE_COUNTER
MIDDLEWARE_INVOKE_COUNTER["request"] += 1
@blueprint_3.route("/") @blueprint_3.route("/")
def blueprint_3_default_route(request): def blueprint_3_default_route(request):
return text("BP3_OK") return text("BP3_OK")
@ -142,7 +147,7 @@ def test_bp_group(app: Sanic):
assert response.text == "BP3_OK" assert response.text == "BP3_OK"
assert MIDDLEWARE_INVOKE_COUNTER["response"] == 3 assert MIDDLEWARE_INVOKE_COUNTER["response"] == 3
assert MIDDLEWARE_INVOKE_COUNTER["request"] == 2 assert MIDDLEWARE_INVOKE_COUNTER["request"] == 4
def test_bp_group_list_operations(app: Sanic): def test_bp_group_list_operations(app: Sanic):
@ -179,3 +184,19 @@ def test_bp_group_list_operations(app: Sanic):
assert len(blueprint_group_1) == 2 assert len(blueprint_group_1) == 2
assert blueprint_group_1.url_prefix == "/bp" assert blueprint_group_1.url_prefix == "/bp"
def test_bp_group_as_list():
blueprint_1 = Blueprint("blueprint_1", url_prefix="/bp1")
blueprint_2 = Blueprint("blueprint_2", url_prefix="/bp2")
blueprint_group_1 = Blueprint.group([blueprint_1, blueprint_2])
assert len(blueprint_group_1) == 2
def test_bp_group_as_nested_group():
blueprint_1 = Blueprint("blueprint_1", url_prefix="/bp1")
blueprint_2 = Blueprint("blueprint_2", url_prefix="/bp2")
blueprint_group_1 = Blueprint.group(
Blueprint.group(blueprint_1, blueprint_2)
)
assert len(blueprint_group_1) == 2

View File

@ -30,6 +30,23 @@ def test_middleware_request(app):
assert type(results[0]) is Request assert type(results[0]) is Request
def test_middleware_request_as_convenience(app):
results = []
@app.on_request
async def handler1(request):
results.append(request)
@app.route("/")
async def handler2(request):
return text("OK")
request, response = app.test_client.get("/")
assert response.text == "OK"
assert type(results[0]) is Request
def test_middleware_response(app): def test_middleware_response(app):
results = [] results = []
@ -54,6 +71,54 @@ def test_middleware_response(app):
assert isinstance(results[2], HTTPResponse) assert isinstance(results[2], HTTPResponse)
def test_middleware_response_as_convenience(app):
results = []
@app.on_request
async def process_request(request):
results.append(request)
@app.on_response
async def process_response(request, response):
results.append(request)
results.append(response)
@app.route("/")
async def handler(request):
return text("OK")
request, response = app.test_client.get("/")
assert response.text == "OK"
assert type(results[0]) is Request
assert type(results[1]) is Request
assert isinstance(results[2], HTTPResponse)
def test_middleware_response_as_convenience_called(app):
results = []
@app.on_request()
async def process_request(request):
results.append(request)
@app.on_response()
async def process_response(request, response):
results.append(request)
results.append(response)
@app.route("/")
async def handler(request):
return text("OK")
request, response = app.test_client.get("/")
assert response.text == "OK"
assert type(results[0]) is Request
assert type(results[1]) is Request
assert isinstance(results[2], HTTPResponse)
def test_middleware_response_exception(app): def test_middleware_response_exception(app):
result = {"status_code": "middleware not run"} result = {"status_code": "middleware not run"}

View File

@ -633,6 +633,19 @@ def test_websocket_route(app, url):
assert ev.is_set() assert ev.is_set()
def test_websocket_route_invalid_handler(app):
with pytest.raises(ValueError) as e:
@app.websocket("/")
async def handler():
...
assert e.match(
r"Required parameter `request` and/or `ws` missing in the "
r"handler\(\) route\?"
)
@pytest.mark.asyncio @pytest.mark.asyncio
@pytest.mark.parametrize("url", ["/ws", "ws"]) @pytest.mark.parametrize("url", ["/ws", "ws"])
async def test_websocket_route_asgi(app, url): async def test_websocket_route_asgi(app, url):

View File

@ -8,6 +8,8 @@ import pytest
from sanic_testing.testing import HOST, PORT from sanic_testing.testing import HOST, PORT
from sanic.exceptions import InvalidUsage
AVAILABLE_LISTENERS = [ AVAILABLE_LISTENERS = [
"before_server_start", "before_server_start",
@ -80,6 +82,18 @@ def test_all_listeners(app):
assert app.name + listener_name == output.pop() assert app.name + listener_name == output.pop()
@skipif_no_alarm
def test_all_listeners_as_convenience(app):
output = []
for listener_name in AVAILABLE_LISTENERS:
listener = create_listener(listener_name, output)
method = getattr(app, listener_name)
method(listener)
start_stop_app(app)
for listener_name in AVAILABLE_LISTENERS:
assert app.name + listener_name == output.pop()
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_trigger_before_events_create_server(app): async def test_trigger_before_events_create_server(app):
class MySanicDb: class MySanicDb:
@ -95,6 +109,20 @@ async def test_trigger_before_events_create_server(app):
assert isinstance(app.db, MySanicDb) assert isinstance(app.db, MySanicDb)
@pytest.mark.asyncio
async def test_trigger_before_events_create_server_missing_event(app):
class MySanicDb:
pass
with pytest.raises(InvalidUsage):
@app.listener
async def init_db(app, loop):
app.db = MySanicDb()
assert not hasattr(app, "db")
def test_create_server_trigger_events(app): def test_create_server_trigger_events(app):
"""Test if create_server can trigger server events""" """Test if create_server can trigger server events"""

View File

@ -127,6 +127,40 @@ def test_static_file_content_type(app, static_file_directory, file_name):
assert response.headers["Content-Type"] == "text/html; charset=utf-8" assert response.headers["Content-Type"] == "text/html; charset=utf-8"
@pytest.mark.parametrize(
"file_name,expected",
[
("test.html", "text/html; charset=utf-8"),
("decode me.txt", "text/plain; charset=utf-8"),
("test.file", "application/octet-stream"),
],
)
def test_static_file_content_type_guessed(
app, static_file_directory, file_name, expected
):
app.static(
"/testing.file",
get_file_path(static_file_directory, file_name),
)
request, response = app.test_client.get("/testing.file")
assert response.status == 200
assert response.body == get_file_content(static_file_directory, file_name)
assert response.headers["Content-Type"] == expected
def test_static_file_content_type_with_charset(app, static_file_directory):
app.static(
"/testing.file",
get_file_path(static_file_directory, "decode me.txt"),
content_type="text/plain;charset=ISO-8859-1",
)
request, response = app.test_client.get("/testing.file")
assert response.status == 200
assert response.headers["Content-Type"] == "text/plain;charset=ISO-8859-1"
@pytest.mark.parametrize( @pytest.mark.parametrize(
"file_name", ["test.file", "decode me.txt", "symlink", "hard_link"] "file_name", ["test.file", "decode me.txt", "symlink", "hard_link"]
) )