Compare commits

...

205 Commits

Author SHA1 Message Date
Adam Hopkins
44bf7ba79a Cleanup tests 2023-08-21 22:54:04 +03:00
Adam Hopkins
9e7ca10c52 Cleanup tests 2023-08-21 21:37:45 +03:00
Adam Hopkins
fe32f4eb74 Fix border character 2023-08-21 20:10:47 +03:00
Adam Hopkins
ebe29d3d26 Make pretty 2023-08-21 16:40:22 +03:00
Adam Hopkins
f651f7436f Fix MOTD for extra data 2023-08-21 16:37:55 +03:00
Adam Hopkins
16256522f6 Disable Test PyPI dist 2023-07-25 16:13:47 +03:00
Adam Hopkins
205795d1e8 Prepare for v23.6 release (#2797) 2023-07-25 15:57:29 +03:00
Adam Hopkins
9cbe1fb8ad Add convenience method for exception reporting (#2792) 2023-07-18 00:21:55 +03:00
L. Kärkkäinen
31d7ba8f8c Add request.client_ip (#2790)
Co-authored-by: L. Kärkkäinen <Tronic@users.noreply.github.com>
2023-07-13 23:01:02 +03:00
Adam Hopkins
dc3c4d1393 Add custom typing to config and ctx (#2785) 2023-07-12 23:47:58 +03:00
Adam Hopkins
929d270569 Update bug-report.yml (#2788) 2023-07-12 19:00:28 +03:00
Adam Hopkins
93714df051 Update bug-report.yml (#2787) 2023-07-12 18:53:14 +03:00
L. Kärkkäinen
6e61eab872 Increase KEEP_ALIVE_TIMEOUT default to 120 seconds (#2670)
Co-authored-by: L. Kärkkäinen <Tronic@users.noreply.github.com>
2023-07-12 08:45:30 +03:00
Adam Hopkins
6848ff24d8 Run keep alive tests in loop to get available port (#2779) 2023-07-09 22:58:17 +03:00
Adam Hopkins
666371bb92 Set multiprocessing start method early (#2776) 2023-07-09 22:34:15 +03:00
Adam Hopkins
4a2b82e42e Remove Python3.7 support (#2777) 2023-07-09 22:00:14 +03:00
Moshe Nahmias
5dd1623192 Alow Blueprint routes to explicitly define error_format (#2773)
Co-authored-by: Adam Hopkins <adam@amhopkins.com>
2023-07-09 14:47:59 +03:00
Adam Hopkins
976da69e79 Add a new exception signal for ALL exceptions raised anywhere in application (#2724) 2023-07-09 10:53:36 +03:00
Liam Coatman
11a0b15194 Handle case when headers argument of ResponseStream constructor is None (#2729)
* Handle case when headers is None

* Add test for response stream with default headers

* Move test

---------

Co-authored-by: Adam Hopkins <adam@amhopkins.com>
2023-07-09 10:34:40 +03:00
Adam Hopkins
c21999a248 Resolve headers on different renderers - Issue 2749 (#2774)
* Resolve headers on different renderers - Issue 2749

* Make pretty
2023-07-09 09:57:22 +03:00
guacs
c17230ef94 Update request type on middleware types (#2754)
Co-authored-by: Adam Hopkins <adam@amhopkins.com>
2023-07-09 09:35:24 +03:00
Adam Hopkins
049983cb70 Fix traversals for intended results (#2728) 2023-07-09 09:21:39 +03:00
Zhiwei
e374409567 Adding allow route overwrite option in blueprint (#2716)
* Adding allow route overwrite option

* Add test case for route overwriting after bp copy

* Fix test

* Fix

* Add test case `test_bp_allow_override`

* Remove conflicted future routes when overwriting is allowed

* Improved test test_bp_copy_with_route_overwriting

* Fix type

* Fix type 2

* Add `test_bp_copy_without_route_overwriting` case

* make `allow_route_overwrite` flag to be internal

* Remove unwanted test case

---------

Co-authored-by: Adam Hopkins <adam@amhopkins.com>
2023-07-07 14:56:42 +03:00
Adam Hopkins
4068a0d83d Add name prefixing to BP groups (#2727) 2023-07-05 19:31:25 +03:00
Benjamin
70da5e9879 Fix Inner bug: TypeError: __init__() got an unexpected keyword argument 'escape_forward_slashes' #2740 (#2772) 2023-07-05 15:30:38 +03:00
Moshe Nahmias
f48506d620 fix #2757 - Improved error messaging on startup time application induced import error (#2770)
Co-authored-by: Adam Hopkins <adam@amhopkins.com>
2023-07-05 14:38:15 +03:00
Mohammad Almoghrabi
f2cc83c1ba fix examples for freeze_support() issue on windows (#2741)
Co-authored-by: Adam Hopkins <adam@amhopkins.com>
2023-07-05 13:45:08 +03:00
Mohammad Almoghrabi
273825dab6 Sanic on pypy (#2682)
Co-authored-by: L. Kärkkäinen <98187+Tronic@users.noreply.github.com>
Co-authored-by: Adam Hopkins <admhpkns@gmail.com>
Co-authored-by: Adam Hopkins <adam@amhopkins.com>
2023-07-05 12:14:47 +03:00
Zhiwei
9a7dafd531 Unpin setuptools version (#2766) 2023-07-05 11:06:43 +03:00
Thirumalaisamy K
50117d174c Fix issue in getting current request through classmethod when served through a different ASGI server (#2760) 2023-06-14 22:03:43 +03:00
Néstor Pérez
af67801062 Fix JSONResponse default content type (#2737) 2023-04-09 22:23:21 +03:00
Adam Hopkins
6eaab2a7e5 Release 23.3 (#2723) 2023-03-26 22:54:28 +03:00
Adam Hopkins
d680af3709 v23.3 Deprecation Removal (#2717) 2023-03-26 15:24:08 +03:00
Adam Hopkins
a8c2d77c91 Sync state tolerance (#2725) 2023-03-26 08:39:59 +03:00
Adam Hopkins
6e1c787e5d Simpler CLI targets (#2700)
Co-authored-by: L. Kärkkäinen <98187+Tronic@users.noreply.github.com>
2023-03-21 20:50:25 +02:00
L. Kärkkäinen
932088e37e Stricter charset handling and escaping of request URLs (#2710)
Co-authored-by: L. Karkkainen <tronic@users.noreply.github.com>
2023-03-21 19:55:21 +02:00
Adam Hopkins
1a63b9bec0 Add convenience methods for cookie creation and deletion (#2706)
* Add convenience methods for cookie creation and deletion

* Restore del

* Backwards compat, forward thinking

* Add delitem deprecation notice

* Add get full deprecation notice

* Add deprecation docstring

* Better deprecation docstring

* Add has_cookie

* Same defaults

* Better deprecation message

* Accessor annotations

* make pretty

* parse cookies

* Revert quote translator

* make pretty

* make pretty

* Add unit tests

* Make pretty

* Fix unit tests

* Directly include unquote

* Add some more unit tests

* Move modules into their own dir

* make pretty

* cleanup test imports

* Add test for cookie accessor

* Make test consistent

* Remove file

* Remove additional escaping

* Add header style getattr for hyphens

* Add test for cookie accessor with hyphens

* Add new translator

* Parametrize test_request_with_duplicate_cookie_key

* make pretty

* Add deprecation of direct cookie encoding

* Speedup Cookie creation

* Implement prefixes on delete_cookie

* typing changes

* Add passthru functions on response objects for setting cookies

* Add test for passthru

---------

Co-authored-by: L. Kärkkäinen <98187+Tronic@users.noreply.github.com>
2023-03-21 11:25:35 +02:00
Zhiwei
61aa16f6ac Decode headers as UTF-8 also in ASGI (#2606)
Co-authored-by: Adam Hopkins <adam@amhopkins.com>
2023-03-20 15:39:57 +02:00
Adam Hopkins
71cd53b64e Standardize init of exceptions (#2545) 2023-03-20 14:50:50 +02:00
Adam Hopkins
89188f5fc6 Add CertLoader as application option (#2722) 2023-03-20 14:05:21 +02:00
Adam Hopkins
a245ab3773 Change logging format to %s (#2721) 2023-03-20 13:25:06 +02:00
Adam Hopkins
ac1f56118a Skip middleware on RequestCancelled (#2720) 2023-03-20 13:00:34 +02:00
Adam Hopkins
53820bc241 Allow password to be passed to TLS context (#2719) 2023-03-20 12:34:36 +02:00
stricaud
009954003c Fix comparison from wrong formats with datetime objects (#2697)
Co-authored-by: Adam Hopkins <adam@amhopkins.com>
2023-03-20 12:12:09 +02:00
Adam Hopkins
8f265b8169 Add convenience for dynamic changes to routing (#2704) 2023-03-19 15:40:58 +02:00
Adam Hopkins
5ee36fd933 Separate ASGI request and lifespan callables (#2646) 2023-03-17 09:05:33 +02:00
Matt Bendiksen
08a81c81be scheme arg to create the redirect should be 'https' (#2712) 2023-03-16 12:13:17 +02:00
Adam Hopkins
5a0ed75171 Consume body on DELETE by default (#2711) 2023-03-15 18:42:45 +02:00
Adam Hopkins
d62a92fac9 API to define a number of workers (#2701) 2023-03-15 16:02:17 +02:00
Adam Hopkins
88c918e72f Update rfc.yml 2023-03-09 10:10:22 +02:00
Adam Hopkins
c8aab8fb3d Update rfc.yml 2023-03-09 10:09:30 +02:00
Adam Hopkins
ecacfd396b Rename rfc to rfc.yml 2023-03-09 10:06:42 +02:00
Adam Hopkins
3c361e9852 Create rfc 2023-03-09 10:05:06 +02:00
L. Kärkkäinen
a5d7d03413 Nicer traceback formatting (#2667)
Co-authored-by: L. Kärkkäinen <98187+Tronic@users.noreply.github.com>
Co-authored-by: Adam Hopkins <adam@amhopkins.com>
Co-authored-by: L. Karkkainen <tronic@users.noreply.github.com>
Co-authored-by: SML <smlbiobot@gmail.com>
2023-03-06 21:24:12 +02:00
L. Kärkkäinen
259e458847 Simplified parse_content_header escaping (#2707) 2023-03-06 06:39:16 +02:00
Adam Hopkins
cb49c2b26d Add header accessors (#2696) 2023-02-28 00:26:53 +02:00
L. Kärkkäinen
dfc0704831 Error page rendering format selection (#2668)
Co-authored-by: Adam Hopkins <adam@amhopkins.com>
Co-authored-by: L. Karkkainen <tronic@users.noreply.github.com>
2023-02-26 08:25:10 +02:00
Adam Hopkins
d238995f1b Refresh Request.accept functionality (#2687) 2023-02-21 08:22:51 +02:00
Mohammad Almoghrabi
6f5303e080 check the status of socket before shutting down (#2680)
* check the status of socket before shutting down

* remove socket status checking & ignore OSError exception
2023-02-14 22:59:41 +02:00
Ryu Juheon
5e7f6998bd fix(websocket): ASGI websocket must pass thru bytes as is (#2651) 2023-02-05 16:41:54 +02:00
L. Kärkkäinen
c7a71cd00c Use FALLBACK_ERROR_FORMAT for handlers that return empty() (#2659)
Co-authored-by: L. Karkkainen <tronic@users.noreply.github.com>
Co-authored-by: Adam Hopkins <adam@amhopkins.com>
2023-02-05 15:29:01 +02:00
Adam Hopkins
9cb9e88678 Establish basic file browser and index fallback (#2662)
Co-authored-by: L. Kärkkäinen <98187+Tronic@users.noreply.github.com>
Co-authored-by: L. Karkkainen <tronic@users.noreply.github.com>
2023-02-05 15:09:04 +02:00
Rodolfo Olivieri
30c53b6857 Remove deprecated property in blueprint (#2666)
Fixes https://github.com/sanic-org/sanic/issues/2442
2023-01-30 09:26:55 +02:00
Adam Hopkins
4ad8168bb0 Version 22.12 release notes (#2637) 2022-12-27 16:50:36 +02:00
Adam Hopkins
28f5b3c301 Add better inspector arg parsing (#2642) 2022-12-26 12:27:40 +02:00
Adam Hopkins
c573019e7f ASGI websocket recv text or bytes (#2640) 2022-12-25 13:52:07 +02:00
Adam Hopkins
029f564032 Pass unquote thru add_route (#2639) 2022-12-21 10:45:23 +02:00
Adam Hopkins
2abe66b670 Add priority to register_middleware method (#2636) 2022-12-19 19:14:46 +02:00
Adam Hopkins
911485d52e Fix Windows sock share (#2635) 2022-12-18 15:04:10 +02:00
Adam Hopkins
4744a89c33 Fix double ctrl-c kill (#2634) 2022-12-18 14:40:38 +02:00
Adam Hopkins
f7040ccec8 Implement restart ordering (#2632) 2022-12-18 14:09:17 +02:00
Adam Hopkins
518152d97e Reload interval on class variable (#2633) 2022-12-18 13:36:54 +02:00
Adam Hopkins
0e44e9cacb Move to HTTP Inspector (#2626) 2022-12-18 10:29:58 +02:00
Adam Hopkins
bfb54b0969 Test for 3.11 support (#2612)
Co-authored-by: Zhiwei <zhi.wei.liang@outlook.com>
2022-12-17 23:46:22 +02:00
Zhiwei
154863d6c6 Method Signal Handler Test (#2630) 2022-12-17 20:38:46 +02:00
Adam Hopkins
a3ff0c13b7 ASGI lifespan failure on exception (#2627) 2022-12-16 08:56:07 +02:00
Mary
95ee518aec Replace deprecated distutils.strtobool (#2628) 2022-12-16 07:48:41 +02:00
Zhiwei
71d3d87bcc Deprecate Conditions and Triggers Saved in handler Callable; Save Condition in signal.extra Instead (#2608) 2022-12-15 12:32:07 +02:00
Adam Hopkins
b276b91c21 Allow fork in limited cases (#2624) 2022-12-15 11:49:26 +02:00
Adam Hopkins
064168f3c8 Add a SIGKILL to second ctrl+c (#2621) 2022-12-14 23:51:11 +02:00
Adam Hopkins
db39e127bf Scale workers (#2617) 2022-12-13 09:28:23 +02:00
L. Kärkkäinen
13e9ab7ba9 Filename normalisation of form-data/multipart file uploads (umlauts on Apple clients) (#2625)
Co-authored-by: L. Karkkainen <tronic@users.noreply.github.com>
2022-12-13 08:36:21 +02:00
Adam Hopkins
92e7463721 Add a restart mechanism to all workers in the multiplexer (#2622) 2022-12-11 11:33:42 +02:00
Néstor Pérez
8e720365c2 Add JSONResponse class (#2569)
Co-authored-by: Adam Hopkins <adam@amhopkins.com>
2022-12-11 10:37:45 +02:00
Adam Hopkins
d4041161c7 Ensure middleware executes once per request timeout (#2615) 2022-12-07 23:07:17 +02:00
Adam Hopkins
f32437bf13 Kill server early on worker error (#2610) 2022-12-07 14:42:17 +02:00
LiraNuna
0909e94527 Corrected Colors enum under Python 3.11 (#2590)
Co-authored-by: Adam Hopkins <adam@amhopkins.com>
Fixes https://github.com/sanic-org/sanic/issues/2589
2022-11-29 12:17:48 +02:00
Adam Hopkins
aef2673c38 Force socket shutdown before close (#2607)
Co-authored-by: Zhiwei <zhi.wei.liang@outlook.com>
2022-11-29 12:04:22 +02:00
Aymeric Augustin
4c14910d5b Add compatibility with websockets 11.0. (#2609)
Co-authored-by: Adam Hopkins <adam@amhopkins.com>
2022-11-29 11:45:18 +02:00
Adam Hopkins
beae35f921 Ignore recent failures on bad TLS tests (#2611) 2022-11-29 10:51:51 +02:00
Zhiwei
ad4e526c77 Require uvloop >= 0.15.0 (#2598) 2022-11-13 15:32:04 +02:00
Adam Hopkins
4422d0c34d Mergeback from current-release 2022-10-31 13:24:47 +02:00
Adam Hopkins
ad9183d21d Merge branch 'main' of github.com:sanic-org/sanic into current-release 2022-10-31 13:22:47 +02:00
Adam Hopkins
d70636ba2e Add GenericCreator for loading SSL certs in processes (#2578) 2022-10-31 13:22:30 +02:00
Adam Hopkins
da23f85675 Set version 2022-10-31 13:20:17 +02:00
Adam Hopkins
3f4663b9f8 Resolve edge case in nested BP Groups (#2592) 2022-10-31 12:58:41 +02:00
Adam Hopkins
65d7447cf6 Add interval sleep in reloader (#2595) 2022-10-31 12:34:01 +02:00
Adam Hopkins
5369291c27 22.9 Docs (#2556) 2022-10-31 11:47:23 +02:00
Ryu Juheon
1c4925edf7 fix: sideeffects created by changing fork to spawn (#2591) 2022-10-27 20:39:17 +03:00
Santi Cardozo
6b9edfd05c improve error message if no apps found in registry (#2585) 2022-10-25 16:54:44 +03:00
Adam Hopkins
97f33f42df Update SECURITY.md 2022-10-25 13:05:13 +03:00
Adam Hopkins
15a588a90c Upgrade markdown templates to issue forms (#2588) 2022-10-25 13:04:11 +03:00
Ryu Juheon
82421e7efc docs: sanic now supports windows. (#2582) 2022-10-21 14:31:22 +03:00
Adam Hopkins
f891995b48 Start v22.12 2022-09-29 13:04:46 +03:00
Adam Hopkins
5052321801 Remove deprecated items (#2555) 2022-09-29 01:07:09 +03:00
Adam Hopkins
23ce4eaaa4 Merge branch 'main' of github.com:sanic-org/sanic 2022-09-23 00:16:27 +03:00
Adam Hopkins
23a430c4ad Set version properly 2022-09-23 00:16:10 +03:00
Adam Hopkins
ec158ffa69 Additional logger and support for multiprocess manager (#2551) 2022-09-23 00:01:33 +03:00
Adam Hopkins
6e32270036 Begin middleware revamp (#2550) 2022-09-22 00:43:42 +03:00
Zhiwei
43ba381e7b Refactor _static_request_handler (#2533)
Co-authored-by: Adam Hopkins <adam@amhopkins.com>
2022-09-21 00:45:03 +03:00
Zhiwei
16503319e5 Make WebsocketImplProtocol async iterable (#2490) 2022-09-21 00:20:32 +03:00
Adam Hopkins
389363ab71 Better request cancel handling (#2513) 2022-09-19 16:04:09 +03:00
Adam Hopkins
7f894c45b3 Add deprecation warning filter (#2546) 2022-09-18 18:54:35 +03:00
Adam Hopkins
4726cf1910 Sanic Server WorkerManager refactor (#2499)
Co-authored-by: Néstor Pérez <25409753+prryplatypus@users.noreply.github.com>
2022-09-18 17:17:23 +03:00
Adam Hopkins
d352a4155e Add signals before and after handler execution (#2540) 2022-09-15 15:49:21 +03:00
Adam Hopkins
e5010286b4 Raise warning and deprecation notice on violations (#2537) 2022-09-15 15:24:46 +03:00
Adam Hopkins
358498db96 Do not apply double slash to Blueprint and static dirs (#2515) 2022-09-15 14:43:20 +03:00
monosans
e4999401ab Improve and fix some type annotations (#2536) 2022-09-13 08:53:48 +03:00
Néstor Pérez
c8df0aa2cb Fix easter egg through CLI (#2542) 2022-09-12 01:44:21 +03:00
Adam Hopkins
5fb207176b Update bug_report.md 2022-08-29 12:47:01 +03:00
Adam Hopkins
a12b560478 Update feature_request.md 2022-08-29 12:46:39 +03:00
Zhiwei
753ee992a6 Validate File When Requested (#2526)
Co-authored-by: Adam Hopkins <adam@amhopkins.com>
2022-08-18 12:05:05 +03:00
Hunt Zhan
09089b1bd3 Resolve issue 2529 (#2530) 2022-08-18 08:58:07 +03:00
Adam Hopkins
7ddbe5e844 Update config.yml 2022-08-17 10:44:02 +03:00
Adam Hopkins
ab5a7038af Update SECURITY.md 2022-08-17 10:42:22 +03:00
Adam Hopkins
4f3c780dc3 Update feature_request.md 2022-08-17 10:38:15 +03:00
Adam Hopkins
71f7765a4c Update bug_report.md 2022-08-17 10:37:35 +03:00
Adam Hopkins
0392d1dcfc Always show server location in ASGI (#2522)
Co-authored-by: Adam Hopkins <adam@amhopkins.com>
Co-authored-by: Zhiwei Liang <zhi.wei.liang@outlook.com>
Co-authored-by: Néstor Pérez <25409753+prryplatypus@users.noreply.github.com>
2022-08-11 10:00:35 +03:00
Adam Hopkins
7827b1b41d Add Request properties for HTTP method info (#2516) 2022-08-10 21:12:09 +03:00
Adam Hopkins
8e9342e188 Warn on duplicate route names (#2525) 2022-08-10 20:36:47 +03:00
Adam Hopkins
2f6f2bfa76 Rename code of conduct 2022-08-02 13:56:53 +03:00
Ryu Juheon
dee09d7fff style: add some type hints (#2517) 2022-08-02 08:47:59 +03:00
Adam Hopkins
9cf38a0a83 MERGEBACK (#2495) (#2512)
Co-authored-by: Adam Hopkins <adam@amhopkins.com>
Co-authored-by: Zhiwei Liang <zhi.wei.liang@outlook.com>
Co-authored-by: Néstor Pérez <25409753+prryplatypus@users.noreply.github.com>
2022-07-31 15:50:46 +03:00
Adam Hopkins
3def3d3569 Use path.parts instead of match (#2508) 2022-07-31 12:54:42 +03:00
Adam Hopkins
e100a14fd4 Use pathlib for path resolution (#2506) 2022-07-31 08:49:02 +03:00
Adam Hopkins
2fa28f1711 Fix dotted test 2022-07-28 10:07:30 +03:00
Néstor Pérez
9d415e4ec6 Prevent directory traversion with static files (#2495)
Co-authored-by: Adam Hopkins <adam@amhopkins.com>
Co-authored-by: Zhiwei Liang <zhi.wei.liang@outlook.com>
2022-07-28 09:45:45 +03:00
Tim Gates
312ab298fd docs: Fix a few typos (#2502)
Co-authored-by: Adam Hopkins <adam@amhopkins.com>
2022-07-24 22:47:39 +03:00
Zhiwei
2fc21ad576 Replace Unsupported Python Version Number from the Contributing Doc (#2505)
Co-authored-by: Adam Hopkins <adam@amhopkins.com>
2022-07-24 22:33:05 +03:00
Zhiwei
8f6c87c3d6 Fix Test Cases: test_http for Py3.9+, test_json_response_json for ujson 5.4.0+, and test_zero_downtime; Test Case Type Annotations (#2504) 2022-07-24 22:07:54 +03:00
Adam Hopkins
4429e76532 Add to changelog 2022-06-30 12:52:27 +03:00
Michael Azimov
e4be70bae8 Add custom loads function (#2445)
Co-authored-by: Zhiwei <chihwei.public@outlook.com>
Co-authored-by: Adam Hopkins <adam@amhopkins.com>
2022-06-29 23:39:21 +03:00
Adam Hopkins
13d5a44278 Release 22.6 (#2487) 2022-06-28 15:25:46 +03:00
Adam Hopkins
aba333bfb6 Improve API docs (#2488) 2022-06-28 10:53:03 +03:00
Adam Hopkins
b59da498cc HTTP/3 Support (#2378) 2022-06-27 11:19:26 +03:00
Zhiwei
70382f21ba Fix and improve file cache control header calculation (#2486) 2022-06-26 23:11:48 +03:00
Néstor Pérez
0e1bf89fad Add missing spaces in CLI error message (#2485) 2022-06-26 10:38:35 +03:00
Aidan Timson
6c48c8b3ba Fix for running in pythonw (#2448)
Co-authored-by: Adam Hopkins <adam@amhopkins.com>
2022-06-19 14:48:06 +03:00
Zhiwei
d1c5e8003b Fix test_cli and test_cookies (#2479) 2022-06-19 04:43:12 +03:00
Adam Hopkins
ce926a34f2 Add Request contextvars (#2475)
* Add Request contextvars

* Add missing contextvar setter

* Move location of context setter
2022-06-16 22:57:02 +03:00
Zhiwei
a744041e38 File Cache Control Headers Support (#2447)
Co-authored-by: Adam Hopkins <adam@amhopkins.com>
2022-06-16 16:24:39 +03:00
Mary
2f90a85df1 feat(type): extend (#2466)
Co-authored-by: Adam Hopkins <adam@amhopkins.com>
2022-06-16 15:38:13 +03:00
Adam Hopkins
a411bc06e3 Resolve typing of stacked route definitions (#2455) 2022-06-16 15:15:20 +03:00
Adam Hopkins
1668e1532f Move verbosity filtering to logger (#2453) 2022-06-16 12:35:49 +03:00
Vetési Zoltán
b87982769f Trigger http.lifecycle.request signal in ASGI mode (#2451)
Co-authored-by: Adam Hopkins <adam@amhopkins.com>
2022-06-16 11:55:50 +03:00
Ryu Juheon
65b53a5f3f style: add msg in `task.cancel` (#2416)
* style: add msg in ``task.cancel``

* style: apply isort

* fix: use else statement

* fix: use tuple

* fix: rollback for test

* fix: rollback like previous change

* fix: add ``=``

Co-authored-by: Adam Hopkins <adam@amhopkins.com>
2022-06-16 10:55:20 +03:00
Zhiwei
49789b7841 Clean Up Black and Isort Config (#2449)
Co-authored-by: Adam Hopkins <adam@amhopkins.com>
2022-05-26 12:48:32 +03:00
Amitay
c249004c30 fixed manual to match current Sanic app name policy (#2461)
Co-authored-by: Adam Hopkins <adam@amhopkins.com>
2022-05-26 10:16:24 +03:00
Ashley Sommer
4ee2e57ec8 Properly catch websocket CancelledError in websocket handler in Python 3.7 (#2463) 2022-05-23 22:47:05 +03:00
Néstor Pérez
86ae5f981c refactor: consistent exception naming (#2420)
Co-authored-by: Adam Hopkins <adam@amhopkins.com>
2022-05-12 20:39:35 +03:00
Adam Hopkins
2bfa65e0de Current release mergeback (#2454) 2022-05-11 09:37:33 +03:00
Adam Hopkins
293278bb08 Resolve warning issue with error handler mismatch warning (#2452) 2022-05-11 09:36:05 +03:00
Michael Azimov
5d683c6ea4 Expose scope parameter in request object (#2432)
Co-authored-by: Adam Hopkins <adam@amhopkins.com>
2022-04-26 17:25:29 +03:00
Stephen Sadowski
78b6723149 Preserve blank form values for urlencoded forms (option) (#2439)
* task(request.form): Add tests for blank values

* fix(request): abstract form property to implement get_form(), allow for preserving of blanks

* fix(request): hinting for parsed_form

* fix(request): typing for parsed_files

* fix(request): ignore type assumption

* fix(request): mypy typechecking caused E501 when type set to ignore

* fix(request): mypy is too stupid to parse continuations

* fix(request): formatting

* fix(request): fix annotation and return for get_form()

* fix(request): linting, hinting
2022-04-24 23:01:35 +03:00
Ryu juheon
3a6cc7389c feat: easier websocket interface annotation (#2438) 2022-04-24 13:32:13 +03:00
Adam Hopkins
cc97287f8e Add fall back for Windows even loop fetching (#2421) 2022-04-17 12:25:41 +03:00
Adam Hopkins
00218aa9f2 22.3 Internal version bumps (#2419) 2022-03-31 14:30:30 +03:00
Adam Hopkins
874718db94 Bump version and 22.3 changelog (#2418) 2022-03-30 15:09:45 +03:00
Javier Marcet
bb4474897f Fix "DeprecationWarning: There is no current event loop" (#2390)
Co-authored-by: Adam Hopkins <adam@amhopkins.com>
2022-03-30 09:40:30 +03:00
Adam Hopkins
0cb342aef4 Better exception for bad URL parse (#2415) 2022-03-25 00:22:12 +02:00
Ashley Sommer
030987480c Add config option to skip Touchup step, for debugging purposes (#2361)
Co-authored-by: Adam Hopkins <adam@amhopkins.com>
2022-03-24 13:52:05 +02:00
Robert Schütz
f6fdc80b40 allow multidict version 6 (#2396)
Co-authored-by: Adam Hopkins <adam@amhopkins.com>
2022-03-24 00:38:45 +02:00
Jonathan Vargas
361c242473 remove error_logger on websockets (#2373)
Co-authored-by: Adam Hopkins <adam@amhopkins.com>
2022-03-23 16:25:19 +02:00
André Ericson
32962d1e1c Fixing typing for ListenerMixin.listener (#2376)
Co-authored-by: Adam Hopkins <adam@amhopkins.com>
2022-03-23 15:34:33 +02:00
Adam Hopkins
6e0a6871b5 Upgrade tests for sanic-routing changes (#2405) 2022-03-23 13:43:36 +02:00
Adam Hopkins
0030425c8c Conditionally inject CLI arguments into factory (#2402) 2022-03-23 12:00:41 +02:00
Adam Hopkins
c9dbc8ed26 Remove loop as required listener arg (#2414) 2022-03-23 11:02:39 +02:00
Callum
44b108b564 Changes to CLI (#2401)
Co-authored-by: Callum Fleming <howzitcal@zohomail.com>
Co-authored-by: Adam Hopkins <adam@amhopkins.com>
2022-03-23 10:30:41 +02:00
Adam Hopkins
2a8e91052f Add two new events on the reloader process (#2413) 2022-03-22 23:29:39 +02:00
Bluenix
0c9df02e66 Add a docstring to Request.respond() (#2409)
Co-authored-by: Ryu juheon <saidbysolo@gmail.com>
Co-authored-by: Adam Hopkins <adam@amhopkins.com>
2022-03-14 13:10:49 +02:00
Arie Bovenberg
7523e87937 remove overlapping slots from app.Sanic, fix broken slots inherit of HTTPResponse (#2387) 2022-02-24 17:45:23 +02:00
Bluenix
d4fb44e986 Document middleware on_request and on_response (#2398) 2022-02-13 21:08:08 +02:00
Ryu juheon
68b654d981 fix(tasks): newly assigned `None` in registry (#2381) 2022-02-08 08:33:09 +02:00
Adam Hopkins
88bc6d8966 Upgrade black and isort changes (#2397) 2022-02-02 10:41:55 +02:00
Adam Hopkins
ac388d644b Downgrade warnings to backwater debug messages (#2382) 2022-01-19 14:26:45 +02:00
Ryu juheon
bb517ddcca fix: deprecation warning in `asyncio.wait` (#2383) 2022-01-19 08:09:17 +02:00
Adam Hopkins
b8d991420b Sanic multi-application server (#2347) 2022-01-16 09:03:04 +02:00
Adam Hopkins
4a416e177a Updates to CLI help messaging (#2372) 2022-01-14 00:54:51 +02:00
Adam Hopkins
8dfa49b648 22.3 Deprecations and changes (#2362) 2022-01-12 16:28:43 +02:00
Adam Hopkins
8b0eaa097c Change back to codecov (#2363) 2022-01-09 12:22:09 +02:00
Sergey Rybakov
101151b419 Add credentials property to Request objects (#2357) 2022-01-06 19:14:52 +02:00
Adam Hopkins
4669036f45 Mergeback of 21.12.1 (#2358)
Co-authored-by: Néstor Pérez <25409753+prryplatypus@users.noreply.github.com>
Co-authored-by: Ryu juheon <saidbysolo@gmail.com>
2022-01-06 12:40:52 +02:00
raphaelauv
9bf9067c99 [FIX] README ASGI link (#2350) 2022-01-04 06:39:59 +02:00
Adam Hopkins
a7bc8b56ba Set dev version 2021-12-26 22:19:39 +02:00
Zhiwei
371985d129 deprecation warning for not catch lowercase env var (#2344) 2021-12-26 21:47:15 +02:00
Adam Hopkins
3eae00898d Set setuptools version for RTD 2021-12-26 14:25:09 +02:00
Adam Hopkins
dc3ccba527 Auto extend with Sanic Extensions (#2308) 2021-12-25 22:20:06 +02:00
Adam Hopkins
b91ffed010 Change signal routing for increased consistency (#2277) 2021-12-24 01:27:54 +02:00
Adam Hopkins
8c07e388cd LTS v21.12 Deprecations (#2306)
Co-authored-by: Néstor Pérez <25409753+prryplatypus@users.noreply.github.com>
2021-12-24 00:30:27 +02:00
Néstor Pérez
98ce4bdeb2 Optional uvloop use (#2264)
Co-authored-by: Adam Hopkins <adam@amhopkins.com>
Co-authored-by: Adam Hopkins <admhpkns@gmail.com>
2021-12-23 11:57:33 +02:00
Adam Hopkins
4659069350 Add route context (#2302) 2021-12-21 22:56:12 +02:00
Adam Hopkins
080d41627a Env custom type casting (#2330) 2021-12-21 00:50:45 +02:00
Adam Hopkins
d799c5f03c Add named tasks (#2304) 2021-12-20 23:50:04 +02:00
Adam Hopkins
abe062b371 Remove app instance from Config for error handler setting (#2320) 2021-12-18 18:58:14 +02:00
Zhiwei
b5a00ac1ca Add Py310 Tests Badge to README (#2341) 2021-12-15 13:09:13 +02:00
296 changed files with 20603 additions and 6226 deletions

View File

@@ -1,2 +0,0 @@
[tool.black]
line-length = 79

View File

@@ -1,28 +0,0 @@
exclude_patterns:
- "sanic/__main__.py"
- "sanic/application/logo.py"
- "sanic/application/motd.py"
- "sanic/reloader_helpers.py"
- "sanic/simple.py"
- "sanic/utils.py"
- ".github/"
- "changelogs/"
- "docker/"
- "docs/"
- "examples/"
- "scripts/"
- "tests/"
checks:
argument-count:
enabled: false
file-lines:
config:
threshold: 1000
method-count:
config:
threshold: 40
complex-logic:
enabled: false
method-complexity:
config:
threshold: 10

View File

@@ -3,13 +3,13 @@ branch = True
source = sanic source = sanic
omit = omit =
site-packages site-packages
sanic/application/logo.py
sanic/application/motd.py
sanic/cli
sanic/__main__.py sanic/__main__.py
sanic/reloader_helpers.py sanic/server/legacy.py
sanic/compat.py
sanic/simple.py sanic/simple.py
sanic/utils.py sanic/utils.py
sanic/cli
sanic/pages
[html] [html]
directory = coverage directory = coverage
@@ -21,3 +21,5 @@ exclude_lines =
noqa noqa
NOQA NOQA
pragma: no cover pragma: no cover
TYPE_CHECKING
skip_empty = True

78
.github/ISSUE_TEMPLATE/bug-report.yml vendored Normal file
View File

@@ -0,0 +1,78 @@
name: 🐞 Bug report
description: Create a report to help us improve
labels: ["bug", "triage"]
body:
- type: checkboxes
id: existing
attributes:
label: Is there an existing issue for this?
description: Please search to see if an issue already exists for the bug you encountered.
options:
- label: I have searched the existing issues
required: true
- type: textarea
id: description
attributes:
label: Describe the bug
description: A clear and concise description of what the bug is, make sure to paste any exceptions and tracebacks using markdown code-block syntax to make it easier to read.
validations:
required: true
- type: textarea
id: code
attributes:
label: Code snippet
description: |
Relevant source code, make sure to remove what is not necessary. Please try and format your code so that it is easier to read. For example:
```python
from sanic import Sanic
app = Sanic("Example")
```
validations:
required: false
- type: textarea
id: expected
attributes:
label: Expected Behavior
description: A concise description of what you expected to happen.
validations:
required: false
- type: dropdown
id: running
attributes:
label: How do you run Sanic?
options:
- Sanic CLI
- As a module
- As a script (`app.run` or `Sanic.serve`)
- ASGI
validations:
required: true
- type: dropdown
id: os
attributes:
label: Operating System
description: What OS?
options:
- Linux
- MacOS
- Windows
- Other (tell us in the description)
validations:
required: true
- type: input
id: version
attributes:
label: Sanic Version
description: Check startup logs or try `sanic --version`
validations:
required: true
- type: textarea
id: additional
attributes:
label: Additional context
description: Add any other context about the problem here.
validations:
required: false

View File

@@ -1,25 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
---
**Describe the bug**
A clear and concise description of what the bug is, make sure to paste any exceptions and tracebacks.
**Code snippet**
Relevant source code, make sure to remove what is not necessary.
**Expected behavior**
A clear and concise description of what you expected to happen.
**Environment (please complete the following information):**
- OS: [e.g. iOS]
- Version [e.g. 0.8.3]
**Additional context**
Add any other context about the problem here.

View File

@@ -1,5 +1,8 @@
blank_issues_enabled: true blank_issues_enabled: false
contact_links: contact_links:
- name: Questions and Help - name: Questions and Help
url: https://community.sanicframework.org/c/questions-and-help url: https://community.sanicframework.org/c/questions-and-help
about: Do you need help with Sanic? Ask your questions here. about: Do you need help with Sanic? Ask your questions here.
- name: Discussion and Support
url: https://discord.gg/FARQzAEMAA
about: For live discussion and support, checkout the Sanic Discord server.

View File

@@ -0,0 +1,34 @@
name: 🌟 Feature request
description: Suggest an enhancement for Sanic
labels: ["feature request"]
body:
- type: checkboxes
id: existing
attributes:
label: Is there an existing issue for this?
description: Please search to see if an issue already exists for the enhancement you are proposing.
options:
- label: I have searched the existing issues
required: true
- type: textarea
id: description
attributes:
label: Is your feature request related to a problem? Please describe.
description: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
validations:
required: false
- type: textarea
id: code
attributes:
label: Describe the solution you'd like
description: A clear and concise description of what you want to happen.
validations:
required: true
- type: textarea
id: additional
attributes:
label: Additional context
description: Add any other context about the problem here.
validations:
required: false

View File

@@ -1,16 +0,0 @@
---
name: Feature request
about: Suggest an idea for Sanic
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Additional context**
Add any other context or sample code about the feature request here.

33
.github/ISSUE_TEMPLATE/rfc.yml vendored Normal file
View File

@@ -0,0 +1,33 @@
name: 💡 Request for Comments
description: Open an RFC for discussion
labels: ["RFC"]
body:
- type: input
id: compare
attributes:
label: Link to code
description: If available, share a [comparison](https://github.com/sanic-org/sanic/compare) from a POC branch to main
placeholder: https://github.com/sanic-org/sanic/compare/main...some-new-branch
validations:
required: false
- type: textarea
id: proposal
attributes:
label: Proposal
description: A thorough discussion of the proposal discussing the problem it solves, potential code, use cases, and impacts
validations:
required: true
- type: textarea
id: additional
attributes:
label: Additional context
description: Add any other context that is relevant
validations:
required: false
- type: checkboxes
id: breaking
attributes:
label: Is this a breaking change?
options:
- label: "Yes"
required: false

View File

@@ -2,9 +2,15 @@ name: "CodeQL"
on: on:
push: push:
branches: [ main ] branches:
- main
- current-release
- "*LTS"
pull_request: pull_request:
branches: [ main ] branches:
- main
- current-release
- "*LTS"
types: [opened, synchronize, reopened, ready_for_review] types: [opened, synchronize, reopened, ready_for_review]
schedule: schedule:
- cron: '25 16 * * 0' - cron: '25 16 * * 0'

View File

@@ -3,13 +3,17 @@ on:
push: push:
branches: branches:
- main - main
- current-release
- "*LTS"
tags: tags:
- "!*" # Do not execute on tags - "!*" # Do not execute on tags
pull_request: pull_request:
types: [opened, synchronize, reopened, ready_for_review] branches:
- main
- current-release
- "*LTS"
jobs: jobs:
test: test:
if: github.event.pull_request.draft == false
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
strategy: strategy:
matrix: matrix:
@@ -19,7 +23,6 @@ jobs:
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: actions/setup-python@v1 - uses: actions/setup-python@v1
with: with:
python-version: ${{ matrix.python-version }} python-version: ${{ matrix.python-version }}
@@ -28,9 +31,10 @@ jobs:
run: | run: |
python -m pip install --upgrade pip python -m pip install --upgrade pip
pip install tox pip install tox
- uses: paambaati/codeclimate-action@v2.5.3 - name: Run coverage
if: always() run: tox -e coverage
env: continue-on-error: true
CC_TEST_REPORTER_ID: ${{ secrets.CODECLIMATE }} - uses: codecov/codecov-action@v2
with: with:
coverageCommand: tox -e coverage files: ./coverage.xml
fail_ci_if_error: false

View File

@@ -3,6 +3,8 @@ on:
pull_request: pull_request:
branches: branches:
- main - main
- current-release
- "*LTS"
types: [opened, synchronize, reopened, ready_for_review] types: [opened, synchronize, reopened, ready_for_review]
jobs: jobs:
@@ -15,10 +17,10 @@ jobs:
matrix: matrix:
os: [ubuntu-latest] os: [ubuntu-latest]
config: config:
- { python-version: 3.7, tox-env: security}
- { python-version: 3.8, tox-env: security} - { python-version: 3.8, tox-env: security}
- { python-version: 3.9, tox-env: security} - { python-version: 3.9, tox-env: security}
- { python-version: "3.10", tox-env: security} - { python-version: "3.10", tox-env: security}
- { python-version: "3.11", tox-env: security}
steps: steps:
- name: Checkout the repository - name: Checkout the repository
uses: actions/checkout@v2 uses: actions/checkout@v2

View File

@@ -3,6 +3,8 @@ on:
pull_request: pull_request:
branches: branches:
- main - main
- current-release
- "*LTS"
types: [opened, synchronize, reopened, ready_for_review] types: [opened, synchronize, reopened, ready_for_review]
jobs: jobs:
@@ -13,7 +15,7 @@ jobs:
strategy: strategy:
matrix: matrix:
config: config:
- {python-version: "3.8", tox-env: "docs"} - {python-version: "3.10", tox-env: "docs"}
fail-fast: false fail-fast: false

View File

@@ -3,6 +3,8 @@ on:
pull_request: pull_request:
branches: branches:
- main - main
- current-release
- "*LTS"
types: [opened, synchronize, reopened, ready_for_review] types: [opened, synchronize, reopened, ready_for_review]
jobs: jobs:
@@ -15,7 +17,7 @@ jobs:
matrix: matrix:
os: [ubuntu-latest] os: [ubuntu-latest]
config: config:
- { python-version: 3.8, tox-env: lint} - { python-version: "3.10", tox-env: lint}
steps: steps:
- name: Checkout the repository - name: Checkout the repository
uses: actions/checkout@v2 uses: actions/checkout@v2

View File

@@ -5,11 +5,11 @@ on:
tox-env: tox-env:
description: "Tox Env to run on the PyPy Infra" description: "Tox Env to run on the PyPy Infra"
required: false required: false
default: "pypy37" default: "pypy310"
pypy-version: pypy-version:
description: "Version of PyPy to use" description: "Version of PyPy to use"
required: false required: false
default: "pypy-3.7" default: "pypy-3.10"
jobs: jobs:
testPyPy: testPyPy:
name: ut-${{ matrix.config.tox-env }}-${{ matrix.os }} name: ut-${{ matrix.config.tox-env }}-${{ matrix.os }}

View File

@@ -3,6 +3,8 @@ on:
pull_request: pull_request:
branches: branches:
- main - main
- current-release
- "*LTS"
types: [opened, synchronize, reopened, ready_for_review] types: [opened, synchronize, reopened, ready_for_review]
jobs: jobs:

View File

@@ -1,23 +1,35 @@
name: Python 3.7 Tests name: Python 3.11 Tests
on: on:
pull_request: pull_request:
branches: branches:
- main - main
- current-release
- "*LTS"
types: [opened, synchronize, reopened, ready_for_review] types: [opened, synchronize, reopened, ready_for_review]
jobs: jobs:
testPy37: testPy311:
if: github.event.pull_request.draft == false if: github.event.pull_request.draft == false
name: ut-${{ matrix.config.tox-env }}-${{ matrix.os }} name: ut-${{ matrix.config.tox-env }}-${{ matrix.os }}
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
strategy: strategy:
fail-fast: true fail-fast: false
matrix: matrix:
# os: [ubuntu-latest, macos-latest] # os: [ubuntu-latest, macos-latest]
os: [ubuntu-latest] os: [ubuntu-latest]
config: config:
- { python-version: 3.7, tox-env: py37 } - {
- { python-version: 3.7, tox-env: py37-no-ext } python-version: "3.11",
tox-env: py311,
ignore-error-flake: "false",
command-timeout: "0",
}
- {
python-version: "3.11",
tox-env: py311-no-ext,
ignore-error-flake: "true",
command-timeout: "600000",
}
steps: steps:
- name: Checkout the Repository - name: Checkout the Repository
uses: actions/checkout@v2 uses: actions/checkout@v2
@@ -30,5 +42,7 @@ jobs:
test-infra-tool: tox test-infra-tool: tox
test-infra-version: latest test-infra-version: latest
action: tests action: tests
test-additional-args: "-e=${{ matrix.config.tox-env }}" test-additional-args: "-e=${{ matrix.config.tox-env }},-vv=''"
experimental-ignore-error: "${{ matrix.config.ignore-error-flake }}"
command-timeout: "${{ matrix.config.command-timeout }}"
test-failure-retry: "3" test-failure-retry: "3"

View File

@@ -3,6 +3,8 @@ on:
pull_request: pull_request:
branches: branches:
- main - main
- current-release
- "*LTS"
types: [opened, synchronize, reopened, ready_for_review] types: [opened, synchronize, reopened, ready_for_review]
jobs: jobs:

View File

@@ -3,6 +3,8 @@ on:
pull_request: pull_request:
branches: branches:
- main - main
- current-release
- "*LTS"
types: [opened, synchronize, reopened, ready_for_review] types: [opened, synchronize, reopened, ready_for_review]
jobs: jobs:

View File

@@ -3,6 +3,8 @@ on:
pull_request: pull_request:
branches: branches:
- main - main
- current-release
- "*LTS"
types: [opened, synchronize, reopened, ready_for_review] types: [opened, synchronize, reopened, ready_for_review]
jobs: jobs:
@@ -15,10 +17,10 @@ jobs:
matrix: matrix:
os: [ubuntu-latest] os: [ubuntu-latest]
config: config:
- { python-version: 3.7, tox-env: type-checking}
- { python-version: 3.8, tox-env: type-checking} - { python-version: 3.8, tox-env: type-checking}
- { python-version: 3.9, tox-env: type-checking} - { python-version: 3.9, tox-env: type-checking}
- { python-version: "3.10", tox-env: type-checking} - { python-version: "3.10", tox-env: type-checking}
- { python-version: "3.11", tox-env: type-checking}
steps: steps:
- name: Checkout the repository - name: Checkout the repository
uses: actions/checkout@v2 uses: actions/checkout@v2

View File

@@ -3,6 +3,8 @@ on:
pull_request: pull_request:
branches: branches:
- main - main
- current-release
- "*LTS"
types: [opened, synchronize, reopened, ready_for_review] types: [opened, synchronize, reopened, ready_for_review]
jobs: jobs:
@@ -14,11 +16,10 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
config: config:
- { python-version: 3.7, tox-env: py37-no-ext }
- { python-version: 3.8, tox-env: py38-no-ext } - { python-version: 3.8, tox-env: py38-no-ext }
- { python-version: 3.9, tox-env: py39-no-ext } - { python-version: 3.9, tox-env: py39-no-ext }
- { python-version: "3.10", tox-env: py310-no-ext } - { python-version: "3.10", tox-env: py310-no-ext }
- { python-version: pypy-3.7, tox-env: pypy37-no-ext } - { python-version: "3.11", tox-env: py310-no-ext }
steps: steps:
- name: Checkout Repository - name: Checkout Repository

View File

@@ -14,7 +14,7 @@ jobs:
strategy: strategy:
fail-fast: true fail-fast: true
matrix: matrix:
python-version: ["3.7", "3.8", "3.9", "3.10"] python-version: ["3.8", "3.9", "3.10", "3.11"]
steps: steps:
- name: Checkout repository - name: Checkout repository

View File

@@ -1,28 +1,39 @@
name: Publish Artifacts name: Upload Python Package
on: on:
release: release:
types: [created] types: [created]
workflow_dispatch:
jobs: jobs:
publishPythonPackage: build-n-publish:
name: Publishing Sanic Release Artifacts name: Build and publish Python 🐍 distributions 📦 to PyPI and TestPyPI
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy:
fail-fast: true
matrix:
python-version: ["3.8"]
steps: steps:
- name: Checkout Repository - uses: actions/checkout@v3
uses: actions/checkout@v2 - name: Set up Python
uses: actions/setup-python@v4
- name: Publish Python Package with:
uses: harshanarayana/custom-actions@main python-version: "3.x"
with: - name: Install pypa/build
python-version: ${{ matrix.python-version }} run: >-
package-infra-name: "twine" python3 -m
pypi-user: __token__ pip install
pypi-access-token: ${{ secrets.PYPI_ACCESS_TOKEN }} build
action: "package-publish" --user
pypi-verify-metadata: "true" - name: Build a binary wheel and a source tarball
run: >-
python3 -m
build
--sdist
--wheel
--outdir dist/
.
# - name: Publish distribution 📦 to Test PyPI
# uses: pypa/gh-action-pypi-publish@release/v1
# with:
# password: ${{ secrets.SANIC_TEST_PYPI_API_TOKEN }}
# repository-url: https://test.pypi.org/legacy/
- name: Publish distribution 📦 to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ secrets.SANIC_PYPI_API_TOKEN }}

1
.gitignore vendored
View File

@@ -21,4 +21,5 @@ dist/*
pip-wheel-metadata/ pip-wheel-metadata/
.pytest_cache/* .pytest_cache/*
.venv/* .venv/*
venv/*
.vscode/* .vscode/*

View File

@@ -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,19 +351,12 @@ 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>`_
Add disable app registry Add disable app registry
Version 20.12.0
---------------
Features
********
* *
`#1945 <https://github.com/sanic-org/sanic/pull/1945>`_ `#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
@@ -416,22 +393,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 +419,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 +437,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 +450,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 +461,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 +488,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 +515,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 +525,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 +535,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 +545,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 +559,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 +581,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 +602,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 +622,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 +653,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 +671,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 +689,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 +699,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 +747,7 @@ Improved Documentation
Version 19.12.0 Version 19.12.0
--------------- ---------------
Bugfixes **Bugfixes**
********
- Fix blueprint middleware application - Fix blueprint middleware application
@@ -814,8 +766,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 +780,7 @@ Improved Documentation
Version 19.6.3 Version 19.6.3
-------------- --------------
Features **Features**
********
- Enable Towncrier Support - Enable Towncrier Support
@@ -838,8 +788,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 +801,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 +817,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 +835,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 +859,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 +927,7 @@ Features
This is a breaking change. This is a breaking change.
Bugfixes **Bugfixes**
********
* *
@@ -1019,8 +963,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 +971,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 +1038,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 +1124,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 +1141,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

View File

@@ -55,7 +55,7 @@ further defined and clarified by project maintainers.
## Enforcement ## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at sanic-maintainers@googlegroups.com. All reported by contacting the project team at adam@sanicframework.org. All
complaints will be reviewed and investigated and will result in a response that complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident. obligated to maintain confidentiality with regard to the reporter of an incident.

View File

@@ -71,9 +71,9 @@ To execute only unittests, run ``tox`` with environment like so:
.. code-block:: bash .. code-block:: bash
tox -e py36 -v -- tests/test_config.py
# or
tox -e py37 -v -- tests/test_config.py tox -e py37 -v -- tests/test_config.py
# or
tox -e py310 -v -- tests/test_config.py
Run lint checks Run lint checks
--------------- ---------------
@@ -140,6 +140,7 @@ To maintain the code consistency, Sanic uses following tools.
#. `isort <https://github.com/timothycrosley/isort>`_ #. `isort <https://github.com/timothycrosley/isort>`_
#. `black <https://github.com/python/black>`_ #. `black <https://github.com/python/black>`_
#. `flake8 <https://github.com/PyCQA/flake8>`_ #. `flake8 <https://github.com/PyCQA/flake8>`_
#. `slotscheck <https://github.com/ariebovenberg/slotscheck>`_
isort isort
***** *****
@@ -167,7 +168,13 @@ flake8
#. pycodestyle #. pycodestyle
#. Ned Batchelder's McCabe script #. Ned Batchelder's McCabe script
``isort``\ , ``black`` and ``flake8`` checks are performed during ``tox`` lint checks. slotscheck
**********
``slotscheck`` ensures that there are no problems with ``__slots__``
(e.g. overlaps, or missing slots in base classes).
``isort``\ , ``black``\ , ``flake8`` and ``slotscheck`` checks are performed during ``tox`` lint checks.
The **easiest** way to make your code conform is to run the following before committing. The **easiest** way to make your code conform is to run the following before committing.

View File

@@ -66,15 +66,15 @@ ifdef include_tests
isort -rc sanic tests isort -rc sanic tests
else else
$(info Sorting Imports) $(info Sorting Imports)
isort -rc sanic tests --profile=black isort -rc sanic tests
endif endif
endif endif
black: black:
black --config ./.black.toml sanic tests black sanic tests
isort: isort:
isort sanic tests --profile=black isort sanic tests
pretty: black isort pretty: black isort

View File

@@ -11,7 +11,7 @@ Sanic | Build fast. Run fast.
:stub-columns: 1 :stub-columns: 1
* - Build * - Build
- | |Py39Test| |Py38Test| |Py37Test| - | |Py310Test| |Py39Test| |Py38Test| |Py37Test|
* - Docs * - Docs
- | |UserGuide| |Documentation| - | |UserGuide| |Documentation|
* - Package * - Package
@@ -27,6 +27,8 @@ Sanic | Build fast. Run fast.
:target: https://community.sanicframework.org/ :target: https://community.sanicframework.org/
.. |Discord| image:: https://img.shields.io/discord/812221182594121728?logo=discord .. |Discord| image:: https://img.shields.io/discord/812221182594121728?logo=discord
:target: https://discord.gg/FARQzAEMAA :target: https://discord.gg/FARQzAEMAA
.. |Py310Test| image:: https://github.com/sanic-org/sanic/actions/workflows/pr-python310.yml/badge.svg?branch=main
:target: https://github.com/sanic-org/sanic/actions/workflows/pr-python310.yml
.. |Py39Test| image:: https://github.com/sanic-org/sanic/actions/workflows/pr-python39.yml/badge.svg?branch=main .. |Py39Test| image:: https://github.com/sanic-org/sanic/actions/workflows/pr-python39.yml/badge.svg?branch=main
:target: https://github.com/sanic-org/sanic/actions/workflows/pr-python39.yml :target: https://github.com/sanic-org/sanic/actions/workflows/pr-python39.yml
.. |Py38Test| image:: https://github.com/sanic-org/sanic/actions/workflows/pr-python38.yml/badge.svg?branch=main .. |Py38Test| image:: https://github.com/sanic-org/sanic/actions/workflows/pr-python38.yml/badge.svg?branch=main
@@ -64,7 +66,7 @@ Sanic | Build fast. Run fast.
Sanic is a **Python 3.7+** web server and web framework that's written to go fast. It allows the usage of the ``async/await`` syntax added in Python 3.5, which makes your code non-blocking and speedy. Sanic is a **Python 3.7+** web server and web framework that's written to go fast. It allows the usage of the ``async/await`` syntax added in Python 3.5, which makes your code non-blocking and speedy.
Sanic is also ASGI compliant, so you can deploy it with an `alternative ASGI webserver <https://sanic.readthedocs.io/en/latest/sanic/deploying.html#running-via-asgi>`_. Sanic is also ASGI compliant, so you can deploy it with an `alternative ASGI webserver <https://sanicframework.org/en/guide/deployment/running.html#asgi>`_.
`Source code on GitHub <https://github.com/sanic-org/sanic/>`_ | `Help and discussion board <https://community.sanicframework.org/>`_ | `User Guide <https://sanicframework.org>`_ | `Chat on Discord <https://discord.gg/FARQzAEMAA>`_ `Source code on GitHub <https://github.com/sanic-org/sanic/>`_ | `Help and discussion board <https://community.sanicframework.org/>`_ | `User Guide <https://sanicframework.org>`_ | `Chat on Discord <https://discord.gg/FARQzAEMAA>`_
@@ -100,9 +102,6 @@ Installation
If you are running on a clean install of Fedora 28 or above, please make sure you have the ``redhat-rpm-config`` package installed in case if you want to If you are running on a clean install of Fedora 28 or above, please make sure you have the ``redhat-rpm-config`` package installed in case if you want to
use ``sanic`` with ``ujson`` dependency. use ``sanic`` with ``ujson`` dependency.
.. note::
Windows support is currently "experimental" and on a best-effort basis. Multiple workers are also not currently supported on Windows (see `Issue #1517 <https://github.com/sanic-org/sanic/issues/1517>`_), but setting ``workers=1`` should launch the server successfully.
Hello World Example Hello World Example
------------------- -------------------
@@ -112,7 +111,7 @@ Hello World Example
from sanic import Sanic from sanic import Sanic
from sanic.response import json from sanic.response import json
app = Sanic("My Hello, world app") app = Sanic("my-hello-world-app")
@app.route('/') @app.route('/')
async def test(request): async def test(request):

View File

@@ -4,31 +4,42 @@
Sanic releases long term support release once a year in December. LTS releases receive bug and security updates for **24 months**. Interim releases throughout the year occur every three months, and are supported until the subsequent interim release. Sanic releases long term support release once a year in December. LTS releases receive bug and security updates for **24 months**. Interim releases throughout the year occur every three months, and are supported until the subsequent interim release.
| Version | LTS | Supported |
| ------- | ------------- | ------------------ |
| 20.12 | until 2022-12 | :heavy_check_mark: |
| 20.9 | | :x: |
| 20.6 | | :x: |
| 20.3 | | :x: |
| 19.12 | until 2021-12 | :white_check_mark: |
| 19.9 | | :x: |
| 19.6 | | :x: |
| 19.3 | | :x: |
| 18.12 | | :x: |
| 0.8.3 | | :x: |
| 0.7.0 | | :x: |
| 0.6.0 | | :x: |
| 0.5.4 | | :x: |
| 0.4.1 | | :x: |
| 0.3.1 | | :x: |
| 0.2.0 | | :x: |
| 0.1.9 | | :x: |
:white_check_mark: = security/bug fixes | Version | LTS | Supported |
:heavy_check_mark: = full support | ------- | ------------- | ----------------------- |
| 22.12 | until 2024-12 | :white_check_mark: |
| 22.9 | | :x: |
| 22.6 | | :x: |
| 22.3 | | :x: |
| 21.12 | until 2023-12 | :ballot_box_with_check: |
| 21.9 | | :x: |
| 21.6 | | :x: |
| 21.3 | | :x: |
| 20.12 | | :x: |
| 20.9 | | :x: |
| 20.6 | | :x: |
| 20.3 | | :x: |
| 19.12 | | :x: |
| 19.9 | | :x: |
| 19.6 | | :x: |
| 19.3 | | :x: |
| 18.12 | | :x: |
| 0.8.3 | | :x: |
| 0.7.0 | | :x: |
| 0.6.0 | | :x: |
| 0.5.4 | | :x: |
| 0.4.1 | | :x: |
| 0.3.1 | | :x: |
| 0.2.0 | | :x: |
| 0.1.9 | | :x: |
:ballot_box_with_check: = security/bug fixes
:white_check_mark: = full support
## Reporting a Vulnerability ## Reporting a Vulnerability
If you discover a security vulnerability, we ask that you **do not** create an issue on GitHub. Instead, please [send a message to the core-devs](https://community.sanicframework.org/g/core-devs) on the community forums. Once logged in, you can send a message to the core-devs by clicking the message button. If you discover a security vulnerability, we ask that you **do not** create an issue on GitHub. Instead, please [send a message to the core-devs](https://community.sanicframework.org/g/core-devs) on the community forums. Once logged in, you can send a message to the core-devs by clicking the message button.
Alternatively, you can send a private message to Adam Hopkins on Discord. Find him on the [Sanic discord server](https://discord.gg/FARQzAEMAA).
This will help to not publicize the issue until the team can address it and resolve it. This will help to not publicize the issue until the team can address it and resolve it.

28
codecov.yml Normal file
View File

@@ -0,0 +1,28 @@
coverage:
status:
patch:
default:
target: auto
threshold: 0.75
informational: true
project:
default:
target: auto
threshold: 0.5
precision: 3
codecov:
require_ci_to_pass: false
ignore:
- "sanic/__main__.py"
- "sanic/compat.py"
- "sanic/simple.py"
- "sanic/utils.py"
- "sanic/cli/"
- "sanic/pages/"
- ".github/"
- "changelogs/"
- "docker/"
- "docs/"
- "examples/"
- "scripts/"
- "tests/"

View File

@@ -2,3 +2,12 @@
.wy-nav-top { .wy-nav-top {
background: #444444; background: #444444;
} }
#changelog section {
padding-left: 3rem;
}
#changelog section h2,
#changelog section h3 {
margin-left: -3rem;
}

View File

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

View File

@@ -9,7 +9,7 @@ API
=== ===
.. toctree:: .. toctree::
:maxdepth: 2 :maxdepth: 3
👥 User Guide <https://sanicframework.org/guide/> 👥 User Guide <https://sanicframework.org/guide/>
sanic/api_reference sanic/api_reference

View File

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

View File

@@ -17,6 +17,14 @@ sanic.handlers
:show-inheritance: :show-inheritance:
sanic.headers
--------------
.. automodule:: sanic.headers
:members:
:show-inheritance:
sanic.request sanic.request
------------- -------------
@@ -38,10 +46,3 @@ sanic.views
.. automodule:: sanic.views .. automodule:: sanic.views
:members: :members:
:show-inheritance: :show-inheritance:
sanic.websocket
---------------
.. automodule:: sanic.websocket
:members:
:show-inheritance:

View File

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

View File

@@ -1,6 +1,16 @@
📜 Changelog 📜 Changelog
============ ============
.. mdinclude:: ./releases/21.9.md | 🔶 Current release
| 🔷 In support release
|
.. mdinclude:: ./releases/23/23.6.md
.. mdinclude:: ./releases/23/23.3.md
.. mdinclude:: ./releases/22/22.12.md
.. mdinclude:: ./releases/22/22.9.md
.. mdinclude:: ./releases/22/22.6.md
.. mdinclude:: ./releases/22/22.3.md
.. mdinclude:: ./releases/21/21.12.md
.. mdinclude:: ./releases/21/21.9.md
.. include:: ../../CHANGELOG.rst .. include:: ../../CHANGELOG.rst

View File

@@ -0,0 +1,66 @@
## Version 21.12.1 🔷
_Current LTS version_
- [#2349](https://github.com/sanic-org/sanic/pull/2349) Only display MOTD on startup
- [#2354](https://github.com/sanic-org/sanic/pull/2354) Ignore name argument in Python 3.7
- [#2355](https://github.com/sanic-org/sanic/pull/2355) Add config.update support for all config values
## 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

View File

@@ -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

View File

@@ -0,0 +1,55 @@
## Version 22.12.0 🔷
_Current version_
### Features
- [#2569](https://github.com/sanic-org/sanic/pull/2569) Add `JSONResponse` class with some convenient methods when updating a response object
- [#2598](https://github.com/sanic-org/sanic/pull/2598) Change `uvloop` requirement to `>=0.15.0`
- [#2609](https://github.com/sanic-org/sanic/pull/2609) Add compatibility with `websockets` v11.0
- [#2610](https://github.com/sanic-org/sanic/pull/2610) Kill server early on worker error
- Raise deadlock timeout to 30s
- [#2617](https://github.com/sanic-org/sanic/pull/2617) Scale number of running server workers
- [#2621](https://github.com/sanic-org/sanic/pull/2621) [#2634](https://github.com/sanic-org/sanic/pull/2634) Send `SIGKILL` on subsequent `ctrl+c` to force worker exit
- [#2622](https://github.com/sanic-org/sanic/pull/2622) Add API to restart all workers from the multiplexer
- [#2624](https://github.com/sanic-org/sanic/pull/2624) Default to `spawn` for all subprocesses unless specifically set:
```python
from sanic import Sanic
Sanic.start_method = "fork"
```
- [#2625](https://github.com/sanic-org/sanic/pull/2625) Filename normalisation of form-data/multipart file uploads
- [#2626](https://github.com/sanic-org/sanic/pull/2626) Move to HTTP Inspector:
- Remote access to inspect running Sanic instances
- TLS support for encrypted calls to Inspector
- Authentication to Inspector with API key
- Ability to extend Inspector with custom commands
- [#2632](https://github.com/sanic-org/sanic/pull/2632) Control order of restart operations
- [#2633](https://github.com/sanic-org/sanic/pull/2633) Move reload interval to class variable
- [#2636](https://github.com/sanic-org/sanic/pull/2636) Add `priority` to `register_middleware` method
- [#2639](https://github.com/sanic-org/sanic/pull/2639) Add `unquote` to `add_route` method
- [#2640](https://github.com/sanic-org/sanic/pull/2640) ASGI websockets to receive `text` or `bytes`
### Bugfixes
- [#2607](https://github.com/sanic-org/sanic/pull/2607) Force socket shutdown before close to allow rebinding
- [#2590](https://github.com/sanic-org/sanic/pull/2590) Use actual `StrEnum` in Python 3.11+
- [#2615](https://github.com/sanic-org/sanic/pull/2615) Ensure middleware executes only once per request timeout
- [#2627](https://github.com/sanic-org/sanic/pull/2627) Crash ASGI application on lifespan failure
- [#2635](https://github.com/sanic-org/sanic/pull/2635) Resolve error with low-level server creation on Windows
### Deprecations and Removals
- [#2608](https://github.com/sanic-org/sanic/pull/2608) [#2630](https://github.com/sanic-org/sanic/pull/2630) Signal conditions and triggers saved on `signal.extra`
- [#2626](https://github.com/sanic-org/sanic/pull/2626) Move to HTTP Inspector
- 🚨 *BREAKING CHANGE*: Moves the Inspector to a Sanic app from a simple TCP socket with a custom protocol
- *DEPRECATE*: The `--inspect*` commands have been deprecated in favor of `inspect ...` commands
- [#2628](https://github.com/sanic-org/sanic/pull/2628) Replace deprecated `distutils.strtobool`
### Developer infrastructure
- [#2612](https://github.com/sanic-org/sanic/pull/2612) Add CI testing for Python 3.11

View File

@@ -0,0 +1,52 @@
## Version 22.3.0
### Features
- [#2347](https://github.com/sanic-org/sanic/pull/2347) API for multi-application server
- 🚨 *BREAKING CHANGE*: The old `sanic.worker.GunicornWorker` has been **removed**. To run Sanic with `gunicorn`, you should use it thru `uvicorn` [as described in their docs](https://www.uvicorn.org/#running-with-gunicorn).
- 🧁 *SIDE EFFECT*: Named background tasks are now supported, even in Python 3.7
- [#2357](https://github.com/sanic-org/sanic/pull/2357) Parse `Authorization` header as `Request.credentials`
- [#2361](https://github.com/sanic-org/sanic/pull/2361) Add config option to skip `Touchup` step in application startup
- [#2372](https://github.com/sanic-org/sanic/pull/2372) Updates to CLI help messaging
- [#2382](https://github.com/sanic-org/sanic/pull/2382) Downgrade warnings to backwater debug messages
- [#2396](https://github.com/sanic-org/sanic/pull/2396) Allow for `multidict` v0.6
- [#2401](https://github.com/sanic-org/sanic/pull/2401) Upgrade CLI catching for alternative application run types
- [#2402](https://github.com/sanic-org/sanic/pull/2402) Conditionally inject CLI arguments into factory
- [#2413](https://github.com/sanic-org/sanic/pull/2413) Add new start and stop event listeners to reloader process
- [#2414](https://github.com/sanic-org/sanic/pull/2414) Remove loop as required listener arg
- [#2415](https://github.com/sanic-org/sanic/pull/2415) Better exception for bad URL parsing
- [sanic-routing#47](https://github.com/sanic-org/sanic-routing/pull/47) Add a new extention parameter type: `<file:ext>`, `<file:ext=jpg>`, `<file:ext=jpg|png|gif|svg>`, `<file=int:ext>`, `<file=int:ext=jpg|png|gif|svg>`, `<file=float:ext=tar.gz>`
- 👶 *BETA FEATURE*: This feature will not work with `path` type matching, and is being released as a beta feature only.
- [sanic-routing#57](https://github.com/sanic-org/sanic-routing/pull/57) Change `register_pattern` to accept a `str` or `Pattern`
- [sanic-routing#58](https://github.com/sanic-org/sanic-routing/pull/58) Default matching on non-empty strings only, and new `strorempty` pattern type
- 🚨 *BREAKING CHANGE*: Previously a route with a dynamic string parameter (`/<foo>` or `/<foo:str>`) would match on any string, including empty strings. It will now **only** match a non-empty string. To retain the old behavior, you should use the new parameter type: `/<foo:strorempty>`.
### Bugfixes
- [#2373](https://github.com/sanic-org/sanic/pull/2373) Remove `error_logger` on websockets
- [#2381](https://github.com/sanic-org/sanic/pull/2381) Fix newly assigned `None` in task registry
- [sanic-routing#52](https://github.com/sanic-org/sanic-routing/pull/52) Add type casting to regex route matching
- [sanic-routing#60](https://github.com/sanic-org/sanic-routing/pull/60) Add requirements check on regex routes (this resolves, for example, multiple static directories with differing `host` values)
### Deprecations and Removals
- [#2362](https://github.com/sanic-org/sanic/pull/2362) 22.3 Deprecations and changes
1. `debug=True` and `--debug` do _NOT_ automatically run `auto_reload`
2. Default error render is with plain text (browsers still get HTML by default because `auto` looks at headers)
3. `config` is required for `ErrorHandler.finalize`
4. `ErrorHandler.lookup` requires two positional args
5. Unused websocket protocol args removed
- [#2344](https://github.com/sanic-org/sanic/pull/2344) Deprecate loading of lowercase environment variables
### Developer infrastructure
- [#2363](https://github.com/sanic-org/sanic/pull/2363) Revert code coverage back to Codecov
- [#2405](https://github.com/sanic-org/sanic/pull/2405) Upgrade tests for `sanic-routing` changes
- [sanic-testing#35](https://github.com/sanic-org/sanic-testing/pull/35) Allow for httpx v0.22
### Improved Documentation
- [#2350](https://github.com/sanic-org/sanic/pull/2350) Fix link in README for ASGI
- [#2398](https://github.com/sanic-org/sanic/pull/2398) Document middleware on_request and on_response
- [#2409](https://github.com/sanic-org/sanic/pull/2409) Add missing documentation for `Request.respond`
### Miscellaneous
- [#2376](https://github.com/sanic-org/sanic/pull/2376) Fix typing for `ListenerMixin.listener`
- [#2383](https://github.com/sanic-org/sanic/pull/2383) Clear deprecation warning in `asyncio.wait`
- [#2387](https://github.com/sanic-org/sanic/pull/2387) Cleanup `__slots__` implementations
- [#2390](https://github.com/sanic-org/sanic/pull/2390) Clear deprecation warning in `asyncio.get_event_loop`

View File

@@ -0,0 +1,54 @@
## Version 22.6.2
### Bugfixes
- [#2522](https://github.com/sanic-org/sanic/pull/2522) Always show server location in ASGI
## Version 22.6.1
### Bugfixes
- [#2477](https://github.com/sanic-org/sanic/pull/2477) Sanic static directory fails when folder name ends with ".."
## Version 22.6.0
### Features
- [#2378](https://github.com/sanic-org/sanic/pull/2378) Introduce HTTP/3 and autogeneration of TLS certificates in `DEBUG` mode
- 👶 *EARLY RELEASE FEATURE*: Serving Sanic over HTTP/3 is an early release feature. It does not yet fully cover the HTTP/3 spec, but instead aims for feature parity with Sanic's existing HTTP/1.1 server. Websockets, WebTransport, push responses are examples of some features not yet implemented.
- 📦 *EXTRA REQUIREMENT*: Not all HTTP clients are capable of interfacing with HTTP/3 servers. You may need to install a [HTTP/3 capable client](https://curl.se/docs/http3.html).
- 📦 *EXTRA REQUIREMENT*: In order to use TLS autogeneration, you must install either [mkcert](https://github.com/FiloSottile/mkcert) or [trustme](https://github.com/python-trio/trustme).
- [#2416](https://github.com/sanic-org/sanic/pull/2416) Add message to `task.cancel`
- [#2420](https://github.com/sanic-org/sanic/pull/2420) Add exception aliases for more consistent naming with standard HTTP response types (`BadRequest`, `MethodNotAllowed`, `RangeNotSatisfiable`)
- [#2432](https://github.com/sanic-org/sanic/pull/2432) Expose ASGI `scope` as a property on the `Request` object
- [#2438](https://github.com/sanic-org/sanic/pull/2438) Easier access to websocket class for annotation: `from sanic import Websocket`
- [#2439](https://github.com/sanic-org/sanic/pull/2439) New API for reading form values with options: `Request.get_form`
- [#2445](https://github.com/sanic-org/sanic/pull/2445) Add custom `loads` function
- [#2447](https://github.com/sanic-org/sanic/pull/2447), [#2486](https://github.com/sanic-org/sanic/pull/2486) Improved API to support setting cache control headers
- [#2453](https://github.com/sanic-org/sanic/pull/2453) Move verbosity filtering to logger
- [#2475](https://github.com/sanic-org/sanic/pull/2475) Expose getter for current request using `Request.get_current()`
### Bugfixes
- [#2448](https://github.com/sanic-org/sanic/pull/2448) Fix to allow running with `pythonw.exe` or places where there is no `sys.stdout`
- [#2451](https://github.com/sanic-org/sanic/pull/2451) Trigger `http.lifecycle.request` signal in ASGI mode
- [#2455](https://github.com/sanic-org/sanic/pull/2455) Resolve typing of stacked route definitions
- [#2463](https://github.com/sanic-org/sanic/pull/2463) Properly catch websocket CancelledError in websocket handler in Python 3.7
### Deprecations and Removals
- [#2487](https://github.com/sanic-org/sanic/pull/2487) v22.6 deprecations and changes
1. Optional application registry
1. Execution of custom handlers after some part of response was sent
1. Configuring fallback handlers on the `ErrorHandler`
1. Custom `LOGO` setting
1. `sanic.response.stream`
1. `AsyncioServer.init`
### Developer infrastructure
- [#2449](https://github.com/sanic-org/sanic/pull/2449) Clean up `black` and `isort` config
- [#2479](https://github.com/sanic-org/sanic/pull/2479) Fix some flappy tests
### Improved Documentation
- [#2461](https://github.com/sanic-org/sanic/pull/2461) Update example to match current application naming standards
- [#2466](https://github.com/sanic-org/sanic/pull/2466) Better type annotation for `Extend`
- [#2485](https://github.com/sanic-org/sanic/pull/2485) Improved help messages in CLI

View File

@@ -0,0 +1,74 @@
## Version 22.9.1
### Features
- [#2585](https://github.com/sanic-org/sanic/pull/2585) Improved error message when no applications have been registered
### Bugfixes
- [#2578](https://github.com/sanic-org/sanic/pull/2578) Add certificate loader for in process certificate creation
- [#2591](https://github.com/sanic-org/sanic/pull/2591) Do not use sentinel identity for `spawn` compatibility
- [#2592](https://github.com/sanic-org/sanic/pull/2592) Fix properties in nested blueprint groups
- [#2595](https://github.com/sanic-org/sanic/pull/2595) Introduce sleep interval on new worker reloader
### Deprecations and Removals
### Developer infrastructure
- [#2588](https://github.com/sanic-org/sanic/pull/2588) Markdown templates on issue forms
### Improved Documentation
- [#2556](https://github.com/sanic-org/sanic/pull/2556) v22.9 documentation
- [#2582](https://github.com/sanic-org/sanic/pull/2582) Cleanup documentation on Windows support
## Version 22.9.0
### Features
- [#2445](https://github.com/sanic-org/sanic/pull/2445) Add custom loads function
- [#2490](https://github.com/sanic-org/sanic/pull/2490) Make `WebsocketImplProtocol` async iterable
- [#2499](https://github.com/sanic-org/sanic/pull/2499) Sanic Server WorkerManager refactor
- [#2506](https://github.com/sanic-org/sanic/pull/2506) Use `pathlib` for path resolution (for static file serving)
- [#2508](https://github.com/sanic-org/sanic/pull/2508) Use `path.parts` instead of `match` (for static file serving)
- [#2513](https://github.com/sanic-org/sanic/pull/2513) Better request cancel handling
- [#2516](https://github.com/sanic-org/sanic/pull/2516) Add request properties for HTTP method info:
- `request.is_safe`
- `request.is_idempotent`
- `request.is_cacheable`
- *See* [MDN docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods) *for more information about when these apply*
- [#2522](https://github.com/sanic-org/sanic/pull/2522) Always show server location in ASGI
- [#2526](https://github.com/sanic-org/sanic/pull/2526) Cache control support for static files for returning 304 when appropriate
- [#2533](https://github.com/sanic-org/sanic/pull/2533) Refactor `_static_request_handler`
- [#2540](https://github.com/sanic-org/sanic/pull/2540) Add signals before and after handler execution
- `http.handler.before`
- `http.handler.after`
- [#2542](https://github.com/sanic-org/sanic/pull/2542) Add *[redacted]* to CLI :)
- [#2546](https://github.com/sanic-org/sanic/pull/2546) Add deprecation warning filter
- [#2550](https://github.com/sanic-org/sanic/pull/2550) Middleware priority and performance enhancements
### Bugfixes
- [#2495](https://github.com/sanic-org/sanic/pull/2495) Prevent directory traversion with static files
- [#2515](https://github.com/sanic-org/sanic/pull/2515) Do not apply double slash to paths in certain static dirs in Blueprints
### Deprecations and Removals
- [#2525](https://github.com/sanic-org/sanic/pull/2525) Warn on duplicate route names, will be prevented outright in v23.3
- [#2537](https://github.com/sanic-org/sanic/pull/2537) Raise warning and deprecation notice on duplicate exceptions, will be prevented outright in v23.3
### Developer infrastructure
- [#2504](https://github.com/sanic-org/sanic/pull/2504) Cleanup test suite
- [#2505](https://github.com/sanic-org/sanic/pull/2505) Replace Unsupported Python Version Number from the Contributing Doc
- [#2530](https://github.com/sanic-org/sanic/pull/2530) Do not include tests folder in installed package resolver
### Improved Documentation
- [#2502](https://github.com/sanic-org/sanic/pull/2502) Fix a few typos
- [#2517](https://github.com/sanic-org/sanic/pull/2517) [#2536](https://github.com/sanic-org/sanic/pull/2536) Add some type hints

View File

@@ -0,0 +1,53 @@
## Version 23.3.0
### Features
- [#2545](https://github.com/sanic-org/sanic/pull/2545) Standardize init of exceptions for more consistent control of HTTP responses using exceptions
- [#2606](https://github.com/sanic-org/sanic/pull/2606) Decode headers as UTF-8 also in ASGI
- [#2646](https://github.com/sanic-org/sanic/pull/2646) Separate ASGI request and lifespan callables
- [#2659](https://github.com/sanic-org/sanic/pull/2659) Use ``FALLBACK_ERROR_FORMAT`` for handlers that return ``empty()``
- [#2662](https://github.com/sanic-org/sanic/pull/2662) Add basic file browser (HTML page) and auto-index serving
- [#2667](https://github.com/sanic-org/sanic/pull/2667) Nicer traceback formatting (HTML page)
- [#2668](https://github.com/sanic-org/sanic/pull/2668) Smarter error page rendering format selection; more reliant upon header and "common sense" defaults
- [#2680](https://github.com/sanic-org/sanic/pull/2680) Check the status of socket before shutting down with ``SHUT_RDWR``
- [#2687](https://github.com/sanic-org/sanic/pull/2687) Refresh ``Request.accept`` functionality to be more performant and spec-compliant
- [#2696](https://github.com/sanic-org/sanic/pull/2696) Add header accessors as properties
```
Example-Field: Foo, Bar
Example-Field: Baz
```
```python
request.headers.example_field == "Foo, Bar,Baz"
```
- [#2700](https://github.com/sanic-org/sanic/pull/2700) Simpler CLI targets
```sh
$ sanic path.to.module:app # global app instance
$ sanic path.to.module:create_app # factory pattern
$ sanic ./path/to/directory/ # simple serve
```
- [#2701](https://github.com/sanic-org/sanic/pull/2701) API to define a number of workers in managed processes
- [#2704](https://github.com/sanic-org/sanic/pull/2704) Add convenience for dynamic changes to routing
- [#2706](https://github.com/sanic-org/sanic/pull/2706) Add convenience methods for cookie creation and deletion
```python
response = text("...")
response.add_cookie("test", "It worked!", domain=".yummy-yummy-cookie.com")
```
- [#2707](https://github.com/sanic-org/sanic/pull/2707) Simplified ``parse_content_header`` escaping to be RFC-compliant and remove outdated FF hack
- [#2710](https://github.com/sanic-org/sanic/pull/2710) Stricter charset handling and escaping of request URLs
- [#2711](https://github.com/sanic-org/sanic/pull/2711) Consume body on ``DELETE`` by default
- [#2719](https://github.com/sanic-org/sanic/pull/2719) Allow ``password`` to be passed to TLS context
- [#2720](https://github.com/sanic-org/sanic/pull/2720) Skip middleware on ``RequestCancelled``
- [#2721](https://github.com/sanic-org/sanic/pull/2721) Change access logging format to ``%s``
- [#2722](https://github.com/sanic-org/sanic/pull/2722) Add ``CertLoader`` as application option for directly controlling ``SSLContext`` objects
- [#2725](https://github.com/sanic-org/sanic/pull/2725) Worker sync state tolerance on race condition
### Bugfixes
- [#2651](https://github.com/sanic-org/sanic/pull/2651) ASGI websocket to pass thru bytes as is
- [#2697](https://github.com/sanic-org/sanic/pull/2697) Fix comparison between datetime aware and naive in ``file`` when using ``If-Modified-Since``
### Deprecations and Removals
- [#2666](https://github.com/sanic-org/sanic/pull/2666) Remove deprecated ``__blueprintname__`` property
### Improved Documentation
- [#2712](https://github.com/sanic-org/sanic/pull/2712) Improved example using ``'https'`` to create the redirect

View File

@@ -0,0 +1,33 @@
## Version 23.6.0 🔶
### Features
- [#2670](https://github.com/sanic-org/sanic/pull/2670) Increase `KEEP_ALIVE_TIMEOUT` default to 120 seconds
- [#2716](https://github.com/sanic-org/sanic/pull/2716) Adding allow route overwrite option in blueprint
- [#2724](https://github.com/sanic-org/sanic/pull/2724) and [#2792](https://github.com/sanic-org/sanic/pull/2792) Add a new exception signal for ALL exceptions raised anywhere in application
- [#2727](https://github.com/sanic-org/sanic/pull/2727) Add name prefixing to BP groups
- [#2754](https://github.com/sanic-org/sanic/pull/2754) Update request type on middleware types
- [#2770](https://github.com/sanic-org/sanic/pull/2770) Better exception message on startup time application induced import error
- [#2776](https://github.com/sanic-org/sanic/pull/2776) Set multiprocessing start method early
- [#2785](https://github.com/sanic-org/sanic/pull/2785) Add custom typing to config and ctx objects
- [#2790](https://github.com/sanic-org/sanic/pull/2790) Add `request.client_ip`
### Bugfixes
- [#2728](https://github.com/sanic-org/sanic/pull/2728) Fix traversals for intended results
- [#2729](https://github.com/sanic-org/sanic/pull/2729) Handle case when headers argument of ResponseStream constructor is None
- [#2737](https://github.com/sanic-org/sanic/pull/2737) Fix type annotation for `JSONREsponse` default content type
- [#2740](https://github.com/sanic-org/sanic/pull/2740) Use Sanic's serializer for JSON responses in the Inspector
- [#2760](https://github.com/sanic-org/sanic/pull/2760) Support for `Request.get_current` in ASGI mode
- [#2773](https://github.com/sanic-org/sanic/pull/2773) Alow Blueprint routes to explicitly define error_format
- [#2774](https://github.com/sanic-org/sanic/pull/2774) Resolve headers on different renderers
- [#2782](https://github.com/sanic-org/sanic/pull/2782) Resolve pypy compatibility issues
### Deprecations and Removals
- [#2777](https://github.com/sanic-org/sanic/pull/2777) Remove Python 3.7 support
### Developer infrastructure
- [#2766](https://github.com/sanic-org/sanic/pull/2766) Unpin setuptools version
- [#2779](https://github.com/sanic-org/sanic/pull/2779) Run keep alive tests in loop to get available port
### Improved Documentation
- [#2741](https://github.com/sanic-org/sanic/pull/2741) Better documentation examples about running Sanic
From that list, the items to highlight in the release notes:

View File

@@ -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():

View File

@@ -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")
@@ -25,5 +25,5 @@ def key_exist_handler(request):
return text("num does not exist in request") return text("num does not exist in request")
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)

View File

@@ -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):

View File

@@ -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
@@ -50,4 +50,5 @@ def pop_handler(request):
app.blueprint(bp, url_prefix="/bp") app.blueprint(bp, url_prefix="/bp")
app.run(host="0.0.0.0", port=8000, debug=True, auto_reload=False) if __name__ == "__main__":
app.run(host="0.0.0.0", port=8000, debug=True, auto_reload=False)

View File

@@ -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")
@@ -37,4 +37,5 @@ app.blueprint(blueprint)
app.blueprint(blueprint2) app.blueprint(blueprint2)
app.blueprint(blueprint3) app.blueprint(blueprint3)
app.run(host="0.0.0.0", port=9999, debug=True) if __name__ == "__main__":
app.run(host="0.0.0.0", port=9999, debug=True)

View File

@@ -3,7 +3,8 @@ 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.config.AUTO_EXTEND = False
@app.get("/") @app.get("/")
@@ -11,7 +12,7 @@ async def handler(request):
return response.redirect("/sleep/3") return response.redirect("/sleep/3")
@app.get("/sleep/<t:number>") @app.get("/sleep/<t:float>")
async def handler2(request, t=0.3): async def handler2(request, t=0.3):
await sleep(t) await sleep(t)
return response.text(f"Slept {t:.1f} seconds.\n") return response.text(f"Slept {t:.1f} seconds.\n")

View File

@@ -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("/")

View File

@@ -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)

View File

@@ -29,7 +29,7 @@ def proxy(request, path):
path=path, path=path,
_server=https.config.SERVER_NAME, _server=https.config.SERVER_NAME,
_external=True, _external=True,
_scheme="http", _scheme="https",
) )
return response.redirect(url) return response.redirect(url)
@@ -69,5 +69,5 @@ async def runner(app: Sanic, app_server: AsyncioServer):
app.is_running = False app.is_running = False
app.is_stopping = True app.is_stopping = True
if __name__ == "__main__":
https.run(port=HTTPS_PORT, debug=True) https.run(port=HTTPS_PORT, debug=True)

View File

@@ -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
@@ -39,4 +39,5 @@ async def test(request):
return json(response) return json(response)
app.run(host="0.0.0.0", port=8000, workers=2) if __name__ == "__main__":
app.run(host="0.0.0.0", port=8000, workers=2)

View File

@@ -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

View File

@@ -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

View File

@@ -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,
) )
app.run(host="0.0.0.0", port=8000, debug=True)
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8000, debug=True)

View File

@@ -20,4 +20,5 @@ def test(request):
return text("hey") return text("hey")
app.run(host="0.0.0.0", port=8000) if __name__ == "__main__":
app.run(host="0.0.0.0", port=8000)

View File

@@ -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):

View File

@@ -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)
)

View File

@@ -1,18 +1,18 @@
from sanic import Sanic from sanic import Sanic, response
from sanic import response
app = Sanic(__name__)
app = Sanic("Example")
@app.route('/')
@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)

View File

@@ -6,5 +6,5 @@ data = ""
for i in range(1, 250000): for i in range(1, 250000):
data += str(i) data += str(i)
r = requests.post('http://0.0.0.0:8000/stream', data=data) r = requests.post("http://0.0.0.0:8000/stream", data=data)
print(r.text) print(r.text)

View File

@@ -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)

View File

@@ -1,21 +1,24 @@
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)
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8000)

View File

@@ -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)
)

View File

@@ -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")

View File

@@ -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("/")

View File

@@ -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
@@ -35,34 +35,34 @@ async def after_server_stop(app, loop):
async def test(request): async def test(request):
return response.json({"answer": "42"}) return response.json({"answer": "42"})
if __name__ == "__main__":
asyncio.set_event_loop(uvloop.new_event_loop())
serv_coro = app.create_server(
host="0.0.0.0", port=8000, return_asyncio_server=True
)
loop = asyncio.get_event_loop()
serv_task = asyncio.ensure_future(serv_coro, loop=loop)
signal(SIGINT, lambda s, f: loop.stop())
server: AsyncioServer = loop.run_until_complete(serv_task)
loop.run_until_complete(server.startup())
asyncio.set_event_loop(uvloop.new_event_loop()) # When using app.run(), this actually triggers before the serv_coro.
serv_coro = app.create_server( # But, in this example, we are using the convenience method, even if it is
host="0.0.0.0", port=8000, return_asyncio_server=True # out of order.
) loop.run_until_complete(server.before_start())
loop = asyncio.get_event_loop() loop.run_until_complete(server.after_start())
serv_task = asyncio.ensure_future(serv_coro, loop=loop) try:
signal(SIGINT, lambda s, f: loop.stop()) loop.run_forever()
server: AsyncioServer = loop.run_until_complete(serv_task) except KeyboardInterrupt:
loop.run_until_complete(server.startup()) loop.stop()
finally:
loop.run_until_complete(server.before_stop())
# When using app.run(), this actually triggers before the serv_coro. # Wait for server to close
# But, in this example, we are using the convenience method, even if it is close_task = server.close()
# out of order. loop.run_until_complete(close_task)
loop.run_until_complete(server.before_start())
loop.run_until_complete(server.after_start())
try:
loop.run_forever()
except KeyboardInterrupt:
loop.stop()
finally:
loop.run_until_complete(server.before_stop())
# Wait for server to close # Complete all tasks on the loop
close_task = server.close() for connection in server.connections:
loop.run_until_complete(close_task) connection.close_if_idle()
loop.run_until_complete(server.after_stop())
# Complete all tasks on the loop
for connection in server.connections:
connection.close_if_idle()
loop.run_until_complete(server.after_stop())

View File

@@ -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)
)

View File

@@ -1,6 +1,6 @@
from sanic import Sanic from sanic import Sanic
app = Sanic(__name__) app = Sanic("Example")
app.static("/", "./static") app.static("/", "./static")

View File

@@ -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)

View File

@@ -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("/")

View File

@@ -1,10 +1,7 @@
import os
import socket
from sanic import Sanic, response from sanic import Sanic, response
app = Sanic(__name__) app = Sanic("Example")
@app.route("/test") @app.route("/test")
@@ -13,13 +10,4 @@ async def test(request):
if __name__ == "__main__": if __name__ == "__main__":
server_address = "./uds_socket" app.run(unix="./uds_socket")
# Make sure the socket does not already exist
try:
os.unlink(server_address)
except OSError:
if os.path.exists(server_address):
raise
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.bind(server_address)
app.run(sock=sock)

View File

@@ -1,7 +1,7 @@
from sanic import Sanic, response from sanic import Sanic, response
app = Sanic(__name__) app = Sanic("Example")
@app.route("/") @app.route("/")

View File

@@ -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")

View File

@@ -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")

View File

@@ -1,3 +1,29 @@
[build-system] [build-system]
requires = ["setuptools", "wheel"] requires = ["setuptools", "wheel"]
build-backend = "setuptools.build_meta" build-backend = "setuptools.build_meta"
[tool.black]
line-length = 79
[tool.isort]
atomic = true
default_section = "THIRDPARTY"
include_trailing_comma = true
known_first_party = "sanic"
known_third_party = "pytest"
line_length = 79
lines_after_imports = 2
lines_between_types = 1
multi_line_output = 3
profile = "black"
[[tool.mypy.overrides]]
module = [
"httptools.*",
"trustme.*",
"sanic_routing.*",
"aioquic.*",
"html5tagger.*",
"tracerite.*",
]
ignore_missing_imports = true

View File

@@ -6,4 +6,4 @@ python:
path: . path: .
extra_requirements: extra_requirements:
- docs - docs
system_packages: true system_packages: true

View File

@@ -1,19 +1,86 @@
from types import SimpleNamespace
from typing_extensions import TypeAlias
from sanic.__version__ import __version__ from sanic.__version__ import __version__
from sanic.app import Sanic from sanic.app import Sanic
from sanic.blueprints import Blueprint from sanic.blueprints import Blueprint
from sanic.config import Config
from sanic.constants import HTTPMethod from sanic.constants import HTTPMethod
from sanic.exceptions import (
BadRequest,
ExpectationFailed,
FileNotFound,
Forbidden,
HeaderNotFound,
InternalServerError,
InvalidHeader,
MethodNotAllowed,
NotFound,
RangeNotSatisfiable,
SanicException,
ServerError,
ServiceUnavailable,
Unauthorized,
)
from sanic.request import Request from sanic.request import Request
from sanic.response import HTTPResponse, html, json, text from sanic.response import (
HTTPResponse,
empty,
file,
html,
json,
raw,
redirect,
text,
)
from sanic.server.websockets.impl import WebsocketImplProtocol as Websocket
DefaultSanic: TypeAlias = "Sanic[Config, SimpleNamespace]"
"""
A type alias for a Sanic app with a default config and namespace.
"""
DefaultRequest: TypeAlias = Request[DefaultSanic, SimpleNamespace]
"""
A type alias for a request with a default Sanic app and namespace.
"""
__all__ = ( __all__ = (
"__version__", "__version__",
# Common objects
"Sanic", "Sanic",
"Config",
"Blueprint", "Blueprint",
"HTTPMethod", "HTTPMethod",
"HTTPResponse", "HTTPResponse",
"Request", "Request",
"Websocket",
# Common types
"DefaultSanic",
"DefaultRequest",
# Common exceptions
"BadRequest",
"ExpectationFailed",
"FileNotFound",
"Forbidden",
"HeaderNotFound",
"InternalServerError",
"InvalidHeader",
"MethodNotAllowed",
"NotFound",
"RangeNotSatisfiable",
"SanicException",
"ServerError",
"ServiceUnavailable",
"Unauthorized",
# Common response methods
"empty",
"file",
"html", "html",
"json", "json",
"raw",
"redirect",
"text", "text",
) )

View File

@@ -6,10 +6,10 @@ if OS_IS_WINDOWS:
enable_windows_color_support() enable_windows_color_support()
def main(): def main(args=None):
cli = SanicCLI() cli = SanicCLI()
cli.attach() cli.attach()
cli.run() cli.run(args)
if __name__ == "__main__": if __name__ == "__main__":

View File

@@ -1 +1 @@
__version__ = "21.12.0dev" __version__ = "23.6.0"

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,32 @@
from enum import Enum, IntEnum, auto
class StrEnum(str, Enum): # no cov
def _generate_next_value_(name: str, *args) -> str: # type: ignore
return name.lower()
def __eq__(self, value: object) -> bool:
value = str(value).upper()
return super().__eq__(value)
def __hash__(self) -> int:
return hash(self.value)
def __str__(self) -> str:
return self.value
class Server(StrEnum):
SANIC = auto()
ASGI = auto()
class Mode(StrEnum):
PRODUCTION = auto()
DEBUG = auto()
class ServerStage(IntEnum):
STOPPED = auto()
PARTIAL = auto()
SERVING = auto()

34
sanic/application/ext.py Normal file
View File

@@ -0,0 +1,34 @@
from __future__ import annotations
from contextlib import suppress
from importlib import import_module
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from sanic import Sanic
def setup_ext(app: Sanic, *, fail: bool = False, **kwargs):
if not app.config.AUTO_EXTEND:
return
sanic_ext = None
with suppress(ModuleNotFoundError):
sanic_ext = import_module("sanic_ext")
if not sanic_ext: # no cov
if fail:
raise RuntimeError(
"Sanic Extensions is not installed. You can add it to your "
"environment using:\n$ pip install sanic[ext]\nor\n$ pip "
"install sanic-ext"
)
return
if not getattr(app, "_ext", None):
Ext = getattr(sanic_ext, "Extend")
app._ext = Ext(app, **kwargs)
return app.ext

View File

@@ -3,6 +3,8 @@ import sys
from os import environ from os import environ
from sanic.helpers import is_atty
BASE_LOGO = """ BASE_LOGO = """
@@ -38,13 +40,15 @@ FULL_COLOR_LOGO = """
""" # noqa """ # noqa
SVG_LOGO_SIMPLE = """<svg id=logo-simple viewBox="0 0 964 279"><desc>Sanic</desc><path d="M107 222c9-2 10-20 1-22s-20-2-30-2-17 7-16 14 6 10 15 10h30zm115-1c16-2 30-11 35-23s6-24 2-33-6-14-15-20-24-11-38-10c-7 3-10 13-5 19s17-1 24 4 15 14 13 24-5 15-14 18-50 0-74 0h-17c-6 4-10 15-4 20s16 2 23 3zM251 83q9-1 9-7 0-15-10-16h-13c-10 6-10 20 0 22zM147 60c-4 0-10 3-11 11s5 13 10 12 42 0 67 0c5-3 7-10 6-15s-4-8-9-8zm-33 1c-8 0-16 0-24 3s-20 10-25 20-6 24-4 36 15 22 26 27 78 8 94 3c4-4 4-12 0-18s-69 8-93-10c-8-7-9-23 0-30s12-10 20-10 12 2 16-3 1-15-5-18z" fill="#ff0d68"/><path d="M676 74c0-14-18-9-20 0s0 30 0 39 20 9 20 2zm-297-10c-12 2-15 12-23 23l-41 58H340l22-30c8-12 23-13 30-4s20 24 24 38-10 10-17 10l-68 2q-17 1-48 30c-7 6-10 20 0 24s15-8 20-13 20 -20 58-21h50 c20 2 33 9 52 30 8 10 24-4 16-13L384 65q-3-2-5-1zm131 0c-10 1-12 12-11 20v96c1 10-3 23 5 32s20-5 17-15c0-23-3-46 2-67 5-12 22-14 32-5l103 87c7 5 19 1 18-9v-64c-3-10-20-9-21 2s-20 22-30 13l-97-80c-5-4-10-10-18-10zM701 76v128c2 10 15 12 20 4s0-102 0-124s-20-18-20-7z M850 63c-35 0-69-2-86 15s-20 60-13 66 13 8 16 0 1-10 1-27 12-26 20-32 66-5 85-5 31 4 31-10-18-7-54-7M764 159c-6-2-15-2-16 12s19 37 33 43 23 8 25-4-4-11-11-14q-9-3-22-18c-4-7-3-16-10-19zM828 196c-4 0-8 1-10 5s-4 12 0 15 8 2 12 2h60c5 0 10-2 12-6 3-7-1-16-8-16z" fill="#1f1f1f"/></svg>""" # noqa
ansi_pattern = re.compile(r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])") ansi_pattern = re.compile(r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])")
def get_logo(full=False, coffee=False): def get_logo(full=False, coffee=False):
logo = ( logo = (
(FULL_COLOR_LOGO if full else (COFFEE_LOGO if coffee else COLOR_LOGO)) (FULL_COLOR_LOGO if full else (COFFEE_LOGO if coffee else COLOR_LOGO))
if sys.stdout.isatty() if is_atty()
else BASE_LOGO else BASE_LOGO
) )

View File

@@ -1,11 +1,10 @@
import sys
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from shutil import get_terminal_size from shutil import get_terminal_size
from textwrap import indent, wrap from textwrap import indent, wrap
from typing import Dict, Optional from typing import Dict, Optional
from sanic import __version__ from sanic import __version__
from sanic.helpers import is_atty
from sanic.log import logger from sanic.log import logger
@@ -36,14 +35,11 @@ class MOTD(ABC):
data: Dict[str, str], data: Dict[str, str],
extra: Dict[str, str], extra: Dict[str, str],
) -> None: ) -> None:
motd_class = MOTDTTY if sys.stdout.isatty() else MOTDBasic motd_class = MOTDTTY if is_atty() else MOTDBasic
motd_class(logo, serve_location, data, extra).display() motd_class(logo, serve_location, data, extra).display()
class MOTDBasic(MOTD): class MOTDBasic(MOTD):
def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
def display(self): def display(self):
if self.logo: if self.logo:
logger.debug(self.logo) logger.debug(self.logo)
@@ -77,6 +73,14 @@ class MOTDTTY(MOTD):
self.value_width = min( self.value_width = min(
max(map(len, self.data.values())), self.max_value_width max(map(len, self.data.values())), self.max_value_width
) )
if self.extra:
self.key_width = max(
self.key_width, max(map(len, self.extra.keys()))
)
self.value_width = min(
max((*map(len, self.extra.values()), self.value_width)),
self.max_value_width,
)
self.logo_lines = self.logo.split("\n") if self.logo else [] self.logo_lines = self.logo.split("\n") if self.logo else []
self.logo_line_length = 24 self.logo_line_length = 24
self.centering_length = ( self.centering_length = (
@@ -84,20 +88,23 @@ class MOTDTTY(MOTD):
) )
self.display_length = self.key_width + self.value_width + 2 self.display_length = self.key_width + self.value_width + 2
def display(self): def display(self, version=True, action="Goin' Fast", out=None):
version = f"Sanic v{__version__}".center(self.centering_length) if not out:
out = logger.info
header = "Sanic"
if version:
header += f" v{__version__}"
header = header.center(self.centering_length)
running = ( running = (
f"Goin' Fast @ {self.serve_location}" f"{action} @ {self.serve_location}" if self.serve_location else ""
if self.serve_location
else ""
).center(self.centering_length) ).center(self.centering_length)
length = len(version) + 2 - self.logo_line_length length = len(header) + 2 - self.logo_line_length
first_filler = "" * (self.logo_line_length - 1) first_filler = "" * (self.logo_line_length - 1)
second_filler = "" * length second_filler = "" * length
display_filler = "" * (self.display_length + 2) display_filler = "" * (self.display_length + 2)
lines = [ lines = [
f"\n{first_filler}{second_filler}", f"\n{first_filler}{second_filler}",
f"{version}", f"{header}",
f"{running}", f"{running}",
f"{first_filler}{second_filler}", f"{first_filler}{second_filler}",
] ]
@@ -105,13 +112,13 @@ class MOTDTTY(MOTD):
self._render_data(lines, self.data, 0) self._render_data(lines, self.data, 0)
if self.extra: if self.extra:
logo_part = self._get_logo_part(len(lines) - 4) logo_part = self._get_logo_part(len(lines) - 4)
lines.append(f"| {logo_part}{display_filler}") lines.append(f" {logo_part}{display_filler}")
self._render_data(lines, self.extra, len(lines) - 4) self._render_data(lines, self.extra, len(lines) - 4)
self._render_fill(lines) self._render_fill(lines)
lines.append(f"{first_filler}{second_filler}\n") lines.append(f"{first_filler}{second_filler}\n")
logger.info(indent("\n".join(lines), " ")) out(indent("\n".join(lines), " "))
def _render_data(self, lines, data, start): def _render_data(self, lines, data, start):
offset = 0 offset = 0

View File

@@ -0,0 +1,86 @@
import os
import sys
import time
from contextlib import contextmanager
from queue import Queue
from threading import Thread
if os.name == "nt": # noqa
import ctypes # noqa
class _CursorInfo(ctypes.Structure):
_fields_ = [("size", ctypes.c_int), ("visible", ctypes.c_byte)]
class Spinner: # noqa
def __init__(self, message: str) -> None:
self.message = message
self.queue: Queue[int] = Queue()
self.spinner = self.cursor()
self.thread = Thread(target=self.run)
def start(self):
self.queue.put(1)
self.thread.start()
self.hide()
def run(self):
while self.queue.get():
output = f"\r{self.message} [{next(self.spinner)}]"
sys.stdout.write(output)
sys.stdout.flush()
time.sleep(0.1)
self.queue.put(1)
def stop(self):
self.queue.put(0)
self.thread.join()
self.show()
@staticmethod
def cursor():
while True:
for cursor in "|/-\\":
yield cursor
@staticmethod
def hide():
if os.name == "nt":
ci = _CursorInfo()
handle = ctypes.windll.kernel32.GetStdHandle(-11)
ctypes.windll.kernel32.GetConsoleCursorInfo(
handle, ctypes.byref(ci)
)
ci.visible = False
ctypes.windll.kernel32.SetConsoleCursorInfo(
handle, ctypes.byref(ci)
)
elif os.name == "posix":
sys.stdout.write("\033[?25l")
sys.stdout.flush()
@staticmethod
def show():
if os.name == "nt":
ci = _CursorInfo()
handle = ctypes.windll.kernel32.GetStdHandle(-11)
ctypes.windll.kernel32.GetConsoleCursorInfo(
handle, ctypes.byref(ci)
)
ci.visible = True
ctypes.windll.kernel32.SetConsoleCursorInfo(
handle, ctypes.byref(ci)
)
elif os.name == "posix":
sys.stdout.write("\033[?25h")
sys.stdout.flush()
@contextmanager
def loading(message: str = "Loading"): # noqa
spinner = Spinner(message)
spinner.start()
yield
spinner.stop()

View File

@@ -3,31 +3,25 @@ from __future__ import annotations
import logging import logging
from dataclasses import dataclass, field from dataclasses import dataclass, field
from enum import Enum, auto
from pathlib import Path from pathlib import Path
from typing import TYPE_CHECKING, Any, Set, Union from socket import socket
from ssl import SSLContext
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Set, Union
from sanic.log import logger from sanic.application.constants import Mode, Server, ServerStage
from sanic.log import VerbosityFilter, logger
from sanic.server.async_server import AsyncioServer
if TYPE_CHECKING: if TYPE_CHECKING:
from sanic import Sanic from sanic import Sanic
class StrEnum(str, Enum): @dataclass
def _generate_next_value_(name: str, *args) -> str: # type: ignore class ApplicationServerInfo:
return name.lower() settings: Dict[str, Any]
stage: ServerStage = field(default=ServerStage.STOPPED)
server: Optional[AsyncioServer] = field(default=None)
class Server(StrEnum):
SANIC = auto()
ASGI = auto()
GUNICORN = auto()
class Mode(StrEnum):
PRODUCTION = auto()
DEBUG = auto()
@dataclass @dataclass
@@ -37,15 +31,21 @@ class ApplicationState:
coffee: bool = field(default=False) coffee: bool = field(default=False)
fast: bool = field(default=False) fast: bool = field(default=False)
host: str = field(default="") host: str = field(default="")
mode: Mode = field(default=Mode.PRODUCTION)
port: int = field(default=0) port: int = field(default=0)
ssl: Optional[SSLContext] = field(default=None)
sock: Optional[socket] = field(default=None)
unix: Optional[str] = field(default=None)
mode: Mode = field(default=Mode.PRODUCTION)
reload_dirs: Set[Path] = field(default_factory=set) reload_dirs: Set[Path] = field(default_factory=set)
auto_reload: bool = field(default=False)
server: Server = field(default=Server.SANIC) server: Server = field(default=Server.SANIC)
is_running: bool = field(default=False) is_running: bool = field(default=False)
is_started: bool = field(default=False) is_started: bool = field(default=False)
is_stopping: bool = field(default=False) is_stopping: bool = field(default=False)
verbosity: int = field(default=0) verbosity: int = field(default=0)
workers: int = field(default=0) workers: int = field(default=0)
primary: bool = field(default=True)
server_info: List[ApplicationServerInfo] = field(default_factory=list)
# This property relates to the ApplicationState instance and should # This property relates to the ApplicationState instance and should
# not be changed except in the __post_init__ method # not be changed except in the __post_init__ method
@@ -69,6 +69,23 @@ class ApplicationState:
if getattr(self.app, "configure_logging", False) and self.app.debug: if getattr(self.app, "configure_logging", False) and self.app.debug:
logger.setLevel(logging.DEBUG) logger.setLevel(logging.DEBUG)
def set_verbosity(self, value: int):
VerbosityFilter.verbosity = value
@property @property
def is_debug(self): def is_debug(self):
return self.mode is Mode.DEBUG return self.mode is Mode.DEBUG
@property
def stage(self) -> ServerStage:
if not self.server_info:
return ServerStage.STOPPED
if all(info.stage is ServerStage.SERVING for info in self.server_info):
return ServerStage.SERVING
elif any(
info.stage is ServerStage.SERVING for info in self.server_info
):
return ServerStage.PARTIAL
return ServerStage.STOPPED

View File

@@ -1,13 +1,14 @@
from __future__ import annotations
import warnings import warnings
from typing import Optional from typing import TYPE_CHECKING, Optional
from urllib.parse import quote
import sanic.app # noqa
from sanic.compat import Header from sanic.compat import Header
from sanic.exceptions import ServerError from sanic.exceptions import BadRequest, ServerError
from sanic.helpers import Default
from sanic.http import Stage from sanic.http import Stage
from sanic.log import error_logger, logger
from sanic.models.asgi import ASGIReceive, ASGIScope, ASGISend, MockTransport from sanic.models.asgi import ASGIReceive, ASGIScope, ASGISend, MockTransport
from sanic.request import Request from sanic.request import Request
from sanic.response import BaseHTTPResponse from sanic.response import BaseHTTPResponse
@@ -15,29 +16,34 @@ from sanic.server import ConnInfo
from sanic.server.websockets.connection import WebSocketConnection from sanic.server.websockets.connection import WebSocketConnection
class Lifespan: if TYPE_CHECKING:
def __init__(self, asgi_app: "ASGIApp") -> None: from sanic import Sanic
self.asgi_app = asgi_app
if (
"server.init.before" class Lifespan:
in self.asgi_app.sanic_app.signal_router.name_index def __init__(
): self, sanic_app, scope: ASGIScope, receive: ASGIReceive, send: ASGISend
warnings.warn( ) -> None:
self.sanic_app = sanic_app
self.scope = scope
self.receive = receive
self.send = send
if "server.init.before" in self.sanic_app.signal_router.name_index:
logger.debug(
'You have set a listener for "before_server_start" ' 'You have set a listener for "before_server_start" '
"in ASGI mode. " "in ASGI mode. "
"It will be executed as early as possible, but not before " "It will be executed as early as possible, but not before "
"the ASGI server is started." "the ASGI server is started.",
extra={"verbosity": 1},
) )
if ( if "server.shutdown.after" in self.sanic_app.signal_router.name_index:
"server.shutdown.after" logger.debug(
in self.asgi_app.sanic_app.signal_router.name_index
):
warnings.warn(
'You have set a listener for "after_server_stop" ' 'You have set a listener for "after_server_stop" '
"in ASGI mode. " "in ASGI mode. "
"It will be executed as late as possible, but not after " "It will be executed as late as possible, but not after "
"the ASGI server is stopped." "the ASGI server is stopped.",
extra={"verbosity": 1},
) )
async def startup(self) -> None: async def startup(self) -> None:
@@ -49,9 +55,16 @@ class Lifespan:
in sequence since the ASGI lifespan protocol only supports a single in sequence since the ASGI lifespan protocol only supports a single
startup event. startup event.
""" """
await self.asgi_app.sanic_app._startup() await self.sanic_app._startup()
await self.asgi_app.sanic_app._server_event("init", "before") await self.sanic_app._server_event("init", "before")
await self.asgi_app.sanic_app._server_event("init", "after") await self.sanic_app._server_event("init", "after")
if not isinstance(self.sanic_app.config.USE_UVLOOP, Default):
warnings.warn(
"You have set the USE_UVLOOP configuration option, but Sanic "
"cannot control the event loop when running in ASGI mode."
"This option will be ignored."
)
async def shutdown(self) -> None: async def shutdown(self) -> None:
""" """
@@ -62,25 +75,37 @@ class Lifespan:
in sequence since the ASGI lifespan protocol only supports a single in sequence since the ASGI lifespan protocol only supports a single
shutdown event. shutdown event.
""" """
await self.asgi_app.sanic_app._server_event("shutdown", "before") await self.sanic_app._server_event("shutdown", "before")
await self.asgi_app.sanic_app._server_event("shutdown", "after") await self.sanic_app._server_event("shutdown", "after")
async def __call__( async def __call__(self) -> None:
self, scope: ASGIScope, receive: ASGIReceive, send: ASGISend while True:
) -> None: message = await self.receive()
message = await receive() if message["type"] == "lifespan.startup":
if message["type"] == "lifespan.startup": try:
await self.startup() await self.startup()
await send({"type": "lifespan.startup.complete"}) except Exception as e:
error_logger.exception(e)
message = await receive() await self.send(
if message["type"] == "lifespan.shutdown": {"type": "lifespan.startup.failed", "message": str(e)}
await self.shutdown() )
await send({"type": "lifespan.shutdown.complete"}) else:
await self.send({"type": "lifespan.startup.complete"})
elif message["type"] == "lifespan.shutdown":
try:
await self.shutdown()
except Exception as e:
error_logger.exception(e)
await self.send(
{"type": "lifespan.shutdown.failed", "message": str(e)}
)
else:
await self.send({"type": "lifespan.shutdown.complete"})
return
class ASGIApp: class ASGIApp:
sanic_app: "sanic.app.Sanic" sanic_app: Sanic
request: Request request: Request
transport: MockTransport transport: MockTransport
lifespan: Lifespan lifespan: Lifespan
@@ -88,66 +113,79 @@ class ASGIApp:
stage: Stage stage: Stage
response: Optional[BaseHTTPResponse] response: Optional[BaseHTTPResponse]
def __init__(self) -> None:
self.ws = None
@classmethod @classmethod
async def create( async def create(
cls, sanic_app, scope: ASGIScope, receive: ASGIReceive, send: ASGISend cls,
) -> "ASGIApp": sanic_app: Sanic,
scope: ASGIScope,
receive: ASGIReceive,
send: ASGISend,
) -> ASGIApp:
instance = cls() instance = cls()
instance.ws = None
instance.sanic_app = sanic_app instance.sanic_app = sanic_app
instance.transport = MockTransport(scope, receive, send) instance.transport = MockTransport(scope, receive, send)
instance.transport.loop = sanic_app.loop instance.transport.loop = sanic_app.loop
instance.stage = Stage.IDLE instance.stage = Stage.IDLE
instance.response = None instance.response = None
instance.sanic_app.state.is_started = True
setattr(instance.transport, "add_task", sanic_app.loop.create_task) setattr(instance.transport, "add_task", sanic_app.loop.create_task)
headers = Header( try:
[ headers = Header(
(key.decode("latin-1"), value.decode("latin-1")) [
for key, value in scope.get("headers", []) (
] key.decode("ASCII"),
) value.decode(errors="surrogateescape"),
instance.lifespan = Lifespan(instance) )
for key, value in scope.get("headers", [])
]
)
except UnicodeDecodeError:
raise BadRequest(
"Header names can only contain US-ASCII characters"
)
if scope["type"] == "lifespan": if scope["type"] == "http":
await instance.lifespan(scope, receive, send) version = scope["http_version"]
method = scope["method"]
elif scope["type"] == "websocket":
version = "1.1"
method = "GET"
instance.ws = instance.transport.create_websocket_connection(
send, receive
)
else: else:
path = ( raise ServerError("Received unknown ASGI scope")
scope["path"][1:]
if scope["path"].startswith("/")
else scope["path"]
)
url = "/".join([scope.get("root_path", ""), quote(path)])
url_bytes = url.encode("latin-1")
url_bytes += b"?" + scope["query_string"]
if scope["type"] == "http": url_bytes, query = scope["raw_path"], scope["query_string"]
version = scope["http_version"] if query:
method = scope["method"] # httpx ASGI client sends query string as part of raw_path
elif scope["type"] == "websocket": url_bytes = url_bytes.split(b"?", 1)[0]
version = "1.1" # All servers send them separately
method = "GET" url_bytes = b"%b?%b" % (url_bytes, query)
instance.ws = instance.transport.create_websocket_connection( request_class = sanic_app.request_class or Request
send, receive instance.request = request_class(
) url_bytes,
else: headers,
raise ServerError("Received unknown ASGI scope") version,
method,
instance.transport,
sanic_app,
)
request_class._current.set(instance.request)
instance.request.stream = instance # type: ignore
instance.request_body = True
instance.request.conn_info = ConnInfo(instance.transport)
request_class = sanic_app.request_class or Request await instance.sanic_app.dispatch(
instance.request = request_class( "http.lifecycle.request",
url_bytes, inline=True,
headers, context={"request": instance.request},
version, fail_not_found=False,
method, )
instance.transport,
sanic_app,
)
instance.request.stream = instance
instance.request_body = True
instance.request.conn_info = ConnInfo(instance.transport)
return instance return instance
@@ -212,4 +250,7 @@ class ASGIApp:
self.stage = Stage.HANDLER self.stage = Stage.HANDLER
await self.sanic_app.handle_request(self.request) await self.sanic_app.handle_request(self.request)
except Exception as e: except Exception as e:
await self.sanic_app.handle_exception(self.request, e) try:
await self.sanic_app.handle_exception(self.request, e)
except Exception as exc:
await self.sanic_app.handle_exception(self.request, exc, False)

0
sanic/base/__init__.py Normal file
View File

6
sanic/base/meta.py Normal file
View 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

View File

@@ -1,14 +1,15 @@
import re import re
from typing import Any, Tuple from typing import Any, Optional
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
from sanic.mixins.middleware import MiddlewareMixin from sanic.mixins.middleware import MiddlewareMixin
from sanic.mixins.routes import RouteMixin from sanic.mixins.routes import RouteMixin
from sanic.mixins.signals import SignalMixin from sanic.mixins.signals import SignalMixin
from sanic.mixins.static import StaticMixin
VALID_NAME = re.compile(r"^[a-zA-Z_][a-zA-Z0-9_\-]*$") VALID_NAME = re.compile(r"^[a-zA-Z_][a-zA-Z0-9_\-]*$")
@@ -16,14 +17,18 @@ VALID_NAME = re.compile(r"^[a-zA-Z_][a-zA-Z0-9_\-]*$")
class BaseSanic( class BaseSanic(
RouteMixin, RouteMixin,
StaticMixin,
MiddlewareMixin, MiddlewareMixin,
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: Optional[str] = None, *args: Any, **kwargs: Any
) -> None:
class_name = self.__class__.__name__ class_name = self.__class__.__name__
if name is None: if name is None:
@@ -33,11 +38,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 +56,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 super().__setattr__(name, value)
# with a proper implementation of __slots__ except AttributeError as e:
if name not in self.__fake_slots__: raise AttributeError(
warn(
f"Setting variables on {self.__class__.__name__} instances is " f"Setting variables on {self.__class__.__name__} instances is "
"deprecated and will be removed in version 21.12. You should " "not allowed. You should change your "
f"change your {self.__class__.__name__} instance to use " f"{self.__class__.__name__} instance to use "
f"instance.ctx.{name} instead.", f"instance.ctx.{name} instead.",
DeprecationWarning, ) from e
)
super().__setattr__(name, value)

View File

@@ -65,6 +65,7 @@ class BlueprintGroup(MutableSequence):
"_version", "_version",
"_strict_slashes", "_strict_slashes",
"_version_prefix", "_version_prefix",
"_name_prefix",
) )
def __init__( def __init__(
@@ -73,6 +74,7 @@ class BlueprintGroup(MutableSequence):
version: Optional[Union[int, str, float]] = None, version: Optional[Union[int, str, float]] = None,
strict_slashes: Optional[bool] = None, strict_slashes: Optional[bool] = None,
version_prefix: str = "/v", version_prefix: str = "/v",
name_prefix: Optional[str] = "",
): ):
""" """
Create a new Blueprint Group Create a new Blueprint Group
@@ -87,6 +89,7 @@ class BlueprintGroup(MutableSequence):
self._version = version self._version = version
self._version_prefix = version_prefix self._version_prefix = version_prefix
self._strict_slashes = strict_slashes self._strict_slashes = strict_slashes
self._name_prefix = name_prefix
@property @property
def url_prefix(self) -> Optional[Union[int, str, float]]: def url_prefix(self) -> Optional[Union[int, str, float]]:
@@ -134,6 +137,15 @@ class BlueprintGroup(MutableSequence):
""" """
return self._version_prefix return self._version_prefix
@property
def name_prefix(self) -> Optional[str]:
"""
Name prefix for the blueprint group
:return: str
"""
return self._name_prefix
def __iter__(self): def __iter__(self):
""" """
Tun the class Blueprint Group into an Iterable item Tun the class Blueprint Group into an Iterable item

View File

@@ -21,10 +21,10 @@ from typing import (
Union, Union,
) )
from sanic_routing.exceptions import NotFound # type: ignore from sanic_routing.exceptions import NotFound
from sanic_routing.route import Route # type: ignore from sanic_routing.route import Route
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
@@ -37,7 +37,7 @@ from sanic.models.handler_types import (
if TYPE_CHECKING: if TYPE_CHECKING:
from sanic import Sanic # noqa from sanic import Sanic
def lazy(func, as_decorator=True): def lazy(func, as_decorator=True):
@@ -85,7 +85,7 @@ class Blueprint(BaseSanic):
trailing */* trailing */*
""" """
__fake_slots__ = ( __slots__ = (
"_apps", "_apps",
"_future_routes", "_future_routes",
"_future_statics", "_future_statics",
@@ -93,12 +93,13 @@ class Blueprint(BaseSanic):
"_future_listeners", "_future_listeners",
"_future_exceptions", "_future_exceptions",
"_future_signals", "_future_signals",
"_allow_route_overwrite",
"copied_from",
"ctx", "ctx",
"exceptions", "exceptions",
"host", "host",
"listeners", "listeners",
"middlewares", "middlewares",
"name",
"routes", "routes",
"statics", "statics",
"strict_slashes", "strict_slashes",
@@ -110,7 +111,7 @@ class Blueprint(BaseSanic):
def __init__( def __init__(
self, self,
name: str = None, name: str,
url_prefix: Optional[str] = None, url_prefix: Optional[str] = None,
host: Optional[Union[List[str], str]] = None, host: Optional[Union[List[str], str]] = None,
version: Optional[Union[int, str, float]] = None, version: Optional[Union[int, str, float]] = None,
@@ -119,6 +120,8 @@ class Blueprint(BaseSanic):
): ):
super().__init__(name=name) super().__init__(name=name)
self.reset() self.reset()
self._allow_route_overwrite = False
self.copied_from = ""
self.ctx = SimpleNamespace() self.ctx = SimpleNamespace()
self.host = host self.host = host
self.strict_slashes = strict_slashes self.strict_slashes = strict_slashes
@@ -168,6 +171,7 @@ class Blueprint(BaseSanic):
def reset(self): def reset(self):
self._apps: Set[Sanic] = set() self._apps: Set[Sanic] = set()
self._allow_route_overwrite = False
self.exceptions: List[RouteHandler] = [] self.exceptions: List[RouteHandler] = []
self.listeners: Dict[str, List[ListenerType[Any]]] = {} self.listeners: Dict[str, List[ListenerType[Any]]] = {}
self.middlewares: List[MiddlewareType] = [] self.middlewares: List[MiddlewareType] = []
@@ -181,6 +185,7 @@ class Blueprint(BaseSanic):
url_prefix: Optional[Union[str, Default]] = _default, url_prefix: Optional[Union[str, Default]] = _default,
version: Optional[Union[int, str, float, Default]] = _default, version: Optional[Union[int, str, float, Default]] = _default,
version_prefix: Union[str, Default] = _default, version_prefix: Union[str, Default] = _default,
allow_route_overwrite: Union[bool, Default] = _default,
strict_slashes: Optional[Union[bool, Default]] = _default, strict_slashes: Optional[Union[bool, Default]] = _default,
with_registration: bool = True, with_registration: bool = True,
with_ctx: bool = False, with_ctx: bool = False,
@@ -214,6 +219,7 @@ class Blueprint(BaseSanic):
self.reset() self.reset()
new_bp = deepcopy(self) new_bp = deepcopy(self)
new_bp.name = name new_bp.name = name
new_bp.copied_from = self.name
if not isinstance(url_prefix, Default): if not isinstance(url_prefix, Default):
new_bp.url_prefix = url_prefix new_bp.url_prefix = url_prefix
@@ -223,6 +229,8 @@ class Blueprint(BaseSanic):
new_bp.strict_slashes = strict_slashes new_bp.strict_slashes = strict_slashes
if not isinstance(version_prefix, Default): if not isinstance(version_prefix, Default):
new_bp.version_prefix = version_prefix new_bp.version_prefix = version_prefix
if not isinstance(allow_route_overwrite, Default):
new_bp._allow_route_overwrite = allow_route_overwrite
for key, value in attrs_backup.items(): for key, value in attrs_backup.items():
setattr(self, key, value) setattr(self, key, value)
@@ -248,6 +256,7 @@ class Blueprint(BaseSanic):
version: Optional[Union[int, str, float]] = None, version: Optional[Union[int, str, float]] = None,
strict_slashes: Optional[bool] = None, strict_slashes: Optional[bool] = None,
version_prefix: str = "/v", version_prefix: str = "/v",
name_prefix: Optional[str] = "",
) -> BlueprintGroup: ) -> BlueprintGroup:
""" """
Create a list of blueprints, optionally grouping them under a Create a list of blueprints, optionally grouping them under a
@@ -273,6 +282,7 @@ class Blueprint(BaseSanic):
version=version, version=version,
strict_slashes=strict_slashes, strict_slashes=strict_slashes,
version_prefix=version_prefix, version_prefix=version_prefix,
name_prefix=name_prefix,
) )
for bp in chain(blueprints): for bp in chain(blueprints):
bps.append(bp) bps.append(bp)
@@ -293,6 +303,7 @@ class Blueprint(BaseSanic):
opt_version = options.get("version", None) opt_version = options.get("version", None)
opt_strict_slashes = options.get("strict_slashes", None) opt_strict_slashes = options.get("strict_slashes", None)
opt_version_prefix = options.get("version_prefix", self.version_prefix) opt_version_prefix = options.get("version_prefix", self.version_prefix)
opt_name_prefix = options.get("name_prefix", None)
error_format = options.get( error_format = options.get(
"error_format", app.config.FALLBACK_ERROR_FORMAT "error_format", app.config.FALLBACK_ERROR_FORMAT
) )
@@ -305,11 +316,12 @@ class Blueprint(BaseSanic):
# Routes # Routes
for future in self._future_routes: for future in self._future_routes:
# attach the blueprint name to the handler so that it can be
# prefixed properly in the router
future.handler.__blueprintname__ = self.name
# Prepend the blueprint URI prefix if available # Prepend the blueprint URI prefix if available
uri = url_prefix + future.uri if url_prefix else future.uri uri = self._setup_uri(future.uri, url_prefix)
route_error_format = (
future.error_format if future.error_format else error_format
)
version_prefix = self.version_prefix version_prefix = self.version_prefix
for prefix in ( for prefix in (
@@ -327,14 +339,17 @@ class Blueprint(BaseSanic):
future.strict_slashes, opt_strict_slashes, self.strict_slashes future.strict_slashes, opt_strict_slashes, self.strict_slashes
) )
name = app._generate_name(future.name) name = future.name
if opt_name_prefix:
name = f"{opt_name_prefix}_{future.name}"
name = app._generate_name(name)
host = future.host or self.host host = future.host or self.host
if isinstance(host, list): if isinstance(host, list):
host = tuple(host) host = tuple(host)
apply_route = FutureRoute( apply_route = FutureRoute(
future.handler, future.handler,
uri[1:] if uri.startswith("//") else uri, uri,
future.methods, future.methods,
host, host,
strict_slashes, strict_slashes,
@@ -347,14 +362,27 @@ class Blueprint(BaseSanic):
future.unquote, future.unquote,
future.static, future.static,
version_prefix, version_prefix,
error_format, route_error_format,
future.route_context,
) )
if (self, apply_route) in app._future_registry: if (self, apply_route) in app._future_registry:
continue continue
registered.add(apply_route) registered.add(apply_route)
route = app._apply_route(apply_route) route = app._apply_route(
apply_route, overwrite=self._allow_route_overwrite
)
# If it is a copied BP, then make sure all of the names of routes
# matchup with the new BP name
if self.copied_from:
for r in route:
r.name = r.name.replace(self.copied_from, self.name)
r.extra.ident = r.extra.ident.replace(
self.copied_from, self.name
)
operation = ( operation = (
routes.extend if isinstance(route, list) else routes.append routes.extend if isinstance(route, list) else routes.append
) )
@@ -363,7 +391,7 @@ class Blueprint(BaseSanic):
# Static Files # Static Files
for future in self._future_statics: for future in self._future_statics:
# Prepend the blueprint URI prefix if available # Prepend the blueprint URI prefix if available
uri = url_prefix + future.uri if url_prefix else future.uri uri = self._setup_uri(future.uri, url_prefix)
apply_route = FutureStatic(uri, *future[1:]) apply_route = FutureStatic(uri, *future[1:])
if (self, apply_route) in app._future_registry: if (self, apply_route) in app._future_registry:
@@ -400,12 +428,13 @@ class Blueprint(BaseSanic):
for future in self._future_signals: for future in self._future_signals:
if (self, future) in app._future_registry: if (self, future) in app._future_registry:
continue continue
future.condition.update({"blueprint": self.name}) future.condition.update({"__blueprint__": self.name})
app._apply_signal(future) # Force exclusive to be False
app._apply_signal(tuple((*future[:-1], False)))
self.routes += [route for route in routes if isinstance(route, Route)] self.routes += [route for route in routes if isinstance(route, Route)]
self.websocket_routes += [ self.websocket_routes += [
route for route in self.routes if route.ctx.websocket route for route in self.routes if route.extra.websocket
] ]
self.middlewares += middleware self.middlewares += middleware
self.exceptions += exception_handlers self.exceptions += exception_handlers
@@ -426,7 +455,7 @@ class Blueprint(BaseSanic):
async def dispatch(self, *args, **kwargs): async def dispatch(self, *args, **kwargs):
condition = kwargs.pop("condition", {}) condition = kwargs.pop("condition", {})
condition.update({"blueprint": self.name}) condition.update({"__blueprint__": self.name})
kwargs["condition"] = condition kwargs["condition"] = condition
await asyncio.gather( await asyncio.gather(
*[app.dispatch(*args, **kwargs) for app in self.apps] *[app.dispatch(*args, **kwargs) for app in self.apps]
@@ -441,7 +470,7 @@ class Blueprint(BaseSanic):
events.add(signal.ctx.event) events.add(signal.ctx.event)
return asyncio.wait( return asyncio.wait(
[event.wait() for event in events], [asyncio.create_task(event.wait()) for event in events],
return_when=asyncio.FIRST_COMPLETED, return_when=asyncio.FIRST_COMPLETED,
timeout=timeout, timeout=timeout,
) )
@@ -455,6 +484,18 @@ class Blueprint(BaseSanic):
break break
return value return value
@staticmethod
def _setup_uri(base: str, prefix: Optional[str]):
uri = base
if prefix:
uri = prefix
if base.startswith("/") and prefix.endswith("/"):
uri += base[1:]
else:
uri += base
return uri[1:] if uri.startswith("//") else uri
@staticmethod @staticmethod
def register_futures( def register_futures(
apps: Set[Sanic], bp: Blueprint, futures: Sequence[Tuple[Any, ...]] apps: Set[Sanic], bp: Blueprint, futures: Sequence[Tuple[Any, ...]]

View File

@@ -2,21 +2,19 @@ import os
import shutil import shutil
import sys import sys
from argparse import ArgumentParser, RawTextHelpFormatter from argparse import Namespace
from importlib import import_module from functools import partial
from pathlib import Path
from textwrap import indent from textwrap import indent
from typing import Any, List, Union from typing import List, Union
from sanic.app import Sanic from sanic.app import Sanic
from sanic.application.logo import get_logo from sanic.application.logo import get_logo
from sanic.cli.arguments import Group from sanic.cli.arguments import Group
from sanic.cli.base import SanicArgumentParser, SanicHelpFormatter
from sanic.cli.inspector import make_inspector_parser
from sanic.cli.inspector_client import InspectorClient
from sanic.log import error_logger from sanic.log import error_logger
from sanic.simple import create_simple_server from sanic.worker.loader import AppLoader
class SanicArgumentParser(ArgumentParser):
...
class SanicCLI: class SanicCLI:
@@ -25,17 +23,22 @@ class SanicCLI:
{get_logo(True)} {get_logo(True)}
To start running a Sanic application, provide a path to the module, where To start running a Sanic application, provide a path to the module, where
app is a Sanic() instance: app is a Sanic() instance in the global scope:
$ sanic path.to.server:app $ sanic path.to.server:app
If the Sanic instance variable is called 'app', you can leave off the last
part, and only provide a path to the module where the instance is:
$ sanic path.to.server
Or, a path to a callable that returns a Sanic() instance: Or, a path to a callable that returns a Sanic() instance:
$ sanic path.to.factory:create_app --factory $ sanic path.to.factory:create_app
Or, a path to a directory to run as a simple HTTP server: Or, a path to a directory to run as a simple HTTP server:
$ sanic ./path/to/static --simple $ sanic ./path/to/static
""", """,
prefix=" ", prefix=" ",
) )
@@ -45,7 +48,7 @@ Or, a path to a directory to run as a simple HTTP server:
self.parser = SanicArgumentParser( self.parser = SanicArgumentParser(
prog="sanic", prog="sanic",
description=self.DESCRIPTION, description=self.DESCRIPTION,
formatter_class=lambda prog: RawTextHelpFormatter( formatter_class=lambda prog: SanicHelpFormatter(
prog, prog,
max_help_position=36 if width > 96 else 24, max_help_position=36 if width > 96 else 24,
indent_increment=4, indent_increment=4,
@@ -57,36 +60,96 @@ Or, a path to a directory to run as a simple HTTP server:
self.main_process = ( self.main_process = (
os.environ.get("SANIC_RELOADER_PROCESS", "") != "true" os.environ.get("SANIC_RELOADER_PROCESS", "") != "true"
) )
self.args: List[Any] = [] self.args: Namespace = Namespace()
self.groups: List[Group] = []
self.inspecting = False
def attach(self): def attach(self):
for group in Group._registry: if len(sys.argv) > 1 and sys.argv[1] == "inspect":
group.create(self.parser).attach() self.inspecting = True
self.parser.description = get_logo(True)
make_inspector_parser(self.parser)
return
def run(self): for group in Group._registry:
# This is to provide backwards compat -v to display version instance = group.create(self.parser)
legacy_version = len(sys.argv) == 2 and sys.argv[-1] == "-v" instance.attach()
parse_args = ["--version"] if legacy_version else None self.groups.append(instance)
def run(self, parse_args=None):
if self.inspecting:
self._inspector()
return
legacy_version = False
if not parse_args:
# This is to provide backwards compat -v to display version
legacy_version = len(sys.argv) == 2 and sys.argv[-1] == "-v"
parse_args = ["--version"] if legacy_version else None
elif parse_args == ["-v"]:
parse_args = ["--version"]
if not legacy_version:
parsed, unknown = self.parser.parse_known_args(args=parse_args)
if unknown and parsed.factory:
for arg in unknown:
if arg.startswith("--"):
self.parser.add_argument(arg.split("=")[0])
self.args = self.parser.parse_args(args=parse_args) self.args = self.parser.parse_args(args=parse_args)
self._precheck() self._precheck()
app_loader = AppLoader(
self.args.target, self.args.factory, self.args.simple, self.args
)
try: try:
app = self._get_app() app = self._get_app(app_loader)
kwargs = self._build_run_kwargs() kwargs = self._build_run_kwargs()
app.run(**kwargs) except ValueError as e:
except ValueError: error_logger.exception(f"Failed to run app: {e}")
error_logger.exception("Failed to run app") else:
for http_version in self.args.http:
app.prepare(**kwargs, version=http_version)
if self.args.single:
serve = Sanic.serve_single
else:
serve = partial(Sanic.serve, app_loader=app_loader)
serve(app)
def _inspector(self):
args = sys.argv[2:]
self.args, unknown = self.parser.parse_known_args(args=args)
if unknown:
for arg in unknown:
if arg.startswith("--"):
try:
key, value = arg.split("=")
key = key.lstrip("-")
except ValueError:
value = False if arg.startswith("--no-") else True
key = (
arg.replace("--no-", "")
.lstrip("-")
.replace("-", "_")
)
setattr(self.args, key, value)
kwargs = {**self.args.__dict__}
host = kwargs.pop("host")
port = kwargs.pop("port")
secure = kwargs.pop("secure")
raw = kwargs.pop("raw")
action = kwargs.pop("action") or "info"
api_key = kwargs.pop("api_key")
positional = kwargs.pop("positional", None)
if action == "<custom>" and positional:
action = positional[0]
if len(positional) > 1:
kwargs["args"] = positional[1:]
InspectorClient(host, port, secure, raw, api_key).do(action, **kwargs)
def _precheck(self): def _precheck(self):
if self.args.debug and self.main_process: # Custom TLS mismatch handling for better diagnostics
error_logger.warning(
"Starting in v22.3, --debug will no "
"longer automatically run the auto-reloader.\n Switch to "
"--dev to continue using that functionality."
)
# # Custom TLS mismatch handling for better diagnostics
if self.main_process and ( if self.main_process and (
# one of cert/key missing # one of cert/key missing
bool(self.args.cert) != bool(self.args.key) bool(self.args.cert) != bool(self.args.key)
@@ -107,47 +170,28 @@ Or, a path to a directory to run as a simple HTTP server:
error_logger.error(message) error_logger.error(message)
sys.exit(1) sys.exit(1)
def _get_app(self): def _get_app(self, app_loader: AppLoader):
try: try:
module_path = os.path.abspath(os.getcwd()) app = app_loader.load()
if module_path not in sys.path:
sys.path.append(module_path)
if self.args.simple:
path = Path(self.args.module)
app = create_simple_server(path)
else:
delimiter = ":" if ":" in self.args.module else "."
module_name, app_name = self.args.module.rsplit(delimiter, 1)
if app_name.endswith("()"):
self.args.factory = True
app_name = app_name[:-2]
module = import_module(module_name)
app = getattr(module, app_name, None)
if self.args.factory:
app = app()
app_type_name = type(app).__name__
if not isinstance(app, Sanic):
raise ValueError(
f"Module is not a Sanic app, it is a {app_type_name}\n"
f" Perhaps you meant {self.args.module}.app?"
)
except ImportError as e: except ImportError as e:
if module_name.startswith(e.name): if app_loader.module_name.startswith(e.name): # type: ignore
error_logger.error( error_logger.error(
f"No module named {e.name} found.\n" f"No module named {e.name} found.\n"
" Example File: project/sanic_server.py -> app\n" " Example File: project/sanic_server.py -> app\n"
" Example Module: project.sanic_server.app" " Example Module: project.sanic_server.app"
) )
error_logger.error(
"\nThe error below might have caused the above one:\n"
f"{e.msg}"
)
sys.exit(1)
else: else:
raise e raise e
return app return app
def _build_run_kwargs(self): def _build_run_kwargs(self):
for group in self.groups:
group.prepare(self.args)
ssl: Union[None, dict, str, list] = [] ssl: Union[None, dict, str, list] = []
if self.args.tlshost: if self.args.tlshost:
ssl.append(None) ssl.append(None)
@@ -160,8 +204,10 @@ Or, a path to a directory to run as a simple HTTP server:
elif len(ssl) == 1 and ssl[0] is not None: elif len(ssl) == 1 and ssl[0] is not None:
# Use only one cert, no TLSSelector. # Use only one cert, no TLSSelector.
ssl = ssl[0] ssl = ssl[0]
kwargs = { kwargs = {
"access_log": self.args.access_log, "access_log": self.args.access_log,
"coffee": self.args.coffee,
"debug": self.args.debug, "debug": self.args.debug,
"fast": self.args.fast, "fast": self.args.fast,
"host": self.args.host, "host": self.args.host,
@@ -172,18 +218,16 @@ Or, a path to a directory to run as a simple HTTP server:
"unix": self.args.unix, "unix": self.args.unix,
"verbosity": self.args.verbosity or 0, "verbosity": self.args.verbosity or 0,
"workers": self.args.workers, "workers": self.args.workers,
"auto_tls": self.args.auto_tls,
"single_process": self.args.single,
} }
if self.args.auto_reload: for maybe_arg in ("auto_reload", "dev"):
kwargs["auto_reload"] = True if getattr(self.args, maybe_arg, False):
kwargs[maybe_arg] = True
if self.args.path: if self.args.path:
if self.args.auto_reload or self.args.debug: kwargs["auto_reload"] = True
kwargs["reload_dir"] = self.args.path kwargs["reload_dir"] = self.args.path
else:
error_logger.warning(
"Ignoring '--reload-dir' since auto reloading was not "
"enabled. If you would like to watch directories for "
"changes, consider using --debug or --auto-reload."
)
return kwargs return kwargs

View File

@@ -3,9 +3,10 @@ from __future__ import annotations
from argparse import ArgumentParser, _ArgumentGroup from argparse import ArgumentParser, _ArgumentGroup
from typing import List, Optional, Type, Union from typing import List, Optional, Type, Union
from sanic_routing import __version__ as __routing_version__ # type: ignore from sanic_routing import __version__ as __routing_version__
from sanic import __version__ from sanic import __version__
from sanic.http.constants import HTTP
class Group: class Group:
@@ -29,7 +30,7 @@ class Group:
instance = cls(parser, cls.name) instance = cls(parser, cls.name)
return instance return instance
def add_bool_arguments(self, *args, **kwargs): def add_bool_arguments(self, *args, nullable=False, **kwargs):
group = self.container.add_mutually_exclusive_group() group = self.container.add_mutually_exclusive_group()
kwargs["help"] = kwargs["help"].capitalize() kwargs["help"] = kwargs["help"].capitalize()
group.add_argument(*args, action="store_true", **kwargs) group.add_argument(*args, action="store_true", **kwargs)
@@ -37,6 +38,12 @@ class Group:
group.add_argument( group.add_argument(
"--no-" + args[0][2:], *args[1:], action="store_false", **kwargs "--no-" + args[0][2:], *args[1:], action="store_false", **kwargs
) )
if nullable:
params = {args[0][2:].replace("-", "_"): None}
group.set_defaults(**params)
def prepare(self, args) -> None:
...
class GeneralGroup(Group): class GeneralGroup(Group):
@@ -50,11 +57,15 @@ class GeneralGroup(Group):
) )
self.container.add_argument( self.container.add_argument(
"module", "target",
help=( help=(
"Path to your Sanic app. Example: path.to.server:app\n" "Path to your Sanic app instance.\n"
"If running a Simple Server, path to directory to serve. " "\tExample: path.to.server:app\n"
"Example: ./\n" "If running a Simple Server, path to directory to serve.\n"
"\tExample: ./\n"
"Additionally, this can be a path to a factory function\n"
"that returns a Sanic app instance.\n"
"\tExample: path.to.server:create_app\n"
), ),
) )
@@ -63,7 +74,8 @@ class ApplicationGroup(Group):
name = "Application" name = "Application"
def attach(self): def attach(self):
self.container.add_argument( group = self.container.add_mutually_exclusive_group()
group.add_argument(
"--factory", "--factory",
action="store_true", action="store_true",
help=( help=(
@@ -71,7 +83,7 @@ class ApplicationGroup(Group):
"i.e. a () -> <Sanic app> callable" "i.e. a () -> <Sanic app> callable"
), ),
) )
self.container.add_argument( group.add_argument(
"-s", "-s",
"--simple", "--simple",
dest="simple", dest="simple",
@@ -83,6 +95,44 @@ class ApplicationGroup(Group):
) )
class HTTPVersionGroup(Group):
name = "HTTP version"
def attach(self):
http_values = [http.value for http in HTTP.__members__.values()]
self.container.add_argument(
"--http",
dest="http",
action="append",
choices=http_values,
type=int,
help=(
"Which HTTP version to use: HTTP/1.1 or HTTP/3. Value should\n"
"be either 1, or 3. [default 1]"
),
)
self.container.add_argument(
"-1",
dest="http",
action="append_const",
const=1,
help=("Run Sanic server using HTTP/1.1"),
)
self.container.add_argument(
"-3",
dest="http",
action="append_const",
const=3,
help=("Run Sanic server using HTTP/3"),
)
def prepare(self, args):
if not args.http:
args.http = [1]
args.http = tuple(sorted(set(map(HTTP, args.http)), reverse=True))
class SocketGroup(Group): class SocketGroup(Group):
name = "Socket binding" name = "Socket binding"
@@ -92,7 +142,6 @@ class SocketGroup(Group):
"--host", "--host",
dest="host", dest="host",
type=str, type=str,
default="127.0.0.1",
help="Host address [default 127.0.0.1]", help="Host address [default 127.0.0.1]",
) )
self.container.add_argument( self.container.add_argument(
@@ -100,7 +149,6 @@ class SocketGroup(Group):
"--port", "--port",
dest="port", dest="port",
type=int, type=int,
default=8000,
help="Port to serve on [default 8000]", help="Port to serve on [default 8000]",
) )
self.container.add_argument( self.container.add_argument(
@@ -167,8 +215,17 @@ class WorkerGroup(Group):
action="store_true", action="store_true",
help="Set the number of workers to max allowed", help="Set the number of workers to max allowed",
) )
group.add_argument(
"--single-process",
dest="single",
action="store_true",
help="Do not use multiprocessing, run server in a single process",
)
self.add_bool_arguments( self.add_bool_arguments(
"--access-logs", dest="access_log", help="display access logs" "--access-logs",
dest="access_log",
help="display access logs",
default=None,
) )
@@ -182,18 +239,6 @@ class DevelopmentGroup(Group):
action="store_true", action="store_true",
help="Run the server in debug mode", help="Run the server in debug mode",
) )
self.container.add_argument(
"-d",
"--dev",
dest="debug",
action="store_true",
help=(
"Currently is an alias for --debug. But starting in v22.3, \n"
"--debug will no longer automatically trigger auto_restart. \n"
"However, --dev will continue, effectively making it the \n"
"same as debug + auto_reload."
),
)
self.container.add_argument( self.container.add_argument(
"-r", "-r",
"--reload", "--reload",
@@ -212,12 +257,34 @@ class DevelopmentGroup(Group):
action="append", action="append",
help="Extra directories to watch and reload on changes", help="Extra directories to watch and reload on changes",
) )
self.container.add_argument(
"-d",
"--dev",
dest="dev",
action="store_true",
help=("debug + auto reload"),
)
self.container.add_argument(
"--auto-tls",
dest="auto_tls",
action="store_true",
help=(
"Create a temporary TLS certificate for local development "
"(requires mkcert or trustme)"
),
)
class OutputGroup(Group): class OutputGroup(Group):
name = "Output" name = "Output"
def attach(self): def attach(self):
self.add_bool_arguments(
"--coffee",
dest="coffee",
default=False,
help="Uhm, coffee?",
)
self.add_bool_arguments( self.add_bool_arguments(
"--motd", "--motd",
dest="motd", dest="motd",

35
sanic/cli/base.py Normal file
View File

@@ -0,0 +1,35 @@
from argparse import (
SUPPRESS,
Action,
ArgumentParser,
RawTextHelpFormatter,
_SubParsersAction,
)
from typing import Any
class SanicArgumentParser(ArgumentParser):
def _check_value(self, action: Action, value: Any) -> None:
if isinstance(action, SanicSubParsersAction):
return
super()._check_value(action, value)
class SanicHelpFormatter(RawTextHelpFormatter):
def add_usage(self, usage, actions, groups, prefix=None):
if not usage:
usage = SUPPRESS
# Add one linebreak, but not two
self.add_text("\x1b[1A")
super().add_usage(usage, actions, groups, prefix)
class SanicSubParsersAction(_SubParsersAction):
def __call__(self, parser, namespace, values, option_string=None):
self._name_parser_map
parser_name = values[0]
if parser_name not in self._name_parser_map:
self._name_parser_map[parser_name] = parser
values = ["<custom>", *values]
super().__call__(parser, namespace, values, option_string)

105
sanic/cli/inspector.py Normal file
View File

@@ -0,0 +1,105 @@
from argparse import ArgumentParser
from sanic.application.logo import get_logo
from sanic.cli.base import SanicHelpFormatter, SanicSubParsersAction
def _add_shared(parser: ArgumentParser) -> None:
parser.add_argument(
"--host",
"-H",
default="localhost",
help="Inspector host address [default 127.0.0.1]",
)
parser.add_argument(
"--port",
"-p",
default=6457,
type=int,
help="Inspector port [default 6457]",
)
parser.add_argument(
"--secure",
"-s",
action="store_true",
help="Whether to access the Inspector via TLS encryption",
)
parser.add_argument("--api-key", "-k", help="Inspector authentication key")
parser.add_argument(
"--raw",
action="store_true",
help="Whether to output the raw response information",
)
class InspectorSubParser(ArgumentParser):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
_add_shared(self)
if not self.description:
self.description = ""
self.description = get_logo(True) + self.description
def make_inspector_parser(parser: ArgumentParser) -> None:
_add_shared(parser)
subparsers = parser.add_subparsers(
action=SanicSubParsersAction,
dest="action",
description=(
"Run one or none of the below subcommands. Using inspect without "
"a subcommand will fetch general information about the state "
"of the application instance.\n\n"
"Or, you can optionally follow inspect with a subcommand. "
"If you have created a custom "
"Inspector instance, then you can run custom commands. See "
"https://sanic.dev/en/guide/deployment/inspector.html "
"for more details."
),
title=" Subcommands",
parser_class=InspectorSubParser,
)
reloader = subparsers.add_parser(
"reload",
help="Trigger a reload of the server workers",
formatter_class=SanicHelpFormatter,
)
reloader.add_argument(
"--zero-downtime",
action="store_true",
help=(
"Whether to wait for the new process to be online before "
"terminating the old"
),
)
subparsers.add_parser(
"shutdown",
help="Shutdown the application and all processes",
formatter_class=SanicHelpFormatter,
)
scale = subparsers.add_parser(
"scale",
help="Scale the number of workers",
formatter_class=SanicHelpFormatter,
)
scale.add_argument(
"replicas",
type=int,
help="Number of workers requested",
)
custom = subparsers.add_parser(
"<custom>",
help="Run a custom command",
description=(
"keyword arguments:\n When running a custom command, you can "
"add keyword arguments by appending them to your command\n\n"
"\tsanic inspect foo --one=1 --two=2"
),
formatter_class=SanicHelpFormatter,
)
custom.add_argument(
"positional",
nargs="*",
help="Add one or more non-keyword args to your custom command",
)

Some files were not shown because too many files have changed in this diff Show More