Compare commits
2256 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
a5a9658896 | ||
![]() |
91d7e6a77d | ||
![]() |
eb3d78f687 | ||
![]() |
d255d1aae1 | ||
![]() |
47215d4635 | ||
![]() |
38ff9069f3 | ||
![]() |
4dde4572ec | ||
![]() |
31d14704cb | ||
![]() |
6a89f4b2fe | ||
![]() |
16256522f6 | ||
![]() |
205795d1e8 | ||
![]() |
9cbe1fb8ad | ||
![]() |
31d7ba8f8c | ||
![]() |
dc3c4d1393 | ||
![]() |
929d270569 | ||
![]() |
93714df051 | ||
![]() |
6e61eab872 | ||
![]() |
6848ff24d8 | ||
![]() |
666371bb92 | ||
![]() |
4a2b82e42e | ||
![]() |
5dd1623192 | ||
![]() |
976da69e79 | ||
![]() |
11a0b15194 | ||
![]() |
c21999a248 | ||
![]() |
c17230ef94 | ||
![]() |
049983cb70 | ||
![]() |
e374409567 | ||
![]() |
4068a0d83d | ||
![]() |
70da5e9879 | ||
![]() |
f48506d620 | ||
![]() |
f2cc83c1ba | ||
![]() |
273825dab6 | ||
![]() |
9a7dafd531 | ||
![]() |
50117d174c | ||
![]() |
af67801062 | ||
![]() |
6eaab2a7e5 | ||
![]() |
d680af3709 | ||
![]() |
a8c2d77c91 | ||
![]() |
6e1c787e5d | ||
![]() |
932088e37e | ||
![]() |
1a63b9bec0 | ||
![]() |
61aa16f6ac | ||
![]() |
71cd53b64e | ||
![]() |
89188f5fc6 | ||
![]() |
a245ab3773 | ||
![]() |
ac1f56118a | ||
![]() |
53820bc241 | ||
![]() |
009954003c | ||
![]() |
8f265b8169 | ||
![]() |
5ee36fd933 | ||
![]() |
08a81c81be | ||
![]() |
5a0ed75171 | ||
![]() |
d62a92fac9 | ||
![]() |
88c918e72f | ||
![]() |
c8aab8fb3d | ||
![]() |
ecacfd396b | ||
![]() |
3c361e9852 | ||
![]() |
a5d7d03413 | ||
![]() |
259e458847 | ||
![]() |
cb49c2b26d | ||
![]() |
dfc0704831 | ||
![]() |
d238995f1b | ||
![]() |
6f5303e080 | ||
![]() |
5e7f6998bd | ||
![]() |
c7a71cd00c | ||
![]() |
9cb9e88678 | ||
![]() |
30c53b6857 | ||
![]() |
4ad8168bb0 | ||
![]() |
28f5b3c301 | ||
![]() |
c573019e7f | ||
![]() |
029f564032 | ||
![]() |
2abe66b670 | ||
![]() |
911485d52e | ||
![]() |
4744a89c33 | ||
![]() |
f7040ccec8 | ||
![]() |
518152d97e | ||
![]() |
0e44e9cacb | ||
![]() |
bfb54b0969 | ||
![]() |
154863d6c6 | ||
![]() |
a3ff0c13b7 | ||
![]() |
95ee518aec | ||
![]() |
71d3d87bcc | ||
![]() |
b276b91c21 | ||
![]() |
064168f3c8 | ||
![]() |
db39e127bf | ||
![]() |
13e9ab7ba9 | ||
![]() |
92e7463721 | ||
![]() |
8e720365c2 | ||
![]() |
d4041161c7 | ||
![]() |
f32437bf13 | ||
![]() |
0909e94527 | ||
![]() |
aef2673c38 | ||
![]() |
4c14910d5b | ||
![]() |
beae35f921 | ||
![]() |
ad4e526c77 | ||
![]() |
4422d0c34d | ||
![]() |
ad9183d21d | ||
![]() |
d70636ba2e | ||
![]() |
da23f85675 | ||
![]() |
3f4663b9f8 | ||
![]() |
65d7447cf6 | ||
![]() |
5369291c27 | ||
![]() |
1c4925edf7 | ||
![]() |
6b9edfd05c | ||
![]() |
97f33f42df | ||
![]() |
15a588a90c | ||
![]() |
82421e7efc | ||
![]() |
f891995b48 | ||
![]() |
5052321801 | ||
![]() |
23ce4eaaa4 | ||
![]() |
23a430c4ad | ||
![]() |
ec158ffa69 | ||
![]() |
6e32270036 | ||
![]() |
43ba381e7b | ||
![]() |
16503319e5 | ||
![]() |
389363ab71 | ||
![]() |
7f894c45b3 | ||
![]() |
4726cf1910 | ||
![]() |
d352a4155e | ||
![]() |
e5010286b4 | ||
![]() |
358498db96 | ||
![]() |
e4999401ab | ||
![]() |
c8df0aa2cb | ||
![]() |
5fb207176b | ||
![]() |
a12b560478 | ||
![]() |
753ee992a6 | ||
![]() |
09089b1bd3 | ||
![]() |
7ddbe5e844 | ||
![]() |
ab5a7038af | ||
![]() |
4f3c780dc3 | ||
![]() |
71f7765a4c | ||
![]() |
0392d1dcfc | ||
![]() |
7827b1b41d | ||
![]() |
8e9342e188 | ||
![]() |
2f6f2bfa76 | ||
![]() |
dee09d7fff | ||
![]() |
9cf38a0a83 | ||
![]() |
3def3d3569 | ||
![]() |
e100a14fd4 | ||
![]() |
2fa28f1711 | ||
![]() |
9d415e4ec6 | ||
![]() |
312ab298fd | ||
![]() |
2fc21ad576 | ||
![]() |
8f6c87c3d6 | ||
![]() |
4429e76532 | ||
![]() |
e4be70bae8 | ||
![]() |
13d5a44278 | ||
![]() |
aba333bfb6 | ||
![]() |
b59da498cc | ||
![]() |
70382f21ba | ||
![]() |
0e1bf89fad | ||
![]() |
6c48c8b3ba | ||
![]() |
d1c5e8003b | ||
![]() |
ce926a34f2 | ||
![]() |
a744041e38 | ||
![]() |
2f90a85df1 | ||
![]() |
a411bc06e3 | ||
![]() |
1668e1532f | ||
![]() |
b87982769f | ||
![]() |
65b53a5f3f | ||
![]() |
49789b7841 | ||
![]() |
c249004c30 | ||
![]() |
4ee2e57ec8 | ||
![]() |
86ae5f981c | ||
![]() |
2bfa65e0de | ||
![]() |
293278bb08 | ||
![]() |
5d683c6ea4 | ||
![]() |
78b6723149 | ||
![]() |
3a6cc7389c | ||
![]() |
cc97287f8e | ||
![]() |
00218aa9f2 | ||
![]() |
874718db94 | ||
![]() |
bb4474897f | ||
![]() |
0cb342aef4 | ||
![]() |
030987480c | ||
![]() |
f6fdc80b40 | ||
![]() |
361c242473 | ||
![]() |
32962d1e1c | ||
![]() |
6e0a6871b5 | ||
![]() |
0030425c8c | ||
![]() |
c9dbc8ed26 | ||
![]() |
44b108b564 | ||
![]() |
2a8e91052f | ||
![]() |
0c9df02e66 | ||
![]() |
7523e87937 | ||
![]() |
d4fb44e986 | ||
![]() |
68b654d981 | ||
![]() |
88bc6d8966 | ||
![]() |
ac388d644b | ||
![]() |
bb517ddcca | ||
![]() |
b8d991420b | ||
![]() |
4a416e177a | ||
![]() |
8dfa49b648 | ||
![]() |
8b0eaa097c | ||
![]() |
101151b419 | ||
![]() |
4669036f45 | ||
![]() |
9bf9067c99 | ||
![]() |
a7bc8b56ba | ||
![]() |
371985d129 | ||
![]() |
3eae00898d | ||
![]() |
dc3ccba527 | ||
![]() |
b91ffed010 | ||
![]() |
8c07e388cd | ||
![]() |
98ce4bdeb2 | ||
![]() |
4659069350 | ||
![]() |
080d41627a | ||
![]() |
d799c5f03c | ||
![]() |
abe062b371 | ||
![]() |
b5a00ac1ca | ||
![]() |
f282865362 | ||
![]() |
377c2ada38 | ||
![]() |
264453459e | ||
![]() |
3d383d7b97 | ||
![]() |
c0cc26021b | ||
![]() |
96c027bad5 | ||
![]() |
b2a1bc69f5 | ||
![]() |
426742b3e2 | ||
![]() |
ab35121864 | ||
![]() |
cf3c205fa5 | ||
![]() |
19f6544923 | ||
![]() |
f641830d26 | ||
![]() |
a8d55e180c | ||
![]() |
55c36e0240 | ||
![]() |
2c03eee329 | ||
![]() |
65e28b8c22 | ||
![]() |
dfd33dd63d | ||
![]() |
722a6db8d9 | ||
![]() |
9c576c74db | ||
![]() |
523db190a7 | ||
![]() |
95631b9686 | ||
![]() |
0860bfe1f1 | ||
![]() |
85e7b712b9 | ||
![]() |
b731a6b48c | ||
![]() |
cde02b5936 | ||
![]() |
abeb8d0bc0 | ||
![]() |
9a9f72ad64 | ||
![]() |
392a497366 | ||
![]() |
36e6a6c506 | ||
![]() |
a361b345ad | ||
![]() |
f5bd6e3b2f | ||
![]() |
6c7df68c7c | ||
![]() |
5b82884f8b | ||
![]() |
f0f81ec458 | ||
![]() |
71cc30e5cd | ||
![]() |
645310cff6 | ||
![]() |
2f30b5748a | ||
![]() |
5e1ef96934 | ||
![]() |
57e98b62b3 | ||
![]() |
3262878ebd | ||
![]() |
5e12edbc38 | ||
![]() |
50a606adee | ||
![]() |
f995612073 | ||
![]() |
bc08383acd | ||
![]() |
b83a1a184c | ||
![]() |
59dd6814f8 | ||
![]() |
f7abf3db1b | ||
![]() |
cf1d2148ac | ||
![]() |
b5f2bd9b0e | ||
![]() |
ba2670e99c | ||
![]() |
6ffc4d9756 | ||
![]() |
595d2c76ac | ||
![]() |
d9796e9b1e | ||
![]() |
404c5f9f9e | ||
![]() |
a937e08ef0 | ||
![]() |
ef4f058a6c | ||
![]() |
69c5dde9bf | ||
![]() |
945885d501 | ||
![]() |
9d0b54c90d | ||
![]() |
2e5c288fea | ||
![]() |
f32ef20b74 | ||
![]() |
e2eefaac55 | ||
![]() |
e1cfbf0fd9 | ||
![]() |
08c5689441 | ||
![]() |
8dbda247d6 | ||
![]() |
71a631237d | ||
![]() |
e22ff3828b | ||
![]() |
b1b12e004e | ||
![]() |
5308fec354 | ||
![]() |
0ba57d4701 | ||
![]() |
54ca6a6178 | ||
![]() |
7dd4a78cf2 | ||
![]() |
52ff49512a | ||
![]() |
5a48b94089 | ||
![]() |
ba1c73d947 | ||
![]() |
4732b6bdfa | ||
![]() |
a6e78b70ab | ||
![]() |
bb1174afc5 | ||
![]() |
df8abe9cfd | ||
![]() |
c3bca97ee1 | ||
![]() |
c3b6fa1bba | ||
![]() |
94d496afe1 | ||
![]() |
7b7a572f9b | ||
![]() |
1b8cb742f9 | ||
![]() |
3492d180a8 | ||
![]() |
021da38373 | ||
![]() |
ac784759d5 | ||
![]() |
36eda2cd62 | ||
![]() |
08a4b3013f | ||
![]() |
1dd0332e8b | ||
![]() |
a90877ac31 | ||
![]() |
8b7ea27a48 | ||
![]() |
8df80e276b | ||
![]() |
30572c972d | ||
![]() |
53da4dd091 | ||
![]() |
108a4a99c7 | ||
![]() |
7c180376d6 | ||
![]() |
f39b8b32f7 | ||
![]() |
c543d19f8a | ||
![]() |
80fca9aef7 | ||
![]() |
5bb9aa0c2c | ||
![]() |
83c746ee57 | ||
![]() |
aff6604636 | ||
![]() |
2c80571a8a | ||
![]() |
d964b552af | ||
![]() |
48f8b37b74 | ||
![]() |
141be0028d | ||
![]() |
a140c47195 | ||
![]() |
0c3a8392f2 | ||
![]() |
16875b1f41 | ||
![]() |
b1f31f2eeb | ||
![]() |
d16b9e5a02 | ||
![]() |
680484bdc8 | ||
![]() |
05cd44b5dd | ||
![]() |
ba374139f4 | ||
![]() |
72a745bfd5 | ||
![]() |
3a6fac7d59 | ||
![]() |
28ba8e53df | ||
![]() |
9b26358e63 | ||
![]() |
e21521f45c | ||
![]() |
30479765cb | ||
![]() |
53a571ec6c | ||
![]() |
ad97cac313 | ||
![]() |
1a352ddf55 | ||
![]() |
5ba43decf2 | ||
![]() |
8f06d035cb | ||
![]() |
b716f48c84 | ||
![]() |
42b1e7143e | ||
![]() |
eba7821a6d | ||
![]() |
93a0246c03 | ||
![]() |
dfd1787a49 | ||
![]() |
4998fd54c0 | ||
![]() |
7be5f0ed3d | ||
![]() |
938d2b5923 | ||
![]() |
13630a79ad | ||
![]() |
2586989eb7 | ||
![]() |
6763e2bb0a | ||
![]() |
15a8b5c894 | ||
![]() |
8a2ea626c6 | ||
![]() |
01f238de79 | ||
![]() |
7bed5a5c3c | ||
![]() |
2fea954dcf | ||
![]() |
d4660d0ca7 | ||
![]() |
824f41d6e0 | ||
![]() |
1165663ec1 | ||
![]() |
b1a57a8b62 | ||
![]() |
d76925cf35 | ||
![]() |
400f54c7ec | ||
![]() |
ec7e4390e8 | ||
![]() |
5ca8bb85cd | ||
![]() |
5c624f8b76 | ||
![]() |
2c25af8cf5 | ||
![]() |
be905e0009 | ||
![]() |
0d2d62eae5 | ||
![]() |
00a1ee0cb6 | ||
![]() |
f0d44822bc | ||
![]() |
e771e4e019 | ||
![]() |
82e5f57edd | ||
![]() |
93a697f6ea | ||
![]() |
a733d32715 | ||
![]() |
c41d7136e8 | ||
![]() |
bb65dda277 | ||
![]() |
e1e9a7cf20 | ||
![]() |
27f64ddae2 | ||
![]() |
4b968dc611 | ||
![]() |
170177feb0 | ||
![]() |
eef44ae179 | ||
![]() |
161efc67a3 | ||
![]() |
dbfc11f4c5 | ||
![]() |
e5ea4f5019 | ||
![]() |
62d0b05309 | ||
![]() |
aae8f13d9a | ||
![]() |
eac900d363 | ||
![]() |
cd0c3d3eea | ||
![]() |
b461cc338c | ||
![]() |
7c34b57e92 | ||
![]() |
c35a53fe24 | ||
![]() |
055c78b475 | ||
![]() |
a9a52ef1e9 | ||
![]() |
a760989354 | ||
![]() |
dee95fd1c0 | ||
![]() |
9c48c7d39b | ||
![]() |
fc5e3b4ede | ||
![]() |
baf96275bf | ||
![]() |
3a45d3291b | ||
![]() |
c7307c1f85 | ||
![]() |
dec46d5c4a | ||
![]() |
93313e6385 | ||
![]() |
070236677c | ||
![]() |
6b86921c7a | ||
![]() |
8f3e076661 | ||
![]() |
37f0a51063 | ||
![]() |
d6343c367f | ||
![]() |
bad9fd0258 | ||
![]() |
d402d0362e | ||
![]() |
209579f280 | ||
![]() |
9e23e75fb4 | ||
![]() |
8f8c00a99d | ||
![]() |
bb5501f4c4 | ||
![]() |
d3cbcf0e46 | ||
![]() |
15502182e3 | ||
![]() |
375319038f | ||
![]() |
4c29846cb1 | ||
![]() |
c942a5c51c | ||
![]() |
a913e712a7 | ||
![]() |
7f63ad5484 | ||
![]() |
a498c29ec9 | ||
![]() |
973c315790 | ||
![]() |
aea2e4e7f4 | ||
![]() |
55a5ab4be1 | ||
![]() |
5377a6eee3 | ||
![]() |
6057da71f3 | ||
![]() |
e91b3d40a2 | ||
![]() |
b89f9a57e0 | ||
![]() |
b850e49cb3 | ||
![]() |
6b68c3702e | ||
![]() |
5f17e9591b | ||
![]() |
64f0496d9e | ||
![]() |
0d5b2a0f69 | ||
![]() |
c08b153cee | ||
![]() |
a434ffa8b7 | ||
![]() |
967c4e6a4e | ||
![]() |
3f1e9ff528 | ||
![]() |
96cc49e31e | ||
![]() |
94e5f82a81 | ||
![]() |
ffca01ffff | ||
![]() |
0165c9c644 | ||
![]() |
7b47a4bebc | ||
![]() |
a46ea4fc59 | ||
![]() |
4358a7eefd | ||
![]() |
b958cdc151 | ||
![]() |
afb527b37c | ||
![]() |
65b76f2762 | ||
![]() |
e54685d34d | ||
![]() |
6009e6d35d | ||
![]() |
d5f09dd9d8 | ||
![]() |
ffae6f56c5 | ||
![]() |
838563c751 | ||
![]() |
83705b91c2 | ||
![]() |
5abeae8f46 | ||
![]() |
693f2a5014 | ||
![]() |
5545264cea | ||
![]() |
e04f206c50 | ||
![]() |
e9459792a4 | ||
![]() |
dadf76ce72 | ||
![]() |
33d7f4da6b | ||
![]() |
5f79291b55 | ||
![]() |
792a72bdf4 | ||
![]() |
c32e7fd678 | ||
![]() |
76ef641743 | ||
![]() |
1f0f4ef5d5 | ||
![]() |
933d005e5d | ||
![]() |
f8f215772c | ||
![]() |
a0066e5752 | ||
![]() |
976a4c764d | ||
![]() |
0d7e2f0d67 | ||
![]() |
6c03dd87b1 | ||
![]() |
0c252e7904 | ||
![]() |
8f4e0ad3c8 | ||
![]() |
95b5260b27 | ||
![]() |
2f48e81e0b | ||
![]() |
7028eae083 | ||
![]() |
574a9c27a6 | ||
![]() |
f9414f89f3 | ||
![]() |
fcdbeb7bd4 | ||
![]() |
206992fe40 | ||
![]() |
31337ba92a | ||
![]() |
150d75b7c6 | ||
![]() |
7475897a03 | ||
![]() |
449bc417a3 | ||
![]() |
262f89f2b6 | ||
![]() |
38337446cf | ||
![]() |
ac1331ea4c | ||
![]() |
2b947e831f | ||
![]() |
112715eb80 | ||
![]() |
ea9cf365bc | ||
![]() |
b9b3b4051a | ||
![]() |
ecb6db29e6 | ||
![]() |
6515dde64b | ||
![]() |
01d2a2aa3c | ||
![]() |
39e12accb8 | ||
![]() |
39fe6ea5b1 | ||
![]() |
fc4b7df088 | ||
![]() |
35f28f7a64 | ||
![]() |
614be40438 | ||
![]() |
bde0428d0c | ||
![]() |
63567c2ae4 | ||
![]() |
ec10f337b6 | ||
![]() |
d0f0e73e96 | ||
![]() |
b4fe2c8a6b | ||
![]() |
33da0771d1 | ||
![]() |
75994cd915 | ||
![]() |
c0839afdde | ||
![]() |
5961da3f57 | ||
![]() |
41f1809351 | ||
![]() |
5fbdcb62e4 | ||
![]() |
677b83e9f8 | ||
![]() |
6a5c8becac | ||
![]() |
fd23b99d60 | ||
![]() |
634b586df3 | ||
![]() |
4ca3e98082 | ||
![]() |
d18a776964 | ||
![]() |
d6b4d7d265 | ||
![]() |
33ee4c21b3 | ||
![]() |
a026cd7195 | ||
![]() |
7b1bce8d90 | ||
![]() |
217a7c5161 | ||
![]() |
2949e3422d | ||
![]() |
16ea99b0c0 | ||
![]() |
19b84ce9f0 | ||
![]() |
e5aed4c067 | ||
![]() |
9e048bc0c3 | ||
![]() |
5d7b0735ce | ||
![]() |
12521cd5b4 | ||
![]() |
7dbd3eb5e8 | ||
![]() |
96364aacc0 | ||
![]() |
fc18f86964 | ||
![]() |
fb3d368a78 | ||
![]() |
f41435fae3 | ||
![]() |
5928c50057 | ||
![]() |
1de4bcef55 | ||
![]() |
7b7559309d | ||
![]() |
066df2c142 | ||
![]() |
0c4a9b1dce | ||
![]() |
65a7060d3b | ||
![]() |
3483e7b061 | ||
![]() |
13094e02bc | ||
![]() |
ed777e9d5b | ||
![]() |
8ad80a282a | ||
![]() |
0b7eb49839 | ||
![]() |
de3b40c2e6 | ||
![]() |
efa0aaf2c2 | ||
![]() |
bd4e1cdc1e | ||
![]() |
eb8df1fc18 | ||
![]() |
9a8e49751d | ||
![]() |
58e15134fd | ||
![]() |
875be11ae5 | ||
![]() |
3f7c9ea3f5 | ||
![]() |
33aa4daac8 | ||
![]() |
58e4087d4b | ||
![]() |
0072fd1573 | ||
![]() |
5d5ed10a45 | ||
![]() |
5ee8ee7b04 | ||
![]() |
521ae7f60e | ||
![]() |
27c8c12420 | ||
![]() |
3d1f100781 | ||
![]() |
16d36fc17f | ||
![]() |
eddb5bad91 | ||
![]() |
23e1b5ee3f | ||
![]() |
9e053bef19 | ||
![]() |
cf234fca15 | ||
![]() |
050a563e1d | ||
![]() |
c347ff742e | ||
![]() |
db1c819fe1 | ||
![]() |
9f2818ee29 | ||
![]() |
26aa6d23c7 | ||
![]() |
ec7e894eb3 | ||
![]() |
71a08382d6 | ||
![]() |
09224f8676 | ||
![]() |
008b8ac394 | ||
![]() |
a357add14e | ||
![]() |
0cfd7b528b | ||
![]() |
9ba4fe05df | ||
![]() |
35786b4b74 | ||
![]() |
c7430d805a | ||
![]() |
8a3fbb555f | ||
![]() |
a62c84a954 | ||
![]() |
4aba74d050 | ||
![]() |
ab2cb88cf4 | ||
![]() |
e79ec7d7e0 | ||
![]() |
ccdb74a9a7 | ||
![]() |
7b96d633db | ||
![]() |
938c49b899 | ||
![]() |
761eef7d96 | ||
![]() |
83511a0ba7 | ||
![]() |
cf9ccdae47 | ||
![]() |
d81096fdc0 | ||
![]() |
6c8e20a859 | ||
![]() |
6239fa4f56 | ||
![]() |
1b324ae981 | ||
![]() |
bedf68a9b2 | ||
![]() |
496e87e4ba | ||
![]() |
fa4f85eb32 | ||
![]() |
1b1dfedc74 | ||
![]() |
230941ff4f | ||
![]() |
4658e0f2f3 | ||
![]() |
7c3c532dae | ||
![]() |
7c04c9a227 | ||
![]() |
44973125c1 | ||
![]() |
6aaccd1e8b | ||
![]() |
e7001b0074 | ||
![]() |
aacbd022cf | ||
![]() |
ae1874ce34 | ||
![]() |
8abba597a8 | ||
![]() |
9987893963 | ||
![]() |
638322d905 | ||
![]() |
ae40f960ff | ||
![]() |
d969fdc19f | ||
![]() |
710024125e | ||
![]() |
9a39aff803 | ||
![]() |
78e912ea45 | ||
![]() |
aa6ea5b5a0 | ||
![]() |
48800e657f | ||
![]() |
120f0262f7 | ||
![]() |
4db075ffc1 | ||
![]() |
60b4efad67 | ||
![]() |
319388d78b | ||
![]() |
ce71514d71 | ||
![]() |
7833d70d9e | ||
![]() |
16961fab9d | ||
![]() |
861e87347a | ||
![]() |
91f6abaa81 | ||
![]() |
d380b52f9a | ||
![]() |
d656a06a19 | ||
![]() |
258dbee3b9 | ||
![]() |
6b9287b076 | ||
![]() |
dac0514441 | ||
![]() |
bffdb3b5c2 | ||
![]() |
e908ca8cef | ||
![]() |
801595e24a | ||
![]() |
ba9b432993 | ||
![]() |
b565072ed9 | ||
![]() |
caa1b4d69b | ||
![]() |
865536c5c4 | ||
![]() |
784d5cce52 | ||
![]() |
0fd08c6114 | ||
![]() |
cd779b6e4f | ||
![]() |
3430907046 | ||
![]() |
2f776eba85 | ||
![]() |
b9cd2ed1f1 | ||
![]() |
850b63f642 | ||
![]() |
a9c669f17b | ||
![]() |
075affec23 | ||
![]() |
3411a12c40 | ||
![]() |
28899356c8 | ||
![]() |
2b5f8d20de | ||
![]() |
243f240e5f | ||
![]() |
0a25868a86 | ||
![]() |
fccbc1adc4 | ||
![]() |
3f6a978328 | ||
![]() |
a6077a1790 | ||
![]() |
179a07942e | ||
![]() |
c3aed01096 | ||
![]() |
028778ed56 | ||
![]() |
2d72874b0b | ||
![]() |
4c45d30400 | ||
![]() |
ecbe5c839f | ||
![]() |
ed1f367a8a | ||
![]() |
a4185a0ba7 | ||
![]() |
e81a8ce073 | ||
![]() |
e506c89304 | ||
![]() |
fcdc9c83c5 | ||
![]() |
be0d539746 | ||
![]() |
4f9739ed2c | ||
![]() |
0df37fa653 | ||
![]() |
3e932505b0 | ||
![]() |
01be691936 | ||
![]() |
134c414fe5 | ||
![]() |
c54a8b10bb | ||
![]() |
6fc3381229 | ||
![]() |
927c0e082e | ||
![]() |
7674e917e4 | ||
![]() |
e13f42c17b | ||
![]() |
b7d4121586 | ||
![]() |
fbcd4b9767 | ||
![]() |
17c5e28727 | ||
![]() |
e62b29ca44 | ||
![]() |
1e4b1c4d1a | ||
![]() |
ae91852cd5 | ||
![]() |
2011f3a0b2 | ||
![]() |
228a31ee0a | ||
![]() |
8bf2bdff74 | ||
![]() |
41862eca61 | ||
![]() |
21307b397b | ||
![]() |
3f9c94ba4a | ||
![]() |
aa270d3ac2 | ||
![]() |
a15d9552c4 | ||
![]() |
2363c0653e | ||
![]() |
651c98d19a | ||
![]() |
c1a7e0e3cd | ||
![]() |
80b32d0c71 | ||
![]() |
3842eb36fd | ||
![]() |
7c7bedfa5d | ||
![]() |
5dafa9a170 | ||
![]() |
b397637bb9 | ||
![]() |
95a0b2db2c | ||
![]() |
83864f890a | ||
![]() |
a019ff61e3 | ||
![]() |
b3ada6308b | ||
![]() |
4e50295bf0 | ||
![]() |
32eb8abb63 | ||
![]() |
84b41123f2 | ||
![]() |
23f2d33394 | ||
![]() |
97f288a534 | ||
![]() |
68d5039c5f | ||
![]() |
9d07988d75 | ||
![]() |
1eaa2e3a5f | ||
![]() |
c7f2399ded | ||
![]() |
650ab61c2e | ||
![]() |
b7df86e7dd | ||
![]() |
72b445621b | ||
![]() |
ba0e9baffa | ||
![]() |
503622438a | ||
![]() |
d5e9aae425 | ||
![]() |
a2666a2b8a | ||
![]() |
966b05b47e | ||
![]() |
78fe97b9cb | ||
![]() |
d2094fed38 | ||
![]() |
e2d65ba57c | ||
![]() |
c9d8ab4b27 | ||
![]() |
891f99d71d | ||
![]() |
3f47fa9f99 | ||
![]() |
b1c23fdbaa | ||
![]() |
62e0e5b9ec | ||
![]() |
fb61834a2e | ||
![]() |
8fbbe94fe1 | ||
![]() |
ab706dda7d | ||
![]() |
b2d4132a14 | ||
![]() |
322cf89c92 | ||
![]() |
09acd64ba1 | ||
![]() |
072fcfe03e | ||
![]() |
13079c6e30 | ||
![]() |
1b1a51c1bb | ||
![]() |
39d134994d | ||
![]() |
5f9e98554f | ||
![]() |
0d9a21718f | ||
![]() |
daf42c5f43 | ||
![]() |
3685b4de85 | ||
![]() |
2631f10c5e | ||
![]() |
f21db60859 | ||
![]() |
c15158224b | ||
![]() |
a57c14c70b | ||
![]() |
bb2bd2fe53 | ||
![]() |
aebe2b5809 | ||
![]() |
9172399b8c | ||
![]() |
22c0d97783 | ||
![]() |
3ead529693 | ||
![]() |
e36f398aa6 | ||
![]() |
18cd4caf70 | ||
![]() |
80df45ba6d | ||
![]() |
16d262e3e5 | ||
![]() |
83e3d4ca1f | ||
![]() |
1c9141bd5d | ||
![]() |
1b984422db | ||
![]() |
b6453e9fac | ||
![]() |
7b8e3624b8 | ||
![]() |
8a56da84e6 | ||
![]() |
14a00490e2 | ||
![]() |
29bf967a7e | ||
![]() |
eeb79f2587 | ||
![]() |
6d1741694d | ||
![]() |
746dccf8f9 | ||
![]() |
28a897e599 | ||
![]() |
21ebf6d777 | ||
![]() |
a2dbbb25a1 | ||
![]() |
2a64dabe82 | ||
![]() |
046ca6eaf1 | ||
![]() |
3661afa461 | ||
![]() |
12f1985375 | ||
![]() |
bb800c9db8 | ||
![]() |
262048df95 | ||
![]() |
9255eb6902 | ||
![]() |
56d386f152 | ||
![]() |
193dbe89cd | ||
![]() |
601e158ffe | ||
![]() |
42b9fa3779 | ||
![]() |
4767a67acd | ||
![]() |
4c8cc84b64 | ||
![]() |
c5efc4b64b | ||
![]() |
25e2151fdf | ||
![]() |
cb10e261a2 | ||
![]() |
bd89c7f269 | ||
![]() |
d4ef151cc4 | ||
![]() |
669cfa33df | ||
![]() |
f70ab2f68a | ||
![]() |
900020ddc9 | ||
![]() |
ec428135ce | ||
![]() |
8e2a1a61a5 | ||
![]() |
5fb8f5d3e7 | ||
![]() |
c68523150f | ||
![]() |
ae2b8f0056 | ||
![]() |
ef6d78c580 | ||
![]() |
ccd4c9615c | ||
![]() |
7d6e60ab7d | ||
![]() |
9615e37ef9 | ||
![]() |
6be12ba773 | ||
![]() |
03855d316b | ||
![]() |
9f07109616 | ||
![]() |
6a4a3f617f | ||
![]() |
f32c9be41f | ||
![]() |
d83d829e0a | ||
![]() |
99e56ef74b | ||
![]() |
df23692802 | ||
![]() |
b68a7fe7ae | ||
![]() |
5c9ba189bc | ||
![]() |
5631a31099 | ||
![]() |
f4bc0efc31 | ||
![]() |
53f45810ff | ||
![]() |
d58151a0eb | ||
![]() |
de582d2fc7 | ||
![]() |
653ac7ee14 | ||
![]() |
0b4769289a | ||
![]() |
3bedb223fc | ||
![]() |
94a1720e04 | ||
![]() |
d0c8808340 | ||
![]() |
dd32d81726 | ||
![]() |
378a732968 | ||
![]() |
b2e82685b4 | ||
![]() |
566940e052 | ||
![]() |
dab802fbf4 | ||
![]() |
7bca95205d | ||
![]() |
669e2ed5b0 | ||
![]() |
783eb1a6e8 | ||
![]() |
9b9599b12f | ||
![]() |
6ed0d3def7 | ||
![]() |
c42731a55c | ||
![]() |
abf8534ea9 | ||
![]() |
773a66bc5b | ||
![]() |
269100eac1 | ||
![]() |
2a15583b87 | ||
![]() |
b534df242b | ||
![]() |
734730640a | ||
![]() |
bee7cfa6aa | ||
![]() |
d5813152ab | ||
![]() |
eacf78b83c | ||
![]() |
348964fe12 | ||
![]() |
e5c7589fc0 | ||
![]() |
4260528645 | ||
![]() |
34fe26e51b | ||
![]() |
8a59907319 | ||
![]() |
52deebaf65 | ||
![]() |
1e05b22fbc | ||
![]() |
ab56af5d15 | ||
![]() |
123f00eee6 | ||
![]() |
42bf103269 | ||
![]() |
c8d2a462e3 | ||
![]() |
08794ae1cf | ||
![]() |
4f70dba935 | ||
![]() |
b926a2c9b0 | ||
![]() |
52bdd1d5a2 | ||
![]() |
bc7d0f0da5 | ||
![]() |
6a8e9c9e95 | ||
![]() |
211a922f3c | ||
![]() |
2758a3ade6 | ||
![]() |
ef3c9eae73 | ||
![]() |
9cf2e1b519 | ||
![]() |
51c2f7a599 | ||
![]() |
5bdd046b11 | ||
![]() |
95526a82de | ||
![]() |
af7ad0a621 | ||
![]() |
1473753d43 | ||
![]() |
b36bd21813 | ||
![]() |
f8f0241c27 | ||
![]() |
1af16836d4 | ||
![]() |
757974714e | ||
![]() |
eed22a7a24 | ||
![]() |
0242bc999f | ||
![]() |
b89c533818 | ||
![]() |
2cb05ab865 | ||
![]() |
391639210e | ||
![]() |
99f34c9f50 | ||
![]() |
d418cc9950 | ||
![]() |
6dfafb0787 | ||
![]() |
7067295e67 | ||
![]() |
2af229eb1a | ||
![]() |
8dd8e9916e | ||
![]() |
96af1fe7cf | ||
![]() |
cb3a03356b | ||
![]() |
68aa2ae3ce | ||
![]() |
52de354e24 | ||
![]() |
f4f90cada4 | ||
![]() |
62420e0f40 | ||
![]() |
102e651741 | ||
![]() |
65daaaf64b | ||
![]() |
b7a6f36e95 | ||
![]() |
a86a10b128 | ||
![]() |
0b728ade3a | ||
![]() |
74f05108d7 | ||
![]() |
9d4d15ddc7 | ||
![]() |
0c5c6dff8f | ||
![]() |
391fcdc83d | ||
![]() |
d76d5e2c5f | ||
![]() |
f0ada573bb | ||
![]() |
ec5b790b51 | ||
![]() |
613b23748d | ||
![]() |
cea1547e08 | ||
![]() |
fd5ae01e1d | ||
![]() |
9b6b93d467 | ||
![]() |
ca179c12a1 | ||
![]() |
4d527035ae | ||
![]() |
19b42830ea | ||
![]() |
f5162f8ab1 | ||
![]() |
ff38a3c6b6 | ||
![]() |
94e85686b5 | ||
![]() |
aea4a8ed33 | ||
![]() |
05dd3b2e9d | ||
![]() |
040468755c | ||
![]() |
50b359fdb2 | ||
![]() |
72f2e18a84 | ||
![]() |
b36dc22b45 | ||
![]() |
15b1c875f5 | ||
![]() |
13804dc380 | ||
![]() |
9bea23da29 | ||
![]() |
7005fabd4d | ||
![]() |
de8c37ad00 | ||
![]() |
a80499c4b7 | ||
![]() |
82f7f847ba | ||
![]() |
4880761fe0 | ||
![]() |
87ab0b386d | ||
![]() |
c42c274002 | ||
![]() |
2d82b8951f | ||
![]() |
b7702bc3e8 | ||
![]() |
9c6b83501f | ||
![]() |
5189d8b14c | ||
![]() |
e13053ed89 | ||
![]() |
efa77cf5ec | ||
![]() |
f6355bd075 | ||
![]() |
e3dfce88ff | ||
![]() |
939b5ea095 | ||
![]() |
e6fba01682 | ||
![]() |
1623d397be | ||
![]() |
09678d601d | ||
![]() |
67d51f7e1b | ||
![]() |
aa7f2759a6 | ||
![]() |
9b9dd67797 | ||
![]() |
3f73bc075a | ||
![]() |
f2a55d01ea | ||
![]() |
56989a017b | ||
![]() |
bf029c1b9d | ||
![]() |
ada5918bc8 | ||
![]() |
375ebd39f0 | ||
![]() |
a33ebbaf11 | ||
![]() |
19b304b0fc | ||
![]() |
0b64fe6746 | ||
![]() |
e978121d58 | ||
![]() |
4efd450b32 | ||
![]() |
d2670664ba | ||
![]() |
fa7405fe9c | ||
![]() |
33297f48a5 | ||
![]() |
956793e066 | ||
![]() |
1bfbc67c62 | ||
![]() |
b5287184e9 | ||
![]() |
7c9957e058 | ||
![]() |
fca7cb9fb0 | ||
![]() |
268d254d85 | ||
![]() |
181adebf82 | ||
![]() |
06297a1918 | ||
![]() |
aa0874b6d8 | ||
![]() |
822ced6294 | ||
![]() |
1a59614f79 | ||
![]() |
f2d528e52a | ||
![]() |
f7adc5f84c | ||
![]() |
e955e833c4 | ||
![]() |
096c44b910 | ||
![]() |
efb9a42045 | ||
![]() |
296cda7801 | ||
![]() |
90b9d73244 | ||
![]() |
c8b0e7f2a7 | ||
![]() |
6ce88ab5a4 | ||
![]() |
e13ab805df | ||
![]() |
e58ea8c7b4 | ||
![]() |
dd5bac61cb | ||
![]() |
6270b27a97 | ||
![]() |
f89ba1d39f | ||
![]() |
8b5d137d8f | ||
![]() |
2629fab649 | ||
![]() |
92cd10c6a8 | ||
![]() |
cc3edb90dc | ||
![]() |
c60ba81984 | ||
![]() |
ece3cdaa2e | ||
![]() |
4cb40f2042 | ||
![]() |
0e9f350982 | ||
![]() |
cf439f01f8 | ||
![]() |
f1f1b8a630 | ||
![]() |
d4d1df03c9 | ||
![]() |
92b73a6f4f | ||
![]() |
b63c06c75a | ||
![]() |
3e3bce422e | ||
![]() |
e3a27c2cc4 | ||
![]() |
f13f451084 | ||
![]() |
df0e3de911 | ||
![]() |
8466be8728 | ||
![]() |
5cf2144b3f | ||
![]() |
7c182f63c8 | ||
![]() |
056180782c | ||
![]() |
ff0d5870e9 | ||
![]() |
b70176f8c7 | ||
![]() |
e3655b525d | ||
![]() |
e63d0091af | ||
![]() |
7b0af2d80d | ||
![]() |
7d79a86d4d | ||
![]() |
ba46aff069 | ||
![]() |
7a65471ba5 | ||
![]() |
c7c46da975 | ||
![]() |
c708e8425f | ||
![]() |
905c51bef0 | ||
![]() |
bd87098b7e | ||
![]() |
5f486cc25f | ||
![]() |
f79fb72a33 | ||
![]() |
0505aa2dda | ||
![]() |
485ff32e42 | ||
![]() |
5ead67972f | ||
![]() |
9c860dbff3 | ||
![]() |
a20ad99638 | ||
![]() |
8ef7bf8e7b | ||
![]() |
0d5be1969a | ||
![]() |
d06ea9bfc3 | ||
![]() |
57e79882e1 | ||
![]() |
20d1ab60c7 | ||
![]() |
277c2ce2d2 | ||
![]() |
34e51f01d1 | ||
![]() |
f4b4e3a58c | ||
![]() |
def2e033c8 | ||
![]() |
dfec18278b | ||
![]() |
cd5bdecda3 | ||
![]() |
9b6217ba41 | ||
![]() |
272f6e195d | ||
![]() |
aa9bf04dfe | ||
![]() |
9ae6dfb6d2 | ||
![]() |
619bb79a2f | ||
![]() |
0cad831eca | ||
![]() |
f15a7fb588 | ||
![]() |
1bdf9ca057 | ||
![]() |
c8c370b784 | ||
![]() |
63182f55f7 | ||
![]() |
41759248e2 | ||
![]() |
3149d5a66d | ||
![]() |
8b13597099 | ||
![]() |
36032cc26e | ||
![]() |
4cb107aedc | ||
![]() |
176f8d1981 | ||
![]() |
9a26030bd5 | ||
![]() |
6778f4d9e0 | ||
![]() |
fd61b9e3e2 | ||
![]() |
298d5cdf24 | ||
![]() |
1bf1c9d006 | ||
![]() |
7dc62be5cf | ||
![]() |
be580a6a5b | ||
![]() |
8ce519668b | ||
![]() |
801258c46a | ||
![]() |
32a1db3622 | ||
![]() |
ed1f3daacc | ||
![]() |
b7d74c82ba | ||
![]() |
c3b31a6fb0 | ||
![]() |
f4c55bbc07 | ||
![]() |
a16842f7bc | ||
![]() |
439a38664f | ||
![]() |
5cc12fd945 | ||
![]() |
fe116fff5a | ||
![]() |
06aaaf4727 | ||
![]() |
6deb9b49b2 | ||
![]() |
d59e92d3e5 | ||
![]() |
cc83c1f0cf | ||
![]() |
1fe7306af8 | ||
![]() |
c796d73fc3 | ||
![]() |
eb93f884f3 | ||
![]() |
3673feb256 | ||
![]() |
7c9c783e9d | ||
![]() |
74a4b9efaa | ||
![]() |
4466e8cce1 | ||
![]() |
b689037984 | ||
![]() |
db1ba21d88 | ||
![]() |
50d270ef7c | ||
![]() |
d1a578b555 | ||
![]() |
76e9859cf8 | ||
![]() |
add9d363c5 | ||
![]() |
1498baab0f | ||
![]() |
df7f63d45d | ||
![]() |
f7425126a1 | ||
![]() |
790047e450 | ||
![]() |
9198b5b0be | ||
![]() |
d534acb79d | ||
![]() |
d100f54551 | ||
![]() |
7a9e100b0f | ||
![]() |
fafe23d7c2 | ||
![]() |
9a08bdae4a | ||
![]() |
bcc11fa7fe | ||
![]() |
7d0c0fdf7c | ||
![]() |
0e33d46ead | ||
![]() |
efbacc17cf | ||
![]() |
bd6dbd9090 | ||
![]() |
076cf51fb2 | ||
![]() |
f8a6af1e28 | ||
![]() |
96912f436d | ||
![]() |
f0e162442f | ||
![]() |
04b8dd989f | ||
![]() |
5851c8bd91 | ||
![]() |
78efcf93f8 | ||
![]() |
bb35bc3898 | ||
![]() |
f38783bdef | ||
![]() |
d8f9986089 | ||
![]() |
3e616b599a | ||
![]() |
d38fc17191 | ||
![]() |
7ae0eb0dc3 | ||
![]() |
9082eb56a7 | ||
![]() |
c578974246 | ||
![]() |
fec81ffe73 | ||
![]() |
30e6a310f1 | ||
![]() |
a87934d434 | ||
![]() |
b398c1fe72 | ||
![]() |
6f813f940e | ||
![]() |
d52498b787 | ||
![]() |
79e35bbdf6 | ||
![]() |
1814ff05f4 | ||
![]() |
ec226e33cb | ||
![]() |
6abdf9f9c1 | ||
![]() |
212da1029e | ||
![]() |
afea15e4a7 | ||
![]() |
39ff02b6e4 | ||
![]() |
b238be54a4 | ||
![]() |
377c9890a3 | ||
![]() |
599834b0e1 | ||
![]() |
a39a7ca9d5 | ||
![]() |
cd22745e6b | ||
![]() |
334649dfd4 | ||
![]() |
becbc5f9ef | ||
![]() |
a7dd73c657 | ||
![]() |
f9b29fd7e7 | ||
![]() |
f770e16f6d | ||
![]() |
9092ee9f0e | ||
![]() |
01257f65a6 | ||
![]() |
c1222175b3 | ||
![]() |
5ff481952d | ||
![]() |
baa689ad43 | ||
![]() |
2f30f4f69f | ||
![]() |
202a4c6525 | ||
![]() |
7928b9b3a2 | ||
![]() |
e1c9020268 | ||
![]() |
04a12b436e | ||
![]() |
818a8c2196 | ||
![]() |
b6715464fd | ||
![]() |
8f2d543d9f | ||
![]() |
6cf320bedb | ||
![]() |
a850ce5086 | ||
![]() |
ef3bdf5408 | ||
![]() |
94b9bc7950 | ||
![]() |
8a07463a67 | ||
![]() |
2995b23929 | ||
![]() |
eb4276373b | ||
![]() |
79df52e519 | ||
![]() |
3dfb31b1b9 | ||
![]() |
c4c4ed70d9 | ||
![]() |
45422df1b7 | ||
![]() |
e0b7624414 | ||
![]() |
b0ecb3170f | ||
![]() |
fc8b5f378a | ||
![]() |
d42cb7ddb3 | ||
![]() |
6454ac0944 | ||
![]() |
31cf83f10b | ||
![]() |
cc84005593 | ||
![]() |
915d2732a1 | ||
![]() |
44bc47361e | ||
![]() |
3619b07843 | ||
![]() |
ad3f588c79 | ||
![]() |
a2fc37121b | ||
![]() |
7f36d20123 | ||
![]() |
d1a8e8b042 | ||
![]() |
c39ddd00d3 | ||
![]() |
d55e453bd5 | ||
![]() |
bffed27bdb | ||
![]() |
fffcb158f1 | ||
![]() |
eca98a54eb | ||
![]() |
46ed2c5270 | ||
![]() |
23ea0b7ec9 | ||
![]() |
ef26cb283b | ||
![]() |
b8bb77eff6 | ||
![]() |
9c75ad3de1 | ||
![]() |
0b38dea613 | ||
![]() |
7e4a9e3bc2 | ||
![]() |
36f12c822f | ||
![]() |
0cbea0f5d3 | ||
![]() |
e735fe54c3 | ||
![]() |
e911e2e1df | ||
![]() |
1d75f6c2be | ||
![]() |
ad8a168469 | ||
![]() |
74fc502089 | ||
![]() |
dfc2166d8b | ||
![]() |
2b70346db4 | ||
![]() |
090df6f224 | ||
![]() |
745a1d6e94 | ||
![]() |
0fe0796870 | ||
![]() |
224b56bd3a | ||
![]() |
571b5b544d | ||
![]() |
220b40f7f4 | ||
![]() |
60774c5a49 | ||
![]() |
6d37ef7256 | ||
![]() |
e083224df1 | ||
![]() |
5ef567405f | ||
![]() |
ea2521f430 | ||
![]() |
82cb182fe7 | ||
![]() |
3fe31ff551 | ||
![]() |
48d45f1ca4 | ||
![]() |
ddf2a604d1 | ||
![]() |
8b920d9d56 | ||
![]() |
f2c0489452 | ||
![]() |
f5a2d19199 | ||
![]() |
86fed12d91 | ||
![]() |
7ca3ad5d4c | ||
![]() |
1eecffce97 | ||
![]() |
5c341a2b00 | ||
![]() |
0ab64e9803 | ||
![]() |
27108334f1 | ||
![]() |
788253cbe8 | ||
![]() |
4b6e89a526 | ||
![]() |
68fd1b66b5 | ||
![]() |
5806666949 | ||
![]() |
a76d8108fe | ||
![]() |
2135294e2e | ||
![]() |
ed1c563d1f | ||
![]() |
74efa3a108 | ||
![]() |
49c29e6862 | ||
![]() |
17d7f24825 | ||
![]() |
f23c3da4ff | ||
![]() |
cabcf50fbe | ||
![]() |
8b23b5d389 | ||
![]() |
37eb2c1db6 | ||
![]() |
9751a37343 | ||
![]() |
ed3bdd3443 | ||
![]() |
285ad9bdc1 | ||
![]() |
16f5914c90 | ||
![]() |
72d56a89a2 | ||
![]() |
1135c8c1b1 | ||
![]() |
ec4339bd47 | ||
![]() |
6c0fbef843 | ||
![]() |
040c85a43b | ||
![]() |
420554c737 | ||
![]() |
f20b854dd2 | ||
![]() |
3844cec7a4 | ||
![]() |
0db49f7520 | ||
![]() |
f8dedcaa1e | ||
![]() |
f8b1122467 | ||
![]() |
f3bf5e9a5c | ||
![]() |
ba1dbacd35 | ||
![]() |
4036f1c121 | ||
![]() |
22ad697d1f | ||
![]() |
a10d7469cd | ||
![]() |
06c3153d22 | ||
![]() |
9677158b75 | ||
![]() |
226a73141b | ||
![]() |
da3201bf35 | ||
![]() |
7daebc6aea | ||
![]() |
d9002769cf | ||
![]() |
6d0b30953a | ||
![]() |
09d6452475 | ||
![]() |
6a61fce84e | ||
![]() |
11017902be | ||
![]() |
bd7333723e | ||
![]() |
6648250fb9 | ||
![]() |
a94a2d46d0 | ||
![]() |
ab97018c78 | ||
![]() |
be702b0924 | ||
![]() |
c5c10cfb50 | ||
![]() |
5682d642a6 | ||
![]() |
62c6d7274c | ||
![]() |
4f8633375d | ||
![]() |
9f559818e5 | ||
![]() |
5f329f72ee | ||
![]() |
7303a06f83 | ||
![]() |
e34de96b24 | ||
![]() |
42cd424274 | ||
![]() |
9426e94314 | ||
![]() |
7a1dab3319 | ||
![]() |
d63ec84745 | ||
![]() |
0e7e2f4e5b | ||
![]() |
46521240a9 | ||
![]() |
a8827a5d95 | ||
![]() |
ca0bc1cb7d | ||
![]() |
8c28ce7d79 | ||
![]() |
5b051f0891 | ||
![]() |
c93de9450a | ||
![]() |
96976fa892 | ||
![]() |
c14e99cef0 | ||
![]() |
c91a806774 | ||
![]() |
2f0076f429 | ||
![]() |
a1ffc6d55b | ||
![]() |
9bdf7a9980 | ||
![]() |
81494453b0 | ||
![]() |
008cbe5ce7 | ||
![]() |
5ee35e7eeb | ||
![]() |
1a98e70281 | ||
![]() |
8e3f3977bd | ||
![]() |
04b04f094c | ||
![]() |
9c02cdbad9 | ||
![]() |
c30e805623 | ||
![]() |
cd81538ce3 | ||
![]() |
68cb280513 | ||
![]() |
19466a15b4 | ||
![]() |
d54b406cba | ||
![]() |
72254a7af9 | ||
![]() |
52feff266e | ||
![]() |
2c3f50e34a | ||
![]() |
2b0258c13a | ||
![]() |
2585900692 | ||
![]() |
48c2dcb110 | ||
![]() |
10a378bd46 | ||
![]() |
3fe3c2c79f | ||
![]() |
52c2a8484e | ||
![]() |
21435c1863 | ||
![]() |
1ea3ab7fe8 | ||
![]() |
1b0ad2c3cd | ||
![]() |
aa4821864a | ||
![]() |
283762224c | ||
![]() |
f50a37fc88 | ||
![]() |
076f0515ca | ||
![]() |
049f12096d | ||
![]() |
f09c0393ba | ||
![]() |
472bbcf293 | ||
![]() |
7a3f9daccf | ||
![]() |
76511d61e0 | ||
![]() |
8e7475ccf6 | ||
![]() |
820d8c7bf5 | ||
![]() |
cfc75b4f1a | ||
![]() |
98567fe5a8 | ||
![]() |
05bb812e2b | ||
![]() |
c9876a6c88 | ||
![]() |
979b5a52d3 | ||
![]() |
e70535e8d7 | ||
![]() |
ed8725bf6c | ||
![]() |
098cd70e82 | ||
![]() |
969dac2033 | ||
![]() |
49b1d667f1 | ||
![]() |
bca1e08411 | ||
![]() |
bf6ed217c2 | ||
![]() |
bb8e9c6438 | ||
![]() |
f128ed5b1f | ||
![]() |
ff5786d61b | ||
![]() |
ca596c8ecd | ||
![]() |
c3bcafb514 | ||
![]() |
a9c7d95e9b | ||
![]() |
63bbcb5152 | ||
![]() |
01042c1d98 | ||
![]() |
5bf722c7ae | ||
![]() |
c2191153cf | ||
![]() |
5bcbc5a337 | ||
![]() |
f721f90add | ||
![]() |
0e92d8ce2c | ||
![]() |
727d6a1b61 | ||
![]() |
666c0847b7 | ||
![]() |
0a411f9bba | ||
![]() |
49f3ba39f9 | ||
![]() |
794128a053 | ||
![]() |
e6be3b2313 | ||
![]() |
9150767574 | ||
![]() |
75f2180cb1 | ||
![]() |
c5cdcf0f95 | ||
![]() |
ea5b07f636 | ||
![]() |
477e6b8663 | ||
![]() |
a0d8418b40 | ||
![]() |
006fb08024 | ||
![]() |
4578f6016b | ||
![]() |
5b06bcc57d | ||
![]() |
d4bb14a511 | ||
![]() |
6d2f5da506 | ||
![]() |
c96df86111 | ||
![]() |
86f87cf4ac | ||
![]() |
770a8fb288 | ||
![]() |
c4e3a98ea7 | ||
![]() |
07e95dba4f | ||
![]() |
9bc1abcd00 | ||
![]() |
4d515b05f3 | ||
![]() |
64edf7ad9c | ||
![]() |
7610c0fb2e | ||
![]() |
0189e4ed59 | ||
![]() |
8018c9b91d | ||
![]() |
4b3920daba | ||
![]() |
d876e3ed5c | ||
![]() |
086b5daa53 | ||
![]() |
4b877e3f6b | ||
![]() |
8ce749e339 | ||
![]() |
752ddfa7fc | ||
![]() |
8700c96c4d | ||
![]() |
e3852ceeca | ||
![]() |
225ea49b6f | ||
![]() |
15fd49037f | ||
![]() |
2fb4697e12 | ||
![]() |
1a9f770317 | ||
![]() |
62871ec9b3 | ||
![]() |
39c64214ee | ||
![]() |
9aec5febb8 | ||
![]() |
91b2167eba | ||
![]() |
00d40a35cd | ||
![]() |
f96ab02767 | ||
![]() |
4ce699e57f | ||
![]() |
4ee042c330 | ||
![]() |
0b23f4ff81 | ||
![]() |
5cef1634ed | ||
![]() |
1b0286916e | ||
![]() |
a8f764c161 | ||
![]() |
1d719252cb | ||
![]() |
d8cebe1188 | ||
![]() |
329ebf6a5d | ||
![]() |
c836441a75 | ||
![]() |
074d36eeba | ||
![]() |
f6eb35f67d | ||
![]() |
77f70a0792 | ||
![]() |
12dafd07b8 | ||
![]() |
9fb8bec715 | ||
![]() |
eb1146c6b6 | ||
![]() |
730f7c5e41 | ||
![]() |
5cabc9cff2 | ||
![]() |
ddc039ed2e | ||
![]() |
a146ebd856 | ||
![]() |
5ee7b6caeb | ||
![]() |
9c4b0f7b15 | ||
![]() |
2e5d1ddff9 | ||
![]() |
24bdb1ce98 | ||
![]() |
8eb59ad4dc | ||
![]() |
d8c8ccd180 | ||
![]() |
a46e004f07 | ||
![]() |
173f94216a | ||
![]() |
1a74accd65 | ||
![]() |
2979e03148 | ||
![]() |
4bdb9a2c8e | ||
![]() |
8f6fa5e9ff | ||
![]() |
986135ff76 | ||
![]() |
c9cbc00e36 | ||
![]() |
c9a40c180a | ||
![]() |
125cb17fcb | ||
![]() |
53a5bd2319 | ||
![]() |
4fd68f9af3 | ||
![]() |
c4417b399b | ||
![]() |
c2a3e42a53 | ||
![]() |
73c04f5a89 | ||
![]() |
195f707f14 | ||
![]() |
bc20dc5c62 | ||
![]() |
8b4ca51805 | ||
![]() |
e2e25eb751 | ||
![]() |
9572ecc5ea | ||
![]() |
97d8b9e908 | ||
![]() |
c59a8a60eb | ||
![]() |
158da0927a | ||
![]() |
78a7338346 | ||
![]() |
90e5c8d39b | ||
![]() |
7a6f2d8336 | ||
![]() |
f49554aa57 | ||
![]() |
0a72168f8f | ||
![]() |
5011bfef55 | ||
![]() |
6038813d03 | ||
![]() |
fee9de96de | ||
![]() |
35e028cd99 | ||
![]() |
145cdd5c1b | ||
![]() |
762b2782ee | ||
![]() |
91f031b661 | ||
![]() |
eab809d410 | ||
![]() |
826f1b4713 | ||
![]() |
fa1a95ae91 | ||
![]() |
63babae63d | ||
![]() |
db9924a399 | ||
![]() |
5d23c7644b | ||
![]() |
ef81a9f547 | ||
![]() |
747c21da70 | ||
![]() |
439ff11d13 | ||
![]() |
947364e15f | ||
![]() |
750115b727 | ||
![]() |
a55efc832d | ||
![]() |
c96bd21389 | ||
![]() |
dd241bd6fa | ||
![]() |
0dbde7400f | ||
![]() |
2587f6753d | ||
![]() |
4155e76a81 | ||
![]() |
756bd19181 | ||
![]() |
fbb2344895 | ||
![]() |
bda6c85638 | ||
![]() |
df4a149cd0 | ||
![]() |
80f27b1db9 | ||
![]() |
d5d1d3b45a | ||
![]() |
c797c3f22d | ||
![]() |
375ed23216 | ||
![]() |
7b66a56cad | ||
![]() |
7216bf7835 | ||
![]() |
8b24c35ac7 | ||
![]() |
f80a6ae228 | ||
![]() |
9b3fbe4593 | ||
![]() |
f99a723627 | ||
![]() |
181ffb00a7 | ||
![]() |
222eca64d7 | ||
![]() |
1b687f3feb | ||
![]() |
2228104bff | ||
![]() |
402c3752c4 | ||
![]() |
69a8bb5e1f | ||
![]() |
0d76f9e030 | ||
![]() |
eb8f65c58b | ||
![]() |
e0e27a671e | ||
![]() |
b65eb69d9f | ||
![]() |
8118e542fb | ||
![]() |
c866759bd4 | ||
![]() |
429f7377cb | ||
![]() |
40776e5324 | ||
![]() |
621343112d | ||
![]() |
eb06e6ba51 | ||
![]() |
da91b16244 | ||
![]() |
918e2ba8d0 | ||
![]() |
f50dc83829 | ||
![]() |
e8a9b4743b | ||
![]() |
f34226425e | ||
![]() |
e27c7ba36f | ||
![]() |
1aad527956 | ||
![]() |
173c62acb6 | ||
![]() |
76605d7dfe | ||
![]() |
32be1a6496 | ||
![]() |
c7d43aa544 | ||
![]() |
198bf55b7b | ||
![]() |
75378d3567 | ||
![]() |
55cb371569 | ||
![]() |
5bb97d25d0 | ||
![]() |
b2017cae77 | ||
![]() |
35af903d4a | ||
![]() |
426e00b6f4 | ||
![]() |
8e62b3e438 | ||
![]() |
4265ad5f23 | ||
![]() |
c181eb0539 | ||
![]() |
e0f06753c6 | ||
![]() |
8aafd72ef0 | ||
![]() |
48549ce97b | ||
![]() |
47abf83960 | ||
![]() |
be0f3731b4 | ||
![]() |
b755431b93 | ||
![]() |
04ff393875 | ||
![]() |
7841274300 | ||
![]() |
235687d983 | ||
![]() |
3d75e6ed95 | ||
![]() |
eb9af8bceb | ||
![]() |
39ea434513 | ||
![]() |
f0a956467c | ||
![]() |
e48bd08095 | ||
![]() |
5d00717f39 | ||
![]() |
3fff685c44 | ||
![]() |
1e75265eed | ||
![]() |
b6ac3ef445 | ||
![]() |
421f78f3e6 | ||
![]() |
b71fdcfc20 | ||
![]() |
021e9b228a | ||
![]() |
00d4533022 | ||
![]() |
fd5faeb5dd | ||
![]() |
e7c8035ed7 | ||
![]() |
e427e38da8 | ||
![]() |
1f24abc3d2 | ||
![]() |
76e62779ba | ||
![]() |
1af343ef50 | ||
![]() |
412ffd1592 | ||
![]() |
b141fec573 | ||
![]() |
d2e14abfd5 | ||
![]() |
d4abca0480 | ||
![]() |
529f5822ee | ||
![]() |
395d85a12f | ||
![]() |
4379a4b067 | ||
![]() |
ad8e1cbf62 | ||
![]() |
dc5a70b0de | ||
![]() |
b5d1f52ea4 | ||
![]() |
221cf235b5 | ||
![]() |
7720e31a31 | ||
![]() |
d812affef0 | ||
![]() |
5c19eb34bf | ||
![]() |
e18ebaee3d | ||
![]() |
dbcbf12456 | ||
![]() |
c04b44057c | ||
![]() |
60aa60f48e | ||
![]() |
2848d7c80e | ||
![]() |
9fcdacb624 | ||
![]() |
cf1713b085 | ||
![]() |
f049a4ca67 | ||
![]() |
55f860da2f | ||
![]() |
b5369e611c | ||
![]() |
3d1dd1c6ac | ||
![]() |
10a363b275 | ||
![]() |
d865c5e2b6 | ||
![]() |
9fac37588c | ||
![]() |
aac0d58417 | ||
![]() |
b37e6187d4 | ||
![]() |
20138ee85f | ||
![]() |
6dc569cde5 | ||
![]() |
77cf0b678a | ||
![]() |
2dfb061063 | ||
![]() |
e4669e2581 | ||
![]() |
df47cf72d3 | ||
![]() |
ba1b34e375 | ||
![]() |
950b5ee529 | ||
![]() |
041c48de19 | ||
![]() |
599fbcee6e | ||
![]() |
ce2df8030c | ||
![]() |
47e761bbe2 | ||
![]() |
0646baa18d | ||
![]() |
38997c1b47 | ||
![]() |
acaafabc23 | ||
![]() |
6a80bdafa6 | ||
![]() |
cf30ed745c | ||
![]() |
a399fb4044 | ||
![]() |
24b946e850 | ||
![]() |
236daf48ff | ||
![]() |
4942af27dc | ||
![]() |
3adb90071b | ||
![]() |
29b4a2a08c | ||
![]() |
e1331fc0a2 | ||
![]() |
3802f8ff65 | ||
![]() |
4b0abdbe7c | ||
![]() |
81889fd7a3 | ||
![]() |
aac99c45c0 | ||
![]() |
566a6369a5 | ||
![]() |
4fdf340d04 | ||
![]() |
ddd7145153 | ||
![]() |
3f22b644b6 | ||
![]() |
639c9f579d | ||
![]() |
735b8665f1 | ||
![]() |
199fa50a9d | ||
![]() |
aac5ad8504 | ||
![]() |
349c108ebc | ||
![]() |
3b464782ef | ||
![]() |
3d97fd8d2a | ||
![]() |
c102e76146 | ||
![]() |
beee7b68bf | ||
![]() |
f47e571d92 | ||
![]() |
4b5320a8f0 | ||
![]() |
30c2c89c6b | ||
![]() |
4a1d1a0dc1 | ||
![]() |
360adc9130 | ||
![]() |
cc21abe843 | ||
![]() |
9a27555763 | ||
![]() |
aaef2fbd01 | ||
![]() |
5bb640ca17 | ||
![]() |
0e5c7a62cb | ||
![]() |
1b33e05f74 | ||
![]() |
53a04309ff | ||
![]() |
dc411651b6 | ||
![]() |
514540b90b | ||
![]() |
a5249d1f5d | ||
![]() |
21aa3f6578 | ||
![]() |
0024edbbb9 | ||
![]() |
23cb39b557 | ||
![]() |
48de321869 | ||
![]() |
c6d68009d2 | ||
![]() |
2cab267405 | ||
![]() |
6bdc0d2e5e | ||
![]() |
3eed81c1eb | ||
![]() |
b447807b36 | ||
![]() |
2771c8c32e | ||
![]() |
21a88bc2d3 | ||
![]() |
57c1838f68 | ||
![]() |
52b0254ec6 | ||
![]() |
49631542ce | ||
![]() |
4b80ffb9eb | ||
![]() |
9efa7c116d | ||
![]() |
5d9c8d59a0 | ||
![]() |
1a60201f68 | ||
![]() |
f3186abf09 | ||
![]() |
6bcc0d3c7f | ||
![]() |
57b9a57dde | ||
![]() |
28994f4b64 | ||
![]() |
588b4712bf | ||
![]() |
d3b6208057 | ||
![]() |
ef80953b1b | ||
![]() |
72db1188c7 | ||
![]() |
0858d3c544 | ||
![]() |
5c5656f981 | ||
![]() |
58a9c92d75 | ||
![]() |
a6dc4646db | ||
![]() |
8ff553e926 | ||
![]() |
848a5c61f0 | ||
![]() |
d49000e9f4 | ||
![]() |
a82145c4e6 | ||
![]() |
181edb7235 | ||
![]() |
ff2ae11ac8 | ||
![]() |
3f841f3b21 | ||
![]() |
181977ad4e | ||
![]() |
e155fe403d | ||
![]() |
bf5438d573 | ||
![]() |
0e4aaf8856 | ||
![]() |
5c44ce1637 | ||
![]() |
974fe25a11 | ||
![]() |
58bae83558 | ||
![]() |
5d309af86f | ||
![]() |
ec857d1c53 | ||
![]() |
2f84cdd708 | ||
![]() |
7cc02e84ed | ||
![]() |
87c2a5bc97 | ||
![]() |
826c2e0f4e | ||
![]() |
b5e25e13b7 | ||
![]() |
f9653114d1 | ||
![]() |
6b7e19891b | ||
![]() |
a677f14423 | ||
![]() |
dddce3f30d | ||
![]() |
be93d670a3 | ||
![]() |
68d4bb6ffe | ||
![]() |
a27471178a | ||
![]() |
66fcb0cc8f | ||
![]() |
05d0ddc281 | ||
![]() |
b1890f50b6 | ||
![]() |
b44c707e94 | ||
![]() |
4c7675939a | ||
![]() |
fa1b7de52a | ||
![]() |
666f8c8d3c | ||
![]() |
996c0b3280 | ||
![]() |
f9d428de8b | ||
![]() |
a17b3f1b84 | ||
![]() |
f39512aa63 | ||
![]() |
23ee9f64d4 | ||
![]() |
ad68739df7 | ||
![]() |
a50d8421b8 | ||
![]() |
3ea1b07906 | ||
![]() |
c3683662c2 | ||
![]() |
861865807a | ||
![]() |
18930082e2 | ||
![]() |
6a14e49479 | ||
![]() |
c530d5f016 | ||
![]() |
ac5e1a6ebd | ||
![]() |
bb6de53f28 | ||
![]() |
bfcd499cc2 | ||
![]() |
3e88ec18e2 | ||
![]() |
1fb640c313 | ||
![]() |
bece3d2bcf | ||
![]() |
307d866bb6 | ||
![]() |
95c9514a44 | ||
![]() |
768be433d6 | ||
![]() |
4d4f38fb35 | ||
![]() |
15ad07f03d | ||
![]() |
0b53c413a7 | ||
![]() |
931397c7e1 | ||
![]() |
ef2cc7ebf5 | ||
![]() |
8d3fd75ec2 | ||
![]() |
a42b254c33 | ||
![]() |
d24e1ae110 | ||
![]() |
2e7badab4e | ||
![]() |
7cf3d49f00 | ||
![]() |
25037006bf | ||
![]() |
f611eb2c2b | ||
![]() |
e12c10b087 | ||
![]() |
9527e5ded8 | ||
![]() |
23b4b20b4f | ||
![]() |
7f9ecd659c | ||
![]() |
bb31d465f2 | ||
![]() |
834468e8e7 | ||
![]() |
4720513672 | ||
![]() |
a480110d43 | ||
![]() |
0a2c95cc10 | ||
![]() |
9d2e32902d | ||
![]() |
77b6413526 | ||
![]() |
9e502099e0 | ||
![]() |
c6a7e44ae7 | ||
![]() |
1bf06312b8 | ||
![]() |
c35721abbd | ||
![]() |
7f3c417078 | ||
![]() |
69511c2783 | ||
![]() |
158a94d34c | ||
![]() |
db58bd68f5 | ||
![]() |
e65f08a2c8 | ||
![]() |
ab8f616385 | ||
![]() |
6dc6f9bbb5 | ||
![]() |
7754bb995b | ||
![]() |
d1fefce61c | ||
![]() |
c3abdab9c4 | ||
![]() |
8b13e103fd | ||
![]() |
5f94f65f4f | ||
![]() |
fc0d69616c | ||
![]() |
656f5b93d6 | ||
![]() |
436d37c079 | ||
![]() |
ed0081fcf7 | ||
![]() |
140062f8a3 | ||
![]() |
75f5fa7c06 | ||
![]() |
9152a1a266 | ||
![]() |
ade89ab795 | ||
![]() |
95cfdee8b8 | ||
![]() |
63a27cc5e2 | ||
![]() |
472face796 | ||
![]() |
8d537a6d0b | ||
![]() |
b3101d339e | ||
![]() |
85acddddba | ||
![]() |
c9d747d97f | ||
![]() |
0bba267808 | ||
![]() |
1036242064 | ||
![]() |
5fd62098bd | ||
![]() |
74cc7be922 | ||
![]() |
b3814ca89a | ||
![]() |
b75a321e4a | ||
![]() |
9caa4fec4a | ||
![]() |
a0cba1aee1 | ||
![]() |
97018ad62f | ||
![]() |
a7d17fae44 | ||
![]() |
6ce0050979 | ||
![]() |
bc035fca78 | ||
![]() |
df914a92e4 | ||
![]() |
1b939a6823 | ||
![]() |
81b6d988ec | ||
![]() |
7e9b65feca | ||
![]() |
6f098b3d21 | ||
![]() |
5ddb0488f2 | ||
![]() |
3e87314adf | ||
![]() |
f6d4a06661 | ||
![]() |
ff17fc95e6 | ||
![]() |
c5a46f1cea | ||
![]() |
0b072189c4 | ||
![]() |
5b22d1486a | ||
![]() |
9eb48c2b0d | ||
![]() |
ff0632001c | ||
![]() |
28bd09a2ea | ||
![]() |
c6aaa9b09c | ||
![]() |
20d9ec1fd2 | ||
![]() |
2c45c2d3c0 | ||
![]() |
18829e648a | ||
![]() |
a64c636a33 | ||
![]() |
5796f211c1 | ||
![]() |
ae09dec05e | ||
![]() |
afd51e0823 | ||
![]() |
0bbf826b21 | ||
![]() |
02d1900e2f | ||
![]() |
73da11b04c | ||
![]() |
4af07e3731 | ||
![]() |
7f60f85cd4 | ||
![]() |
4c66cb1854 | ||
![]() |
35b92e1511 | ||
![]() |
e5d3fe52c5 | ||
![]() |
63fe7c0a86 | ||
![]() |
c5f137c715 | ||
![]() |
66923bc0e3 | ||
![]() |
8bf7b5a323 | ||
![]() |
36d4d85849 | ||
![]() |
5f0e05f3bf | ||
![]() |
235e5511eb | ||
![]() |
6fb60ae0b1 | ||
![]() |
6b2883074b | ||
![]() |
7fe418d1b7 | ||
![]() |
f872ceb0d9 | ||
![]() |
0f10a36b40 | ||
![]() |
3c45c9170f | ||
![]() |
a0730aeb44 | ||
![]() |
e5fdc7fdd0 | ||
![]() |
015c87b5e1 | ||
![]() |
d20a49e500 | ||
![]() |
adb7331670 | ||
![]() |
084f0d27a3 | ||
![]() |
522a0beec0 | ||
![]() |
bf46bcf376 | ||
![]() |
f330c3f8c5 | ||
![]() |
77a51c1e05 | ||
![]() |
144f215705 | ||
![]() |
51b01b6b44 | ||
![]() |
09885534c6 | ||
![]() |
b9dfec38c2 | ||
![]() |
2ef8120073 | ||
![]() |
52ff2e0e63 | ||
![]() |
8cf7dce33f | ||
![]() |
9d3bb4a37a | ||
![]() |
c30437448b | ||
![]() |
7e3496f8aa | ||
![]() |
46ac79f4dc | ||
![]() |
833b14e353 | ||
![]() |
e9eca25792 | ||
![]() |
1854ad133c | ||
![]() |
2b5e723ea5 | ||
![]() |
9a18906edd | ||
![]() |
93cb7582c2 | ||
![]() |
b4529639f6 | ||
![]() |
f0a59fccf8 | ||
![]() |
46dbaf95a6 | ||
![]() |
d418b03708 | ||
![]() |
765e90ecfa | ||
![]() |
ff1e88dde6 | ||
![]() |
62ebcba647 | ||
![]() |
429e90183b | ||
![]() |
875790e862 | ||
![]() |
25edbe6805 | ||
![]() |
e148b50d6a | ||
![]() |
06d46d56cd | ||
![]() |
edd8770c67 | ||
![]() |
daedda8547 | ||
![]() |
df9d897e75 | ||
![]() |
fcd8e5e5ad | ||
![]() |
6c003f71f4 | ||
![]() |
5b704478d9 | ||
![]() |
60eb528d68 | ||
![]() |
1cf730d957 | ||
![]() |
171110b445 | ||
![]() |
22699db855 | ||
![]() |
18405b3908 | ||
![]() |
f0a55b5cbb | ||
![]() |
04a0774ee5 | ||
![]() |
3a8cfb1f45 | ||
![]() |
dcc19d17d4 | ||
![]() |
1ef69adc6f | ||
![]() |
75a4df0f32 | ||
![]() |
8ba1b5fc35 | ||
![]() |
a09471ac6c | ||
![]() |
a916eea684 | ||
![]() |
511998d8e1 | ||
![]() |
e3cf50f791 | ||
![]() |
42ba5298a7 | ||
![]() |
ee79750a22 | ||
![]() |
1787f8617f | ||
![]() |
748ca28185 | ||
![]() |
9c68d713ba | ||
![]() |
fc69678206 | ||
![]() |
aebd717039 | ||
![]() |
1ddb01ac44 | ||
![]() |
3e279cd670 | ||
![]() |
724c03630a | ||
![]() |
b00b2561e5 | ||
![]() |
c5b50fe3cf | ||
![]() |
df9884de3c | ||
![]() |
65ae7669f9 | ||
![]() |
179606feb1 | ||
![]() |
536140340e | ||
![]() |
5d293df64b | ||
![]() |
6188891a53 | ||
![]() |
9774661cfe | ||
![]() |
563bc34fb5 | ||
![]() |
9c95ab3a28 | ||
![]() |
b776c37b36 | ||
![]() |
5b3f92b70f | ||
![]() |
1562b81522 | ||
![]() |
be1016ace6 | ||
![]() |
ee27c689e1 | ||
![]() |
fdbf452ced | ||
![]() |
3d9927dee0 | ||
![]() |
1456b128d2 | ||
![]() |
166f77cb86 | ||
![]() |
5577838905 | ||
![]() |
9c15982299 | ||
![]() |
63c24122db | ||
![]() |
1396ca903d | ||
![]() |
d1fb5bdc30 | ||
![]() |
e27812bf3e | ||
![]() |
11a3cf9b99 | ||
![]() |
a90d70feae | ||
![]() |
466b34735c | ||
![]() |
7ca9116e37 | ||
![]() |
decd3e737c | ||
![]() |
f35442ad1b | ||
![]() |
2b296435b3 | ||
![]() |
19ee1dfecc | ||
![]() |
7da4596ef8 | ||
![]() |
a379ef6781 | ||
![]() |
7beb065be3 | ||
![]() |
38b9091513 | ||
![]() |
96db3c9601 | ||
![]() |
43c4fc8e33 | ||
![]() |
986ff101ce | ||
![]() |
94c83c445f | ||
![]() |
625865412f | ||
![]() |
46677e69ce | ||
![]() |
5fbca5b823 | ||
![]() |
879fab120f | ||
![]() |
391b24bc17 | ||
![]() |
d713533d26 | ||
![]() |
24f745a334 | ||
![]() |
86f3101861 | ||
![]() |
fd823c63ab | ||
![]() |
fa69892f70 | ||
![]() |
cfc53d0d26 | ||
![]() |
97c2056e4a | ||
![]() |
0ad0164171 | ||
![]() |
df0e285b6f | ||
![]() |
e92f1b8c28 | ||
![]() |
410f86c960 | ||
![]() |
85f27320e7 | ||
![]() |
9a3fac90e1 | ||
![]() |
6984f6eec4 | ||
![]() |
d05f502fc8 | ||
![]() |
ba41ab8f67 | ||
![]() |
250bb7e29d | ||
![]() |
48a26fd5df | ||
![]() |
3af26540ec | ||
![]() |
7d9de068d9 | ||
![]() |
d174917a07 | ||
![]() |
af398fc4c4 | ||
![]() |
878ef446a2 | ||
![]() |
668f6477bb | ||
![]() |
01a770cbca | ||
![]() |
23a1174aa2 | ||
![]() |
414020e75b | ||
![]() |
ed74bccad6 | ||
![]() |
0eedde445c | ||
![]() |
88bf78213f | ||
![]() |
d342461a51 | ||
![]() |
dffaaf8751 | ||
![]() |
313edadf47 | ||
![]() |
c9ce33dfe6 | ||
![]() |
0f50ac7205 | ||
![]() |
e807c08275 | ||
![]() |
893977365c | ||
![]() |
0cac45809f | ||
![]() |
489ca3c207 | ||
![]() |
313535c599 | ||
![]() |
7f1e0557c9 | ||
![]() |
0860f84a39 | ||
![]() |
2fe9e78b6d | ||
![]() |
2ba30f2022 | ||
![]() |
b3b27cab34 | ||
![]() |
694207a86d | ||
![]() |
90138c4bae | ||
![]() |
58a833e987 | ||
![]() |
86c5a569d5 | ||
![]() |
19592e8eea | ||
![]() |
8e6678d526 | ||
![]() |
e792a1e030 | ||
![]() |
f0e818a28c | ||
![]() |
69bd63b742 | ||
![]() |
b40f30f2e5 | ||
![]() |
1fbde87ec2 | ||
![]() |
f9dc34c8fa | ||
![]() |
f7186f5331 | ||
![]() |
6a680e4db0 | ||
![]() |
f6b69f412f | ||
![]() |
5aed18862d | ||
![]() |
62bf213a6e | ||
![]() |
e5c32e9b48 | ||
![]() |
b87dc37fbb | ||
![]() |
002d4cb37c | ||
![]() |
ff321fc355 | ||
![]() |
c3386dec84 | ||
![]() |
927d2761f7 | ||
![]() |
5e5f513088 | ||
![]() |
9fcf725061 | ||
![]() |
601e015f00 | ||
![]() |
3289e8403a | ||
![]() |
104a7c7d05 | ||
![]() |
7560660ec7 | ||
![]() |
40ccb4a0dd | ||
![]() |
f90288f5dc | ||
![]() |
3bf79898d9 | ||
![]() |
1d6e11ca10 | ||
![]() |
6e903ee7d5 | ||
![]() |
21fb1dff7e | ||
![]() |
da924a359c | ||
![]() |
a5066f15dc | ||
![]() |
2dca53a696 | ||
![]() |
bfbcdf8b86 | ||
![]() |
42e1d18470 | ||
![]() |
435dc72aa1 | ||
![]() |
66c380548b | ||
![]() |
13f81e9a6f | ||
![]() |
68c8796adb | ||
![]() |
4232f5342e | ||
![]() |
28c31359bf | ||
![]() |
a36e815227 | ||
![]() |
ff3d33d5e0 | ||
![]() |
d015d6b103 | ||
![]() |
0d160fbeaa | ||
![]() |
fedd7920a8 | ||
![]() |
6afac06596 | ||
![]() |
91b2b40b9a | ||
![]() |
56ecb6a3ea | ||
![]() |
64f73f624f | ||
![]() |
20b78b68a6 | ||
![]() |
d8a6d7e02f | ||
![]() |
d1cc14201b | ||
![]() |
a57ec9e669 | ||
![]() |
1a8961587c | ||
![]() |
fa13ad8849 | ||
![]() |
8b23dec322 | ||
![]() |
4e8aac4b41 | ||
![]() |
9c72b557ec | ||
![]() |
207ec1e032 | ||
![]() |
ff5d4276bc | ||
![]() |
26c9618e63 | ||
![]() |
9c306899ba | ||
![]() |
4b7e7aab33 | ||
![]() |
2921e9f868 | ||
![]() |
70b7606cb8 | ||
![]() |
0cae91d525 | ||
![]() |
2c2a28e46b | ||
![]() |
c75d484f23 | ||
![]() |
072f7fec30 | ||
![]() |
a73379495f | ||
![]() |
0b914866eb | ||
![]() |
f12186d024 | ||
![]() |
4a2835dc84 | ||
![]() |
55dc45de33 | ||
![]() |
39aa64ff68 | ||
![]() |
94bb91f2fa | ||
![]() |
f553ca95a5 | ||
![]() |
b44e8167d7 | ||
![]() |
36c1122d20 | ||
![]() |
ad13529eaa | ||
![]() |
04c8f4a5ed | ||
![]() |
850446195f | ||
![]() |
865506546f | ||
![]() |
fe72fadedd | ||
![]() |
9cf9d3ad5c | ||
![]() |
426d0e6af1 | ||
![]() |
483b442b7d | ||
![]() |
9420b0c947 | ||
![]() |
dcfb7345f0 | ||
![]() |
f9ab24a077 | ||
![]() |
f932d16ba7 | ||
![]() |
7d9acc3c36 | ||
![]() |
72f735124f | ||
![]() |
6ea5a4719a | ||
![]() |
f8c50b7f1e | ||
![]() |
feb1f1d71a | ||
![]() |
550afc27dc | ||
![]() |
e6a828572a | ||
![]() |
6ea43d8e6d | ||
![]() |
d13af4bdc4 | ||
![]() |
53a365dd2b | ||
![]() |
6726affa7e | ||
![]() |
6ecf2a6eb2 | ||
![]() |
a359e11f97 | ||
![]() |
54b2d74068 | ||
![]() |
c99aa7279b | ||
![]() |
4fa568ce8a | ||
![]() |
874698b93f | ||
![]() |
b286fc1e4a | ||
![]() |
1cf1024332 | ||
![]() |
6b391b701b | ||
![]() |
efc90f8f5a | ||
![]() |
6535ba7c24 | ||
![]() |
5c29c3d160 | ||
![]() |
742d4bff78 | ||
![]() |
7726ffa3f7 | ||
![]() |
b442d78ebb | ||
![]() |
d44edb5930 | ||
![]() |
d5633b3705 | ||
![]() |
3b68dc72e7 | ||
![]() |
51611c3934 | ||
![]() |
747b7567d7 | ||
![]() |
797891d6cf | ||
![]() |
286dc3c32b | ||
![]() |
a66ba21c3d | ||
![]() |
b139810b6a | ||
![]() |
dddc18d77c | ||
![]() |
56f56d008a | ||
![]() |
81a8a99b6e | ||
![]() |
07aa0ee7ad | ||
![]() |
b66a6bddbc | ||
![]() |
d57d90fe6b | ||
![]() |
de6c646ee8 | ||
![]() |
4839ede64f | ||
![]() |
84f5faf653 | ||
![]() |
281077bc26 | ||
![]() |
ed5fe9ae9f | ||
![]() |
1866e4ef44 | ||
![]() |
a6a07c3b3a | ||
![]() |
b2af8e640c | ||
![]() |
7a3f5d508b | ||
![]() |
7e1fd03104 | ||
![]() |
758415d326 | ||
![]() |
1660041470 | ||
![]() |
1783df883e | ||
![]() |
b2be821637 | ||
![]() |
051ff2b325 | ||
![]() |
4d6f9ffd7c | ||
![]() |
d614823013 | ||
![]() |
48aa51b739 | ||
![]() |
41c6125e1b | ||
![]() |
bb3d48f98b | ||
![]() |
b5e46e83e2 | ||
![]() |
2340910b46 | ||
![]() |
d8c4c1525d | ||
![]() |
6713ef7726 | ||
![]() |
ae7555b065 | ||
![]() |
ee6ff0cc60 | ||
![]() |
94b2352c2c | ||
![]() |
cf3f943feb | ||
![]() |
55c4d583b9 | ||
![]() |
0e1bb6ab04 | ||
![]() |
7944cff7a5 | ||
![]() |
8b08a370c5 | ||
![]() |
2d5fd2fe1c | ||
![]() |
b5e50ecb75 | ||
![]() |
e00c9d0ee0 | ||
![]() |
be9c9f045a | ||
![]() |
75fca1b9c7 | ||
![]() |
1436fb3ef4 | ||
![]() |
2bfd127218 | ||
![]() |
de5da63d5c | ||
![]() |
fb419eaa36 | ||
![]() |
cf2a363e5e | ||
![]() |
7401facc21 | ||
![]() |
579afe012b | ||
![]() |
4f856e8783 | ||
![]() |
eb059183f7 | ||
![]() |
419156f7cc | ||
![]() |
b7f7883fb7 | ||
![]() |
a5a7490bca | ||
![]() |
6724d8131c | ||
![]() |
a19bb15070 | ||
![]() |
2ee0147848 | ||
![]() |
252f1add7e | ||
![]() |
413c92c631 | ||
![]() |
36d519026f | ||
![]() |
aa54785918 | ||
![]() |
d6b386083f | ||
![]() |
82f383b64f | ||
![]() |
a15ee3ad06 | ||
![]() |
29680cb515 | ||
![]() |
884749f345 | ||
![]() |
5c63ce666c | ||
![]() |
5632d073be | ||
![]() |
f9056099f9 | ||
![]() |
7c09ec29f7 | ||
![]() |
6f0b09509e | ||
![]() |
31c53d67e2 | ||
![]() |
6a322ba3f8 | ||
![]() |
dece636d54 | ||
![]() |
b29f648148 | ||
![]() |
c91d264ff1 | ||
![]() |
487e3352e4 | ||
![]() |
34966fb182 | ||
![]() |
17a92a58b2 | ||
![]() |
60d3e5b9e0 | ||
![]() |
d193a1eb70 | ||
![]() |
1501c56bbc | ||
![]() |
6d18fb6bae | ||
![]() |
eac26a4514 | ||
![]() |
1649f30808 | ||
![]() |
82680bf43f | ||
![]() |
41da793b5a | ||
![]() |
cfb5734b85 | ||
![]() |
b72d841619 | ||
![]() |
0ef39f35ae | ||
![]() |
38d1ed76d2 | ||
![]() |
4c80cd185f | ||
![]() |
629524af04 | ||
![]() |
a245e54bd3 | ||
![]() |
52e485cce9 | ||
![]() |
02d374b65b | ||
![]() |
0de6bb0063 | ||
![]() |
9d6b379999 | ||
![]() |
c132c4e673 | ||
![]() |
6962dcd66c | ||
![]() |
2a9496fcda | ||
![]() |
82d1d30a41 | ||
![]() |
85639d0806 | ||
![]() |
3fd6ecaedb | ||
![]() |
884d3a0316 | ||
![]() |
10dbb9186d | ||
![]() |
a547798b08 | ||
![]() |
894b434875 | ||
![]() |
8db0ece459 | ||
![]() |
f56c5e3a45 | ||
![]() |
0a5fa72099 | ||
![]() |
0eaccea38f | ||
![]() |
de32c389d0 | ||
![]() |
753d2da6db | ||
![]() |
d3344da9c5 | ||
![]() |
ae0876876e | ||
![]() |
dea8e16f49 | ||
![]() |
13803bdb30 | ||
![]() |
7257e5794f | ||
![]() |
41c52487ee | ||
![]() |
0eb779185d | ||
![]() |
5bba3388a0 | ||
![]() |
0b9094d348 | ||
![]() |
2d4512cd1c | ||
![]() |
a73a7d1e7b | ||
![]() |
ef9edfd160 | ||
![]() |
234a7925c6 | ||
![]() |
a550b5c112 | ||
![]() |
04798cbf5b |
38
.appveyor.yml
Normal file
38
.appveyor.yml
Normal file
@ -0,0 +1,38 @@
|
||||
version: "{branch}.{build}"
|
||||
|
||||
environment:
|
||||
matrix:
|
||||
- TOXENV: py36-no-ext
|
||||
PYTHON: "C:\\Python36-x64"
|
||||
PYTHON_VERSION: "3.6.x"
|
||||
PYTHON_ARCH: "64"
|
||||
|
||||
- TOXENV: py37-no-ext
|
||||
PYTHON: "C:\\Python37-x64"
|
||||
PYTHON_VERSION: "3.7.x"
|
||||
PYTHON_ARCH: "64"
|
||||
|
||||
- TOXENV: py38-no-ext
|
||||
PYTHON: "C:\\Python38-x64"
|
||||
PYTHON_VERSION: "3.8.x"
|
||||
PYTHON_ARCH: "64"
|
||||
|
||||
# - TOXENV: py39-no-ext
|
||||
# PYTHON: "C:\\Python39-x64\\python"
|
||||
# PYTHONPATH: "C:\\Python39-x64"
|
||||
# PYTHON_VERSION: "3.9.x"
|
||||
# PYTHON_ARCH: "64"
|
||||
|
||||
init: SET "PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%"
|
||||
|
||||
install:
|
||||
- pip install tox
|
||||
|
||||
build: off
|
||||
|
||||
test_script: tox
|
||||
|
||||
notifications:
|
||||
- provider: Email
|
||||
on_build_success: false
|
||||
on_build_status_changed: false
|
24
.coveragerc
24
.coveragerc
@ -1,7 +1,25 @@
|
||||
[run]
|
||||
branch = True
|
||||
source = sanic, tests
|
||||
omit = site-packages
|
||||
source = sanic
|
||||
omit =
|
||||
site-packages
|
||||
sanic/__main__.py
|
||||
sanic/server/legacy.py
|
||||
sanic/compat.py
|
||||
sanic/simple.py
|
||||
sanic/utils.py
|
||||
sanic/cli
|
||||
sanic/pages
|
||||
|
||||
[html]
|
||||
directory = coverage
|
||||
directory = coverage
|
||||
|
||||
[report]
|
||||
exclude_lines =
|
||||
no cov
|
||||
no qa
|
||||
noqa
|
||||
NOQA
|
||||
pragma: no cover
|
||||
TYPE_CHECKING
|
||||
skip_empty = True
|
||||
|
3
.github/CODEOWNERS
vendored
Normal file
3
.github/CODEOWNERS
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
* @sanic-org/sanic-release-managers
|
||||
/sanic/ @sanic-org/framework
|
||||
/tests/ @sanic-org/framework
|
12
.github/FUNDING.yml
vendored
Normal file
12
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: sanic-org # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
78
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
Normal file
78
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
Normal 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
|
||||
|
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Questions and Help
|
||||
url: https://community.sanicframework.org/c/questions-and-help
|
||||
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.
|
34
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
Normal file
34
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
Normal 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
|
||||
|
33
.github/ISSUE_TEMPLATE/rfc.yml
vendored
Normal file
33
.github/ISSUE_TEMPLATE/rfc.yml
vendored
Normal 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
|
20
.github/stale.yml
vendored
Normal file
20
.github/stale.yml
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
# Number of days of inactivity before an issue becomes stale
|
||||
daysUntilStale: 90
|
||||
# Number of days of inactivity before a stale issue is closed
|
||||
daysUntilClose: 30
|
||||
# Issues with these labels will never be considered stale
|
||||
exemptLabels:
|
||||
- bug
|
||||
- urgent
|
||||
- necessary
|
||||
- help wanted
|
||||
- RFC
|
||||
# Label to use when marking an issue as stale
|
||||
staleLabel: stale
|
||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
This issue has been automatically marked as stale because it has not had
|
||||
recent activity. It will be closed if no further activity occurs. If this
|
||||
is incorrect, please respond with an update. Thank you for your contributions.
|
||||
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||
closeComment: false
|
42
.github/workflows/codeql-analysis.yml
vendored
Normal file
42
.github/workflows/codeql-analysis.yml
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- current-release
|
||||
- "*LTS"
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- current-release
|
||||
- "*LTS"
|
||||
types: [opened, synchronize, reopened, ready_for_review]
|
||||
schedule:
|
||||
- cron: '25 16 * * 0'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
if: github.event.pull_request.draft == false
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'python' ]
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v1
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
34
.github/workflows/coverage.yml
vendored
Normal file
34
.github/workflows/coverage.yml
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
name: Coverage check
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- current-release
|
||||
- "*LTS"
|
||||
tags:
|
||||
- "!*" # Do not execute on tags
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- current-release
|
||||
- "*LTS"
|
||||
|
||||
jobs:
|
||||
coverage:
|
||||
name: Check coverage
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
||||
steps:
|
||||
- name: Run coverage
|
||||
uses: sanic-org/simple-tox-action@v1
|
||||
with:
|
||||
python-version: "3.11"
|
||||
tox-env: coverage
|
||||
ignore-errors: true
|
||||
- name: Run Codecov
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
files: ./coverage.xml
|
||||
fail_ci_if_error: false
|
174
.github/workflows/publish-release.yml
vendored
Normal file
174
.github/workflows/publish-release.yml
vendored
Normal file
@ -0,0 +1,174 @@
|
||||
name: Publish release
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [created]
|
||||
|
||||
env:
|
||||
IS_TEST: false
|
||||
DOCKER_ORG_NAME: sanicframework
|
||||
DOCKER_IMAGE_NAME: sanic
|
||||
DOCKER_BASE_IMAGE_NAME: sanic-build
|
||||
DOCKER_IMAGE_DOCKERFILE: ./docker/Dockerfile
|
||||
DOCKER_BASE_IMAGE_DOCKERFILE: ./docker/Dockerfile-base
|
||||
|
||||
jobs:
|
||||
generate_info:
|
||||
name: Generate info
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
docker-tags: ${{ steps.generate_docker_info.outputs.tags }}
|
||||
pypi-version: ${{ steps.parse_version_tag.outputs.pypi-version }}
|
||||
steps:
|
||||
- name: Parse version tag
|
||||
id: parse_version_tag
|
||||
env:
|
||||
TAG_NAME: ${{ github.event.release.tag_name }}
|
||||
run: |
|
||||
tag_name="${{ env.TAG_NAME }}"
|
||||
|
||||
if [[ ! "${tag_name}" =~ ^v([0-9]{2})\.([0-9]{1,2})\.([0-9]+)$ ]]; then
|
||||
echo "::error::Tag name must be in the format vYY.MM.MICRO"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
year_output="year=${BASH_REMATCH[1]}"
|
||||
month_output="month=${BASH_REMATCH[2]}"
|
||||
pypi_output="pypi-version=${tag_name#v}"
|
||||
|
||||
echo "${year_output}"
|
||||
echo "${month_output}"
|
||||
echo "${pypi_output}"
|
||||
|
||||
echo "${year_output}" >> $GITHUB_OUTPUT
|
||||
echo "${month_output}" >> $GITHUB_OUTPUT
|
||||
echo "${pypi_output}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Get latest release
|
||||
id: get_latest_release
|
||||
run: |
|
||||
latest_tag=$(
|
||||
curl -L \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "Authorization: Bearer ${{ github.token }}" \
|
||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||
https://api.github.com/repos/${{ github.repository }}/releases/latest \
|
||||
| jq -r '.tag_name'
|
||||
)
|
||||
echo "latest_tag=$latest_tag" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Generate Docker info
|
||||
id: generate_docker_info
|
||||
run: |
|
||||
tag_year="${{ steps.parse_version_tag.outputs.year }}"
|
||||
tag_month="${{ steps.parse_version_tag.outputs.month }}"
|
||||
latest_tag="${{ steps.get_latest_release.outputs.latest_tag }}"
|
||||
tag="${{ github.event.release.tag_name }}"
|
||||
|
||||
tags="${tag_year}.${tag_month}"
|
||||
|
||||
if [[ "${tag_month}" == "12" ]]; then
|
||||
tags+=",LTS"
|
||||
echo "::notice::Tag ${tag} is LTS version"
|
||||
else
|
||||
echo "::notice::Tag ${tag} is not LTS version"
|
||||
fi
|
||||
|
||||
if [[ "${latest_tag}" == "${{ github.event.release.tag_name }}" ]]; then
|
||||
tags+=",latest"
|
||||
echo "::notice::Tag ${tag} is marked as latest"
|
||||
else
|
||||
echo "::notice::Tag ${tag} is not marked as latest"
|
||||
fi
|
||||
|
||||
tags_output="tags=${tags}"
|
||||
|
||||
echo "${tags_output}"
|
||||
echo "${tags_output}" >> $GITHUB_OUTPUT
|
||||
|
||||
publish_package:
|
||||
name: Build and publish package
|
||||
runs-on: ubuntu-latest
|
||||
needs: generate_info
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.11"
|
||||
|
||||
- name: Install dependencies
|
||||
run: pip install build twine
|
||||
|
||||
- name: Update package version
|
||||
run: |
|
||||
echo "__version__ = \"${{ needs.generate_info.outputs.pypi-version }}\"" > sanic/__version__.py
|
||||
|
||||
- name: Build a binary wheel and a source tarball
|
||||
run: python -m build --sdist --wheel --outdir dist/ .
|
||||
|
||||
- name: Publish to PyPi 🚀
|
||||
run: twine upload --non-interactive --disable-progress-bar dist/*
|
||||
env:
|
||||
TWINE_USERNAME: __token__
|
||||
TWINE_PASSWORD: ${{ env.IS_TEST == 'true' && secrets.SANIC_TEST_PYPI_API_TOKEN || secrets.SANIC_PYPI_API_TOKEN }}
|
||||
TWINE_REPOSITORY: ${{ env.IS_TEST == 'true' && 'testpypi' || 'pypi' }}
|
||||
|
||||
publish_docker:
|
||||
name: Publish Docker / Python ${{ matrix.python-version }}
|
||||
needs: [generate_info, publish_package]
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
python-version: ["3.10", "3.11"]
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_ACCESS_USER }}
|
||||
password: ${{ secrets.DOCKER_ACCESS_TOKEN }}
|
||||
|
||||
- name: Build and push base image
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
push: ${{ env.IS_TEST == 'false' }}
|
||||
file: ${{ env.DOCKER_BASE_IMAGE_DOCKERFILE }}
|
||||
tags: ${{ env.DOCKER_ORG_NAME }}/${{ env.DOCKER_BASE_IMAGE_NAME }}:${{ matrix.python-version }}
|
||||
build-args: |
|
||||
PYTHON_VERSION=${{ matrix.python-version }}
|
||||
|
||||
- name: Parse tags for this Python version
|
||||
id: parse_tags
|
||||
run: |
|
||||
IFS=',' read -ra tags <<< "${{ needs.generate_info.outputs.docker-tags }}"
|
||||
tag_args=""
|
||||
|
||||
for tag in "${tags[@]}"; do
|
||||
tag_args+=",${{ env.DOCKER_ORG_NAME }}/${{ env.DOCKER_IMAGE_NAME }}:${tag}-py${{ matrix.python-version }}"
|
||||
done
|
||||
|
||||
tag_args_output="tag_args=${tag_args:1}"
|
||||
|
||||
echo "${tag_args_output}"
|
||||
echo "${tag_args_output}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Build and push Sanic image
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
push: ${{ env.IS_TEST == 'false' }}
|
||||
file: ${{ env.DOCKER_IMAGE_DOCKERFILE }}
|
||||
tags: ${{ steps.parse_tags.outputs.tag_args }}
|
||||
build-args: |
|
||||
BASE_IMAGE_ORG=${{ env.DOCKER_ORG_NAME }}
|
||||
BASE_IMAGE_NAME=${{ env.DOCKER_BASE_IMAGE_NAME }}
|
||||
BASE_IMAGE_TAG=${{ matrix.python-version }}
|
||||
SANIC_PYPI_VERSION=${{ needs.generate_info.outputs.pypi-version }}
|
56
.github/workflows/tests.yml
vendored
Normal file
56
.github/workflows/tests.yml
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
name: Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- current-release
|
||||
- "*LTS"
|
||||
tags:
|
||||
- "!*"
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- current-release
|
||||
- "*LTS"
|
||||
types: [opened, synchronize, reopened, ready_for_review]
|
||||
|
||||
jobs:
|
||||
run_tests:
|
||||
name: "${{ matrix.config.platform == 'windows-latest' && 'Windows' || 'Linux' }} / Python ${{ matrix.config.python-version }} / tox -e ${{ matrix.config.tox-env }}"
|
||||
if: github.event.pull_request.draft == false
|
||||
runs-on: ${{ matrix.config.platform || 'ubuntu-latest' }}
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
config:
|
||||
- { python-version: "3.8", tox-env: security }
|
||||
- { python-version: "3.9", tox-env: security }
|
||||
- { python-version: "3.10", tox-env: security }
|
||||
- { python-version: "3.11", tox-env: security }
|
||||
- { python-version: "3.10", tox-env: lint }
|
||||
# - { python-version: "3.10", tox-env: docs }
|
||||
- { python-version: "3.8", tox-env: type-checking }
|
||||
- { python-version: "3.9", tox-env: type-checking }
|
||||
- { python-version: "3.10", tox-env: type-checking }
|
||||
- { python-version: "3.11", tox-env: type-checking }
|
||||
- { python-version: "3.8", tox-env: py38, max-attempts: 3 }
|
||||
- { python-version: "3.8", tox-env: py38-no-ext, max-attempts: 3 }
|
||||
- { python-version: "3.9", tox-env: py39, max-attempts: 3 }
|
||||
- { python-version: "3.9", tox-env: py39-no-ext, max-attempts: 3 }
|
||||
- { python-version: "3.10", tox-env: py310, max-attempts: 3 }
|
||||
- { python-version: "3.10", tox-env: py310-no-ext, max-attempts: 3 }
|
||||
- { python-version: "3.11", tox-env: py311, max-attempts: 3 }
|
||||
- { python-version: "3.11", tox-env: py311-no-ext, max-attempts: 3 }
|
||||
- { python-version: "3.8", tox-env: py38-no-ext, platform: windows-latest, ignore-errors: true }
|
||||
- { python-version: "3.9", tox-env: py39-no-ext, platform: windows-latest, ignore-errors: true }
|
||||
- { python-version: "3.10", tox-env: py310-no-ext, platform: windows-latest, ignore-errors: true }
|
||||
- { python-version: "3.11", tox-env: py310-no-ext, platform: windows-latest, ignore-errors: true }
|
||||
steps:
|
||||
- name: Run tests
|
||||
uses: sanic-org/simple-tox-action@v1
|
||||
with:
|
||||
python-version: ${{ matrix.config.python-version }}
|
||||
tox-env: ${{ matrix.config.tox-env }}
|
||||
max-attempts: ${{ matrix.config.max-attempts || 1 }}
|
||||
ignore-errors: ${{ matrix.config.ignore-errors || false }}
|
13
.gitignore
vendored
13
.gitignore
vendored
@ -6,10 +6,21 @@
|
||||
.coverage
|
||||
.coverage.*
|
||||
coverage
|
||||
coverage.xml
|
||||
.tox
|
||||
settings.py
|
||||
.idea/*
|
||||
.cache/*
|
||||
.mypy_cache/
|
||||
.python-version
|
||||
docs/_build/
|
||||
docs/_api/
|
||||
docs/_api/
|
||||
build/*
|
||||
.DS_Store
|
||||
dist/*
|
||||
pip-wheel-metadata/
|
||||
.pytest_cache/*
|
||||
.venv/*
|
||||
venv/*
|
||||
.vscode/*
|
||||
guide/node_modules/
|
||||
|
14
.travis.yml
14
.travis.yml
@ -1,14 +0,0 @@
|
||||
sudo: false
|
||||
language: python
|
||||
python:
|
||||
- '3.5'
|
||||
- '3.6'
|
||||
install: pip install tox-travis
|
||||
script: tox
|
||||
deploy:
|
||||
provider: pypi
|
||||
user: channelcat
|
||||
password:
|
||||
secure: jH4+Di2/qcBwWVhI5/3NYd/JuDDgf5/cF85h+oQnAjgwP6me3th9RS0PHL2gjKJrmyRgwrW7a3eSAityo5sQSlBloQCNrtCE30rkDiwtgoIxDW72NR/nE8nUkS9Utgy87eS+3B4NrO7ag4GTqO5ET8SQ4/MCiQwyUQATLXj2s2eTpQvqJeZG6YgoeFAOYvlR580yznXoOwldWlkiymJiWSdR/01lthtWCi40sYC/QoU7psODJ/tPcsqgQtQKyUVsci7mKvp3Y8ImkoO/POM01jYNsS9qLh5pKTNCEYxtyzC77whenCNHn7WReVidd56g1ADosbNo4yY/1D3VAvwjUnkQ0SzdBQfT7IIzccEuC0j1NXKPN97OX0a6XzyUMYJ1XiU3juTJOPxdYBPbsDM3imQiwrOh1faIf0HCgNTN+Lxe5l8obCH7kffNcVUhs2zI0+2t4MS5tjb/OVuYD/TFn+bM33DqzLctTOK/pGn6xefzZcdzb191LPo99Lof+4fo6jNUpb0UmcBu5ZJzxh0lGe8FPIK3UAG/hrYDDgjx8s8RtUJjcEUQz0659XffYx7DLlgHO7cWyfjrHD3yrLzDbYr5mAS4FR+4D917V7UL+on4SsKHN00UuMGPguqSYo/xYyPLnJU5XK0du4MIpsNMB8TtrJOIewOOfD32+AisPQ8=
|
||||
on:
|
||||
tags: true
|
22
CHANGELOG.md
22
CHANGELOG.md
@ -1,22 +0,0 @@
|
||||
Version 0.1
|
||||
-----------
|
||||
- 0.1.7
|
||||
- Reversed static url and directory arguments to meet spec
|
||||
- 0.1.6
|
||||
- Static files
|
||||
- Lazy Cookie Loading
|
||||
- 0.1.5
|
||||
- Cookies
|
||||
- Blueprint listeners and ordering
|
||||
- Faster Router
|
||||
- Fix: Incomplete file reads on medium+ sized post requests
|
||||
- Breaking: after_start and before_stop now pass sanic as their first argument
|
||||
- 0.1.4
|
||||
- Multiprocessing
|
||||
- 0.1.3
|
||||
- Blueprint support
|
||||
- Faster Response processing
|
||||
- 0.1.1 - 0.1.2
|
||||
- Struggling to update pypi via CI
|
||||
- 0.1.0
|
||||
- Released to public
|
1159
CHANGELOG.rst
Normal file
1159
CHANGELOG.rst
Normal file
File diff suppressed because it is too large
Load Diff
74
CODE_OF_CONDUCT.md
Normal file
74
CODE_OF_CONDUCT.md
Normal file
@ -0,0 +1,74 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, gender identity and expression, level of experience,
|
||||
nationality, personal appearance, race, religion, or sexual identity and
|
||||
orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at adam@sanicframework.org. All
|
||||
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
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at [http://contributor-covenant.org/version/1/4][version]
|
||||
|
||||
[homepage]: http://contributor-covenant.org
|
||||
[version]: http://contributor-covenant.org/version/1/4/
|
240
CONTRIBUTING.rst
Normal file
240
CONTRIBUTING.rst
Normal file
@ -0,0 +1,240 @@
|
||||
Thank you for your interest! Sanic is always looking for contributors. If you
|
||||
don't feel comfortable contributing code, adding docstrings to the source files,
|
||||
or helping with the `Sanic User Guide <https://github.com/sanic-org/sanic-guide>`_
|
||||
by providing documentation or implementation examples would be appreciated!
|
||||
|
||||
We are committed to providing a friendly, safe and welcoming environment for all,
|
||||
regardless of gender, sexual orientation, disability, ethnicity, religion,
|
||||
or similar personal characteristic.
|
||||
Our `code of conduct <https://github.com/sanic-org/sanic/blob/master/CONDUCT.md>`_ sets the standards for behavior.
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
To develop on Sanic (and mainly to just run the tests) it is highly recommend to
|
||||
install from sources.
|
||||
|
||||
So assume you have already cloned the repo and are in the working directory with
|
||||
a virtual environment already set up, then run:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pip install -e ".[dev]"
|
||||
|
||||
Dependency Changes
|
||||
------------------
|
||||
|
||||
``Sanic`` doesn't use ``requirements*.txt`` files to manage any kind of dependencies related to it in order to simplify the
|
||||
effort required in managing the dependencies. Please make sure you have read and understood the following section of
|
||||
the document that explains the way ``sanic`` manages dependencies inside the ``setup.py`` file.
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
|
||||
* - Dependency Type
|
||||
- Usage
|
||||
- Installation
|
||||
* - requirements
|
||||
- Bare minimum dependencies required for sanic to function
|
||||
- ``pip3 install -e .``
|
||||
* - tests_require / extras_require['test']
|
||||
- Dependencies required to run the Unit Tests for ``sanic``
|
||||
- ``pip3 install -e '.[test]'``
|
||||
* - extras_require['dev']
|
||||
- Additional Development requirements to add contributing
|
||||
- ``pip3 install -e '.[dev]'``
|
||||
* - extras_require['docs']
|
||||
- Dependencies required to enable building and enhancing sanic documentation
|
||||
- ``pip3 install -e '.[docs]'``
|
||||
|
||||
|
||||
Running all tests
|
||||
-----------------
|
||||
|
||||
To run the tests for Sanic it is recommended to use tox like so:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
tox
|
||||
|
||||
See it's that simple!
|
||||
|
||||
``tox.ini`` contains different environments. Running ``tox`` without any arguments will
|
||||
run all unittests, perform lint and other checks.
|
||||
|
||||
Run unittests
|
||||
-------------
|
||||
|
||||
``tox`` environment -> ``[testenv]``
|
||||
|
||||
To execute only unittests, run ``tox`` with environment like so:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
tox -e py37 -v -- tests/test_config.py
|
||||
# or
|
||||
tox -e py310 -v -- tests/test_config.py
|
||||
|
||||
Run lint checks
|
||||
---------------
|
||||
|
||||
``tox`` environment -> ``[testenv:lint]``
|
||||
|
||||
Permform ``flake8``\ , ``black`` and ``isort`` checks.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
tox -e lint
|
||||
|
||||
Run type annotation checks
|
||||
--------------------------
|
||||
|
||||
``tox`` environment -> ``[testenv:type-checking]``
|
||||
|
||||
Permform ``mypy`` checks.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
tox -e type-checking
|
||||
|
||||
Run other checks
|
||||
----------------
|
||||
|
||||
``tox`` environment -> ``[testenv:check]``
|
||||
|
||||
Perform other checks.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
tox -e check
|
||||
|
||||
Run Static Analysis
|
||||
-------------------
|
||||
|
||||
``tox`` environment -> ``[testenv:security]``
|
||||
|
||||
Perform static analysis security scan
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
tox -e security
|
||||
|
||||
Run Documentation sanity check
|
||||
------------------------------
|
||||
|
||||
``tox`` environment -> ``[testenv:docs]``
|
||||
|
||||
Perform sanity check on documentation
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
tox -e docs
|
||||
|
||||
|
||||
Code Style
|
||||
----------
|
||||
|
||||
To maintain the code consistency, Sanic uses following tools.
|
||||
|
||||
|
||||
#. `isort <https://github.com/timothycrosley/isort>`_
|
||||
#. `black <https://github.com/python/black>`_
|
||||
#. `flake8 <https://github.com/PyCQA/flake8>`_
|
||||
#. `slotscheck <https://github.com/ariebovenberg/slotscheck>`_
|
||||
|
||||
isort
|
||||
*****
|
||||
|
||||
``isort`` sorts Python imports. It divides imports into three
|
||||
categories sorted each in alphabetical order.
|
||||
|
||||
|
||||
#. built-in
|
||||
#. third-party
|
||||
#. project-specific
|
||||
|
||||
black
|
||||
*****
|
||||
|
||||
``black`` is a Python code formatter.
|
||||
|
||||
flake8
|
||||
******
|
||||
|
||||
``flake8`` is a Python style guide that wraps following tools into one.
|
||||
|
||||
|
||||
#. PyFlakes
|
||||
#. pycodestyle
|
||||
#. Ned Batchelder's McCabe script
|
||||
|
||||
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.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
make pretty
|
||||
|
||||
Refer `tox <https://tox.readthedocs.io/en/latest/index.html>`_ documentation for more details.
|
||||
|
||||
Pull requests
|
||||
-------------
|
||||
|
||||
So the pull request approval rules are pretty simple:
|
||||
|
||||
#. All pull requests must pass unit tests.
|
||||
#. All pull requests must be reviewed and approved by at least one current member of the Core Developer team.
|
||||
#. All pull requests must pass flake8 checks.
|
||||
#. All pull requests must match ``isort`` and ``black`` requirements.
|
||||
#. All pull requests must be **PROPERLY** type annotated, unless exemption is given.
|
||||
#. All pull requests must be consistent with the existing code.
|
||||
#. If you decide to remove/change anything from any common interface a deprecation message should accompany it in accordance with our `deprecation policy <https://sanicframework.org/en/guide/project/policies.html#deprecation>`_.
|
||||
#. If you implement a new feature you should have at least one unit test to accompany it.
|
||||
#. An example must be one of the following:
|
||||
|
||||
* Example of how to use Sanic
|
||||
* Example of how to use Sanic extensions
|
||||
* Example of how to use Sanic and asynchronous library
|
||||
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
Sanic's API documentation is built using `sphinx <http://www.sphinx-doc.org/en/1.5.1/>`_ with module references
|
||||
automatically generated using ``sphinx-apidoc``.
|
||||
|
||||
The User Guide is in the `sanic-guide <https://github.com/sanic-org/sanic-guide>`_ repository.
|
||||
|
||||
To generate the documentation from scratch:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
sphinx-apidoc -fo docs/_api/ sanic
|
||||
sphinx-build -b html docs docs/_build
|
||||
|
||||
# There is a simple make command provided to ease the work required in generating
|
||||
# the documentation
|
||||
make docs
|
||||
|
||||
The HTML documentation will be created in the ``docs/_build`` folder.
|
||||
|
||||
You can run the following to have a live development server with the API documents
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
make docs-serve
|
||||
|
||||
Refer to the User Guide repo for documentation on how to contribute there.
|
||||
|
||||
.. warning::
|
||||
One of the main goals of Sanic is speed. Code that lowers the performance of
|
||||
Sanic without significant gains in usability, security, or features may not be
|
||||
merged. Please don't let this intimidate you! If you have any concerns about an
|
||||
idea, open an issue for discussion and help.
|
4
LICENSE
4
LICENSE
@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) [year] [fullname]
|
||||
Copyright (c) 2016-present Sanic Community
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
SOFTWARE.
|
||||
|
15
MANIFEST.in
Normal file
15
MANIFEST.in
Normal file
@ -0,0 +1,15 @@
|
||||
# Non Code related contents
|
||||
include LICENSE
|
||||
include README.rst
|
||||
include pyproject.toml
|
||||
|
||||
# Setup
|
||||
include setup.py
|
||||
include Makefile
|
||||
|
||||
# Tests
|
||||
include .coveragerc
|
||||
graft tests
|
||||
|
||||
global-exclude __pycache__
|
||||
global-exclude *.py[co]
|
105
Makefile
Normal file
105
Makefile
Normal file
@ -0,0 +1,105 @@
|
||||
.PHONY: help test test-coverage install docker-test black fix-import beautify
|
||||
|
||||
.DEFAULT: help
|
||||
|
||||
help:
|
||||
@echo "Please use \`make <target>' where <target> is one of"
|
||||
@echo "test"
|
||||
@echo " Run Sanic Unit Tests"
|
||||
@echo "test-coverage"
|
||||
@echo " Run Sanic Unit Tests with Coverage"
|
||||
@echo "install"
|
||||
@echo " Install Sanic"
|
||||
@echo "docker-test"
|
||||
@echo " Run Sanic Unit Tests using Docker"
|
||||
@echo "black"
|
||||
@echo " Analyze and fix linting issues using Black"
|
||||
@echo "fix-import"
|
||||
@echo " Analyze and fix import order using isort"
|
||||
@echo "beautify [sort_imports=1] [include_tests=1]"
|
||||
@echo " Analyze and fix linting issue using black and optionally fix import sort using isort"
|
||||
@echo ""
|
||||
@echo "docs"
|
||||
@echo " Generate Sanic documentation"
|
||||
@echo ""
|
||||
@echo "clean-docs"
|
||||
@echo " Clean Sanic documentation"
|
||||
@echo ""
|
||||
@echo "docs-test"
|
||||
@echo " Test Sanic Documentation for errors"
|
||||
@echo ""
|
||||
@echo "changelog"
|
||||
@echo " Generate changelog for Sanic to prepare for new release"
|
||||
@echo ""
|
||||
@echo "release"
|
||||
@echo " Prepare Sanic for a new changes by version bump and changelog"
|
||||
@echo ""
|
||||
|
||||
|
||||
clean:
|
||||
find . ! -path "./.eggs/*" -name "*.pyc" -exec rm {} \;
|
||||
find . ! -path "./.eggs/*" -name "*.pyo" -exec rm {} \;
|
||||
find . ! -path "./.eggs/*" -name ".coverage" -exec rm {} \;
|
||||
rm -rf build/* > /dev/null 2>&1
|
||||
rm -rf dist/* > /dev/null 2>&1
|
||||
|
||||
test: clean
|
||||
python setup.py test
|
||||
|
||||
test-coverage: clean
|
||||
python setup.py test --pytest-args="--cov sanic --cov-report term --cov-append "
|
||||
|
||||
view-coverage:
|
||||
sanic ./coverage --simple
|
||||
|
||||
install:
|
||||
python setup.py install
|
||||
|
||||
docker-test: clean
|
||||
docker build -t sanic/test-image -f docker/Dockerfile .
|
||||
docker run -t sanic/test-image tox
|
||||
|
||||
beautify: black
|
||||
ifdef sort_imports
|
||||
ifdef include_tests
|
||||
$(warning It is suggested that you do not run sort import on tests)
|
||||
isort -rc sanic tests
|
||||
else
|
||||
$(info Sorting Imports)
|
||||
isort -rc sanic tests
|
||||
endif
|
||||
endif
|
||||
|
||||
black:
|
||||
black sanic tests
|
||||
|
||||
isort:
|
||||
isort sanic tests
|
||||
|
||||
pretty: black isort
|
||||
|
||||
docs-clean:
|
||||
cd docs && make clean
|
||||
|
||||
docs: docs-clean
|
||||
cd docs && make html
|
||||
|
||||
docs-test: docs-clean
|
||||
cd docs && make dummy
|
||||
|
||||
docs-serve:
|
||||
sphinx-autobuild docs docs/_build/html --port 9999 --watch ./
|
||||
|
||||
changelog:
|
||||
python scripts/changelog.py
|
||||
|
||||
guide-serve:
|
||||
cd guide && sanic server:app -r -R ./content -R ./style
|
||||
|
||||
release:
|
||||
ifdef version
|
||||
python scripts/release.py --release-version ${version} --generate-changelog
|
||||
else
|
||||
python scripts/release.py --generate-changelog
|
||||
endif
|
||||
|
218
README.rst
218
README.rst
@ -1,41 +1,105 @@
|
||||
Sanic
|
||||
=================================
|
||||
.. image:: https://raw.githubusercontent.com/sanic-org/sanic-assets/master/png/sanic-framework-logo-400x97.png
|
||||
:alt: Sanic | Build fast. Run fast.
|
||||
|
||||
|Join the chat at https://gitter.im/sanic-python/Lobby| |Build Status| |PyPI| |PyPI version|
|
||||
Sanic | Build fast. Run fast.
|
||||
=============================
|
||||
|
||||
Sanic is a Flask-like Python 3.5+ web server that's written to go fast. It's based on the work done by the amazing folks at magicstack, and was inspired by `this article <https://magic.io/blog/uvloop-blazing-fast-python-networking/>`_.
|
||||
.. start-badges
|
||||
|
||||
On top of being Flask-like, Sanic supports async request handlers. This means you can use the new shiny async/await syntax from Python 3.5, making your code non-blocking and speedy.
|
||||
.. list-table::
|
||||
:widths: 15 85
|
||||
:stub-columns: 1
|
||||
|
||||
Sanic is developed `on GitHub <https://github.com/channelcat/sanic/>`_. Contributions are welcome!
|
||||
* - Build
|
||||
- | |Tests|
|
||||
* - Docs
|
||||
- | |UserGuide| |Documentation|
|
||||
* - Package
|
||||
- | |PyPI| |PyPI version| |Wheel| |Supported implementations| |Code style black|
|
||||
* - Support
|
||||
- | |Forums| |Discord| |Awesome|
|
||||
* - Stats
|
||||
- | |Monthly Downloads| |Weekly Downloads| |Conda downloads|
|
||||
|
||||
Benchmarks
|
||||
----------
|
||||
.. |UserGuide| image:: https://img.shields.io/badge/user%20guide-sanic-ff0068
|
||||
:target: https://sanicframework.org/
|
||||
.. |Forums| image:: https://img.shields.io/badge/forums-community-ff0068.svg
|
||||
:target: https://community.sanicframework.org/
|
||||
.. |Discord| image:: https://img.shields.io/discord/812221182594121728?logo=discord
|
||||
:target: https://discord.gg/FARQzAEMAA
|
||||
.. |Tests| image:: https://github.com/sanic-org/sanic/actions/workflows/tests.yml/badge.svg?branch=main
|
||||
:target: https://github.com/sanic-org/sanic/actions/workflows/tests.yml
|
||||
.. |Documentation| image:: https://readthedocs.org/projects/sanic/badge/?version=latest
|
||||
:target: http://sanic.readthedocs.io/en/latest/?badge=latest
|
||||
.. |PyPI| image:: https://img.shields.io/pypi/v/sanic.svg
|
||||
:target: https://pypi.python.org/pypi/sanic/
|
||||
.. |PyPI version| image:: https://img.shields.io/pypi/pyversions/sanic.svg
|
||||
:target: https://pypi.python.org/pypi/sanic/
|
||||
.. |Code style black| image:: https://img.shields.io/badge/code%20style-black-000000.svg
|
||||
:target: https://github.com/ambv/black
|
||||
.. |Wheel| image:: https://img.shields.io/pypi/wheel/sanic.svg
|
||||
:alt: PyPI Wheel
|
||||
:target: https://pypi.python.org/pypi/sanic
|
||||
.. |Supported implementations| image:: https://img.shields.io/pypi/implementation/sanic.svg
|
||||
:alt: Supported implementations
|
||||
:target: https://pypi.python.org/pypi/sanic
|
||||
.. |Awesome| image:: https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg
|
||||
:alt: Awesome Sanic List
|
||||
:target: https://github.com/mekicha/awesome-sanic
|
||||
.. |Monthly Downloads| image:: https://img.shields.io/pypi/dm/sanic.svg
|
||||
:alt: Downloads
|
||||
:target: https://pepy.tech/project/sanic
|
||||
.. |Weekly Downloads| image:: https://img.shields.io/pypi/dw/sanic.svg
|
||||
:alt: Downloads
|
||||
:target: https://pepy.tech/project/sanic
|
||||
.. |Conda downloads| image:: https://img.shields.io/conda/dn/conda-forge/sanic.svg
|
||||
:alt: Downloads
|
||||
:target: https://anaconda.org/conda-forge/sanic
|
||||
.. |Linode| image:: https://www.linode.com/wp-content/uploads/2021/01/Linode-Logo-Black.svg
|
||||
:alt: Linode
|
||||
:target: https://www.linode.com
|
||||
:width: 200px
|
||||
|
||||
All tests were run on an AWS medium instance running ubuntu, using 1
|
||||
process. Each script delivered a small JSON response and was tested with
|
||||
wrk using 100 connections. Pypy was tested for Falcon and Flask but did
|
||||
not speed up requests.
|
||||
.. end-badges
|
||||
|
||||
Sanic is a **Python 3.8+** 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://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>`_
|
||||
|
||||
The project is maintained by the community, for the community. **Contributions are welcome!**
|
||||
|
||||
The goal of the project is to provide a simple way to get up and running a highly performant HTTP server that is easy to build, to expand, and ultimately to scale.
|
||||
|
||||
Sponsor
|
||||
-------
|
||||
|
||||
Check out `open collective <https://opencollective.com/sanic-org>`_ to learn more about helping to fund Sanic.
|
||||
|
||||
Thanks to `Linode <https://www.linode.com>`_ for their contribution towards the development and community of Sanic.
|
||||
|
||||
|Linode|
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
``pip3 install sanic``
|
||||
|
||||
Sanic makes use of ``uvloop`` and ``ujson`` to help with performance. If you do not want to use those packages, simply add an environmental variable ``SANIC_NO_UVLOOP=true`` or ``SANIC_NO_UJSON=true`` at install time.
|
||||
|
||||
.. code:: shell
|
||||
|
||||
$ export SANIC_NO_UVLOOP=true
|
||||
$ export SANIC_NO_UJSON=true
|
||||
$ pip3 install --no-binary :all: sanic
|
||||
|
||||
|
||||
.. note::
|
||||
|
||||
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.
|
||||
|
||||
+-----------+-----------------------+----------------+---------------+
|
||||
| Server | Implementation | Requests/sec | Avg Latency |
|
||||
+===========+=======================+================+===============+
|
||||
| Sanic | Python 3.5 + uvloop | 33,342 | 2.96ms |
|
||||
+-----------+-----------------------+----------------+---------------+
|
||||
| Wheezy | gunicorn + meinheld | 20,244 | 4.94ms |
|
||||
+-----------+-----------------------+----------------+---------------+
|
||||
| Falcon | gunicorn + meinheld | 18,972 | 5.27ms |
|
||||
+-----------+-----------------------+----------------+---------------+
|
||||
| Bottle | gunicorn + meinheld | 13,596 | 7.36ms |
|
||||
+-----------+-----------------------+----------------+---------------+
|
||||
| Flask | gunicorn + meinheld | 4,988 | 20.08ms |
|
||||
+-----------+-----------------------+----------------+---------------+
|
||||
| Kyoukai | Python 3.5 + uvloop | 3,889 | 27.44ms |
|
||||
+-----------+-----------------------+----------------+---------------+
|
||||
| Aiohttp | Python 3.5 + uvloop | 2,979 | 33.42ms |
|
||||
+-----------+-----------------------+----------------+---------------+
|
||||
| Tornado | Python 3.5 | 2,138 | 46.66ms |
|
||||
+-----------+-----------------------+----------------+---------------+
|
||||
|
||||
Hello World Example
|
||||
-------------------
|
||||
@ -45,83 +109,55 @@ Hello World Example
|
||||
from sanic import Sanic
|
||||
from sanic.response import json
|
||||
|
||||
app = Sanic("my-hello-world-app")
|
||||
|
||||
app = Sanic()
|
||||
|
||||
|
||||
@app.route("/")
|
||||
@app.route('/')
|
||||
async def test(request):
|
||||
return json({"hello": "world"})
|
||||
return json({'hello': 'world'})
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(host="0.0.0.0", port=8000)
|
||||
if __name__ == '__main__':
|
||||
app.run()
|
||||
|
||||
SSL Example
|
||||
-----------
|
||||
Sanic can now be easily run using ``sanic hello.app``.
|
||||
|
||||
Optionally pass in an SSLContext:
|
||||
.. code::
|
||||
|
||||
.. code:: python
|
||||
[2018-12-30 11:37:41 +0200] [13564] [INFO] Goin' Fast @ http://127.0.0.1:8000
|
||||
[2018-12-30 11:37:41 +0200] [13564] [INFO] Starting worker [13564]
|
||||
|
||||
import ssl
|
||||
certificate = "/path/to/certificate"
|
||||
keyfile = "/path/to/keyfile"
|
||||
context = ssl.create_default_context(purpose=ssl.Purpose.CLIENT_AUTH)
|
||||
context.load_cert_chain(certificate, keyfile=keyfile)
|
||||
And, we can verify it is working: ``curl localhost:8000 -i``
|
||||
|
||||
app.run(host="0.0.0.0", port=8443, ssl=context)
|
||||
.. code::
|
||||
|
||||
Installation
|
||||
------------
|
||||
HTTP/1.1 200 OK
|
||||
Connection: keep-alive
|
||||
Keep-Alive: 5
|
||||
Content-Length: 17
|
||||
Content-Type: application/json
|
||||
|
||||
- ``python -m pip install sanic``
|
||||
{"hello":"world"}
|
||||
|
||||
**Now, let's go build something fast!**
|
||||
|
||||
Minimum Python version is 3.8. If you need Python 3.7 support, please use v22.12LTS.
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
Documentation can be found in the ``docs`` directory.
|
||||
`User Guide <https://sanic.dev>`__ and `API Documentation <http://sanic.readthedocs.io/>`__.
|
||||
|
||||
.. |Join the chat at https://gitter.im/sanic-python/Lobby| image:: https://badges.gitter.im/sanic-python/Lobby.svg
|
||||
:target: https://gitter.im/sanic-python/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
|
||||
.. |Build Status| image:: https://travis-ci.org/channelcat/sanic.svg?branch=master
|
||||
:target: https://travis-ci.org/channelcat/sanic
|
||||
.. |PyPI| image:: https://img.shields.io/pypi/v/sanic.svg
|
||||
:target: https://pypi.python.org/pypi/sanic/
|
||||
.. |PyPI version| image:: https://img.shields.io/pypi/pyversions/sanic.svg
|
||||
:target: https://pypi.python.org/pypi/sanic/
|
||||
Changelog
|
||||
---------
|
||||
|
||||
TODO
|
||||
----
|
||||
* Streamed file processing
|
||||
* File output
|
||||
* Examples of integrations with 3rd-party modules
|
||||
* RESTful router
|
||||
`Release Changelogs <https://sanic.readthedocs.io/en/stable/sanic/changelog.html>`__.
|
||||
|
||||
Limitations
|
||||
-----------
|
||||
* No wheels for uvloop and httptools on Windows :(
|
||||
|
||||
Final Thoughts
|
||||
--------------
|
||||
Questions and Discussion
|
||||
------------------------
|
||||
|
||||
::
|
||||
`Ask a question or join the conversation <https://community.sanicframework.org/>`__.
|
||||
|
||||
▄▄▄▄▄
|
||||
▀▀▀██████▄▄▄ _______________
|
||||
▄▄▄▄▄ █████████▄ / \
|
||||
▀▀▀▀█████▌ ▀▐▄ ▀▐█ | Gotta go fast! |
|
||||
▀▀█████▄▄ ▀██████▄██ | _________________/
|
||||
▀▄▄▄▄▄ ▀▀█▄▀█════█▀ |/
|
||||
▀▀▀▄ ▀▀███ ▀ ▄▄
|
||||
▄███▀▀██▄████████▄ ▄▀▀▀▀▀▀█▌
|
||||
██▀▄▄▄██▀▄███▀ ▀▀████ ▄██
|
||||
▄▀▀▀▄██▄▀▀▌████▒▒▒▒▒▒███ ▌▄▄▀
|
||||
▌ ▐▀████▐███▒▒▒▒▒▐██▌
|
||||
▀▄▄▄▄▀ ▀▀████▒▒▒▒▄██▀
|
||||
▀▀█████████▀
|
||||
▄▄██▀██████▀█
|
||||
▄██▀ ▀▀▀ █
|
||||
▄█ ▐▌
|
||||
▄▄▄▄█▌ ▀█▄▄▄▄▀▀▄
|
||||
▌ ▐ ▀▀▄▄▄▀
|
||||
▀▀▄▄▀
|
||||
Contribution
|
||||
------------
|
||||
|
||||
We are always happy to have new contributions. We have `marked issues good for anyone looking to get started <https://github.com/sanic-org/sanic/issues?q=is%3Aopen+is%3Aissue+label%3Abeginner>`_, and welcome `questions on the forums <https://community.sanicframework.org/>`_. Please take a look at our `Contribution guidelines <https://github.com/sanic-org/sanic/blob/master/CONTRIBUTING.rst>`_.
|
||||
|
45
SECURITY.md
Normal file
45
SECURITY.md
Normal file
@ -0,0 +1,45 @@
|
||||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
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 |
|
||||
| ------- | ------------- | ----------------------- |
|
||||
| 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
|
||||
|
||||
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.
|
2
changelogs/.gitignore
vendored
Normal file
2
changelogs/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
# Except this file
|
||||
!.gitignore
|
1
changelogs/1892.removal.rst
Normal file
1
changelogs/1892.removal.rst
Normal file
@ -0,0 +1 @@
|
||||
Remove [version] section.
|
3
changelogs/1904.feature.rst
Normal file
3
changelogs/1904.feature.rst
Normal file
@ -0,0 +1,3 @@
|
||||
Adds WEBSOCKET_PING_TIMEOUT and WEBSOCKET_PING_INTERVAL configuration values
|
||||
|
||||
Allows setting the ping_interval and ping_timeout arguments when initializing `WebSocketCommonProtocol`.
|
1
changelogs/1970.misc.rst
Normal file
1
changelogs/1970.misc.rst
Normal file
@ -0,0 +1 @@
|
||||
Adds py.typed file to expose type information to other packages.
|
28
codecov.yml
Normal file
28
codecov.yml
Normal 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/"
|
13
docker/Dockerfile
Normal file
13
docker/Dockerfile
Normal file
@ -0,0 +1,13 @@
|
||||
ARG BASE_IMAGE_ORG
|
||||
ARG BASE_IMAGE_NAME
|
||||
ARG BASE_IMAGE_TAG
|
||||
|
||||
FROM ${BASE_IMAGE_ORG}/${BASE_IMAGE_NAME}:${BASE_IMAGE_TAG}
|
||||
|
||||
RUN apk update
|
||||
RUN update-ca-certificates
|
||||
|
||||
ARG SANIC_PYPI_VERSION
|
||||
|
||||
RUN pip install -U pip && pip install sanic==${SANIC_PYPI_VERSION}
|
||||
RUN apk del build-base
|
9
docker/Dockerfile-base
Normal file
9
docker/Dockerfile-base
Normal file
@ -0,0 +1,9 @@
|
||||
ARG PYTHON_VERSION
|
||||
|
||||
FROM python:${PYTHON_VERSION}-alpine
|
||||
RUN apk update
|
||||
RUN apk add --no-cache --update build-base \
|
||||
ca-certificates \
|
||||
openssl
|
||||
RUN update-ca-certificates
|
||||
RUN rm -rf /var/cache/apk/*
|
225
docs/Makefile
Normal file
225
docs/Makefile
Normal file
@ -0,0 +1,225 @@
|
||||
# Makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = sphinx-build
|
||||
PAPER =
|
||||
BUILDDIR = _build
|
||||
|
||||
# Internal variables.
|
||||
PAPEROPT_a4 = -D latex_paper_size=a4
|
||||
PAPEROPT_letter = -D latex_paper_size=letter
|
||||
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
|
||||
# the i18n builder cannot share the environment and doctrees with the others
|
||||
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
|
||||
|
||||
.PHONY: help
|
||||
help:
|
||||
@echo "Please use \`make <target>' where <target> is one of"
|
||||
@echo " html to make standalone HTML files"
|
||||
@echo " dirhtml to make HTML files named index.html in directories"
|
||||
@echo " singlehtml to make a single large HTML file"
|
||||
@echo " pickle to make pickle files"
|
||||
@echo " json to make JSON files"
|
||||
@echo " htmlhelp to make HTML files and a HTML help project"
|
||||
@echo " qthelp to make HTML files and a qthelp project"
|
||||
@echo " applehelp to make an Apple Help Book"
|
||||
@echo " devhelp to make HTML files and a Devhelp project"
|
||||
@echo " epub to make an epub"
|
||||
@echo " epub3 to make an epub3"
|
||||
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
|
||||
@echo " latexpdf to make LaTeX files and run them through pdflatex"
|
||||
@echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
|
||||
@echo " text to make text files"
|
||||
@echo " man to make manual pages"
|
||||
@echo " texinfo to make Texinfo files"
|
||||
@echo " info to make Texinfo files and run them through makeinfo"
|
||||
@echo " gettext to make PO message catalogs"
|
||||
@echo " changes to make an overview of all changed/added/deprecated items"
|
||||
@echo " xml to make Docutils-native XML files"
|
||||
@echo " pseudoxml to make pseudoxml-XML files for display purposes"
|
||||
@echo " linkcheck to check all external links for integrity"
|
||||
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
|
||||
@echo " coverage to run coverage check of the documentation (if enabled)"
|
||||
@echo " dummy to check syntax errors of document sources"
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -rf $(BUILDDIR)/*
|
||||
|
||||
.PHONY: html
|
||||
html:
|
||||
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
|
||||
|
||||
.PHONY: dirhtml
|
||||
dirhtml:
|
||||
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
|
||||
|
||||
.PHONY: singlehtml
|
||||
singlehtml:
|
||||
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
|
||||
|
||||
.PHONY: pickle
|
||||
pickle:
|
||||
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
|
||||
@echo
|
||||
@echo "Build finished; now you can process the pickle files."
|
||||
|
||||
.PHONY: json
|
||||
json:
|
||||
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
|
||||
@echo
|
||||
@echo "Build finished; now you can process the JSON files."
|
||||
|
||||
.PHONY: htmlhelp
|
||||
htmlhelp:
|
||||
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run HTML Help Workshop with the" \
|
||||
".hhp project file in $(BUILDDIR)/htmlhelp."
|
||||
|
||||
.PHONY: qthelp
|
||||
qthelp:
|
||||
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
|
||||
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
|
||||
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/aiographite.qhcp"
|
||||
@echo "To view the help file:"
|
||||
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/aiographite.qhc"
|
||||
|
||||
.PHONY: applehelp
|
||||
applehelp:
|
||||
$(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp
|
||||
@echo
|
||||
@echo "Build finished. The help book is in $(BUILDDIR)/applehelp."
|
||||
@echo "N.B. You won't be able to view it unless you put it in" \
|
||||
"~/Library/Documentation/Help or install it in your application" \
|
||||
"bundle."
|
||||
|
||||
.PHONY: devhelp
|
||||
devhelp:
|
||||
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
|
||||
@echo
|
||||
@echo "Build finished."
|
||||
@echo "To view the help file:"
|
||||
@echo "# mkdir -p $$HOME/.local/share/devhelp/aiographite"
|
||||
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/aiographite"
|
||||
@echo "# devhelp"
|
||||
|
||||
.PHONY: epub
|
||||
epub:
|
||||
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
|
||||
@echo
|
||||
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
|
||||
|
||||
.PHONY: epub3
|
||||
epub3:
|
||||
$(SPHINXBUILD) -b epub3 $(ALLSPHINXOPTS) $(BUILDDIR)/epub3
|
||||
@echo
|
||||
@echo "Build finished. The epub3 file is in $(BUILDDIR)/epub3."
|
||||
|
||||
.PHONY: latex
|
||||
latex:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo
|
||||
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
|
||||
@echo "Run \`make' in that directory to run these through (pdf)latex" \
|
||||
"(use \`make latexpdf' here to do that automatically)."
|
||||
|
||||
.PHONY: latexpdf
|
||||
latexpdf:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo "Running LaTeX files through pdflatex..."
|
||||
$(MAKE) -C $(BUILDDIR)/latex all-pdf
|
||||
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
|
||||
|
||||
.PHONY: latexpdfja
|
||||
latexpdfja:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo "Running LaTeX files through platex and dvipdfmx..."
|
||||
$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
|
||||
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
|
||||
|
||||
.PHONY: text
|
||||
text:
|
||||
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
|
||||
@echo
|
||||
@echo "Build finished. The text files are in $(BUILDDIR)/text."
|
||||
|
||||
.PHONY: man
|
||||
man:
|
||||
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
|
||||
@echo
|
||||
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
|
||||
|
||||
.PHONY: texinfo
|
||||
texinfo:
|
||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
||||
@echo
|
||||
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
|
||||
@echo "Run \`make' in that directory to run these through makeinfo" \
|
||||
"(use \`make info' here to do that automatically)."
|
||||
|
||||
.PHONY: info
|
||||
info:
|
||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
||||
@echo "Running Texinfo files through makeinfo..."
|
||||
make -C $(BUILDDIR)/texinfo info
|
||||
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
|
||||
|
||||
.PHONY: gettext
|
||||
gettext:
|
||||
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
|
||||
@echo
|
||||
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
|
||||
|
||||
.PHONY: changes
|
||||
changes:
|
||||
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
|
||||
@echo
|
||||
@echo "The overview file is in $(BUILDDIR)/changes."
|
||||
|
||||
.PHONY: linkcheck
|
||||
linkcheck:
|
||||
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
|
||||
@echo
|
||||
@echo "Link check complete; look for any errors in the above output " \
|
||||
"or in $(BUILDDIR)/linkcheck/output.txt."
|
||||
|
||||
.PHONY: doctest
|
||||
doctest:
|
||||
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
|
||||
@echo "Testing of doctests in the sources finished, look at the " \
|
||||
"results in $(BUILDDIR)/doctest/output.txt."
|
||||
|
||||
.PHONY: coverage
|
||||
coverage:
|
||||
$(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage
|
||||
@echo "Testing of coverage in the sources finished, look at the " \
|
||||
"results in $(BUILDDIR)/coverage/python.txt."
|
||||
|
||||
.PHONY: xml
|
||||
xml:
|
||||
$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
|
||||
@echo
|
||||
@echo "Build finished. The XML files are in $(BUILDDIR)/xml."
|
||||
|
||||
.PHONY: pseudoxml
|
||||
pseudoxml:
|
||||
$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
|
||||
@echo
|
||||
@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
|
||||
|
||||
.PHONY: dummy
|
||||
dummy:
|
||||
$(SPHINXBUILD) -b dummy $(ALLSPHINXOPTS) $(BUILDDIR)/dummy
|
||||
@echo
|
||||
@echo "Build finished. Dummy builder generates no files."
|
0
docs/_static/.gitkeep
vendored
Normal file
0
docs/_static/.gitkeep
vendored
Normal file
13
docs/_static/custom.css
vendored
Normal file
13
docs/_static/custom.css
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
.wy-side-nav-search,
|
||||
.wy-nav-top {
|
||||
background: #444444;
|
||||
}
|
||||
|
||||
#changelog section {
|
||||
padding-left: 3rem;
|
||||
}
|
||||
|
||||
#changelog section h2,
|
||||
#changelog section h3 {
|
||||
margin-left: -3rem;
|
||||
}
|
BIN
docs/_static/sanic-framework-logo-white-400x97.png
vendored
Normal file
BIN
docs/_static/sanic-framework-logo-white-400x97.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
@ -1,164 +0,0 @@
|
||||
# Blueprints
|
||||
|
||||
Blueprints are objects that can be used for sub-routing within an application.
|
||||
Instead of adding routes to the application instance, blueprints define similar
|
||||
methods for adding routes, which are then registered with the application in a
|
||||
flexible and pluggable manner.
|
||||
|
||||
Blueprints are especially useful for larger applications, where your
|
||||
application logic can be broken down into several groups or areas of
|
||||
responsibility.
|
||||
|
||||
## My First Blueprint
|
||||
|
||||
The following shows a very simple blueprint that registers a handler-function at
|
||||
the root `/` of your application.
|
||||
|
||||
Suppose you save this file as `my_blueprint.py`, which can be imported into your
|
||||
main application later.
|
||||
|
||||
```python
|
||||
from sanic.response import json
|
||||
from sanic import Blueprint
|
||||
|
||||
bp = Blueprint('my_blueprint')
|
||||
|
||||
@bp.route('/')
|
||||
async def bp_root(request):
|
||||
return json({'my': 'blueprint'})
|
||||
|
||||
```
|
||||
|
||||
## Registering blueprints
|
||||
|
||||
Blueprints must be registered with the application.
|
||||
|
||||
```python
|
||||
from sanic import Sanic
|
||||
from my_blueprint import bp
|
||||
|
||||
app = Sanic(__name__)
|
||||
app.blueprint(bp)
|
||||
|
||||
app.run(host='0.0.0.0', port=8000, debug=True)
|
||||
```
|
||||
|
||||
This will add the blueprint to the application and register any routes defined
|
||||
by that blueprint. In this example, the registered routes in the `app.router`
|
||||
will look like:
|
||||
|
||||
```python
|
||||
[Route(handler=<function bp_root at 0x7f908382f9d8>, methods=None, pattern=re.compile('^/$'), parameters=[])]
|
||||
```
|
||||
|
||||
## Using blueprints
|
||||
|
||||
Blueprints have much the same functionality as an application instance.
|
||||
|
||||
### Middleware
|
||||
|
||||
Using blueprints allows you to also register middleware globally.
|
||||
|
||||
```python
|
||||
@bp.middleware
|
||||
async def halt_request(request):
|
||||
print("I am a spy")
|
||||
|
||||
@bp.middleware('request')
|
||||
async def halt_request(request):
|
||||
return text('I halted the request')
|
||||
|
||||
@bp.middleware('response')
|
||||
async def halt_response(request, response):
|
||||
return text('I halted the response')
|
||||
```
|
||||
|
||||
### Exceptions
|
||||
|
||||
Exceptions can be applied exclusively to blueprints globally.
|
||||
|
||||
```python
|
||||
@bp.exception(NotFound)
|
||||
def ignore_404s(request, exception):
|
||||
return text("Yep, I totally found the page: {}".format(request.url))
|
||||
```
|
||||
|
||||
### Static files
|
||||
|
||||
Static files can be served globally, under the blueprint prefix.
|
||||
|
||||
```python
|
||||
bp.static('/folder/to/serve', '/web/path')
|
||||
```
|
||||
|
||||
## Start and stop
|
||||
|
||||
Blueprints can run functions during the start and stop process of the server.
|
||||
If running in multiprocessor mode (more than 1 worker), these are triggered
|
||||
after the workers fork.
|
||||
|
||||
Available events are:
|
||||
|
||||
- `before_server_start`: Executed before the server begins to accept connections
|
||||
- `after_server_start`: Executed after the server begins to accept connections
|
||||
- `before_server_stop`: Executed before the server stops accepting connections
|
||||
- `after_server_stop`: Executed after the server is stopped and all requests are complete
|
||||
|
||||
```python
|
||||
bp = Blueprint('my_blueprint')
|
||||
|
||||
@bp.listener('before_server_start')
|
||||
async def setup_connection(app, loop):
|
||||
global database
|
||||
database = mysql.connect(host='127.0.0.1'...)
|
||||
|
||||
@bp.listener('after_server_stop')
|
||||
async def close_connection(app, loop):
|
||||
await database.close()
|
||||
```
|
||||
|
||||
## Use-case: API versioning
|
||||
|
||||
Blueprints can be very useful for API versioning, where one blueprint may point
|
||||
at `/v1/<routes>`, and another pointing at `/v2/<routes>`.
|
||||
|
||||
When a blueprint is initialised, it can take an optional `url_prefix` argument,
|
||||
which will be prepended to all routes defined on the blueprint. This feature
|
||||
can be used to implement our API versioning scheme.
|
||||
|
||||
```python
|
||||
# blueprints.py
|
||||
from sanic.response import text
|
||||
from sanic import Blueprint
|
||||
|
||||
blueprint_v1 = Blueprint('v1')
|
||||
blueprint_v2 = Blueprint('v2')
|
||||
|
||||
@blueprint_v1.route('/')
|
||||
async def api_v1_root(request):
|
||||
return text('Welcome to version 1 of our documentation')
|
||||
|
||||
@blueprint_v2.route('/')
|
||||
async def api_v2_root(request):
|
||||
return text('Welcome to version 2 of our documentation')
|
||||
```
|
||||
|
||||
When we register our blueprints on the app, the routes `/v1` and `/v2` will now
|
||||
point to the individual blueprints, which allows the creation of *sub-sites*
|
||||
for each API version.
|
||||
|
||||
```python
|
||||
# main.py
|
||||
from sanic import Sanic
|
||||
from blueprints import blueprint_v1, blueprint_v2
|
||||
|
||||
app = Sanic(__name__)
|
||||
app.blueprint(blueprint_v1)
|
||||
app.blueprint(blueprint_v2)
|
||||
|
||||
app.run(host='0.0.0.0', port=8000, debug=True)
|
||||
```
|
||||
|
||||
**Previous:** [Exceptions](exceptions.md)
|
||||
|
||||
**Next:** [Class-based views](class_based_views.md)
|
@ -1,112 +0,0 @@
|
||||
# Class-Based Views
|
||||
|
||||
Class-based views are simply classes which implement response behaviour to
|
||||
requests. They provide a way to compartmentalise handling of different HTTP
|
||||
request types at the same endpoint. Rather than defining and decorating three
|
||||
different handler functions, one for each of an endpoint's supported request
|
||||
type, the endpoint can be assigned a class-based view.
|
||||
|
||||
## Defining views
|
||||
|
||||
A class-based view should subclass `HTTPMethodView`. You can then implement
|
||||
class methods for every HTTP request type you want to support. If a request is
|
||||
received that has no defined method, a `405: Method not allowed` response will
|
||||
be generated.
|
||||
|
||||
To register a class-based view on an endpoint, the `app.add_route` method is
|
||||
used. The first argument should be the defined class with the method `as_view`
|
||||
invoked, and the second should be the URL endpoint.
|
||||
|
||||
The available methods are `get`, `post`, `put`, `patch`, and `delete`. A class
|
||||
using all these methods would look like the following.
|
||||
|
||||
```python
|
||||
from sanic import Sanic
|
||||
from sanic.views import HTTPMethodView
|
||||
from sanic.response import text
|
||||
|
||||
app = Sanic('some_name')
|
||||
|
||||
class SimpleView(HTTPMethodView):
|
||||
|
||||
def get(self, request):
|
||||
return text('I am get method')
|
||||
|
||||
def post(self, request):
|
||||
return text('I am post method')
|
||||
|
||||
def put(self, request):
|
||||
return text('I am put method')
|
||||
|
||||
def patch(self, request):
|
||||
return text('I am patch method')
|
||||
|
||||
def delete(self, request):
|
||||
return text('I am delete method')
|
||||
|
||||
app.add_route(SimpleView.as_view(), '/')
|
||||
|
||||
```
|
||||
|
||||
## URL parameters
|
||||
|
||||
If you need any URL parameters, as discussed in the routing guide, include them
|
||||
in the method definition.
|
||||
|
||||
```python
|
||||
class NameView(HTTPMethodView):
|
||||
|
||||
def get(self, request, name):
|
||||
return text('Hello {}'.format(name))
|
||||
|
||||
app.add_route(NameView.as_view(), '/<name>')
|
||||
```
|
||||
|
||||
## Decorators
|
||||
|
||||
If you want to add any decorators to the class, you can set the `decorators`
|
||||
class variable. These will be applied to the class when `as_view` is called.
|
||||
|
||||
```
|
||||
class ViewWithDecorator(HTTPMethodView):
|
||||
decorators = [some_decorator_here]
|
||||
|
||||
def get(self, request, name):
|
||||
return text('Hello I have a decorator')
|
||||
|
||||
app.add_route(ViewWithDecorator.as_view(), '/url')
|
||||
```
|
||||
|
||||
## Using CompositionView
|
||||
|
||||
As an alternative to the `HTTPMethodView`, you can use `CompositionView` to
|
||||
move handler functions outside of the view class.
|
||||
|
||||
Handler functions for each supported HTTP method are defined elsewhere in the
|
||||
source, and then added to the view using the `CompositionView.add` method. The
|
||||
first parameter is a list of HTTP methods to handle (e.g. `['GET', 'POST']`),
|
||||
and the second is the handler function. The following example shows
|
||||
`CompositionView` usage with both an external handler function and an inline
|
||||
lambda:
|
||||
|
||||
```python
|
||||
from sanic import Sanic
|
||||
from sanic.views import CompositionView
|
||||
from sanic.response import text
|
||||
|
||||
app = Sanic(__name__)
|
||||
|
||||
def get_handler(request):
|
||||
return text('I am a get method')
|
||||
|
||||
view = CompositionView()
|
||||
view.add(['GET'], get_handler)
|
||||
view.add(['POST', 'PUT'], lambda request: text('I am a post/put method'))
|
||||
|
||||
# Use the new view to handle requests to the base URL
|
||||
app.add_route(view, '/')
|
||||
```
|
||||
|
||||
**Previous:** [Blueprints](blueprints.md)
|
||||
|
||||
**Next:** [Cookies](cookies.md)
|
92
docs/conf.py
92
docs/conf.py
@ -10,8 +10,9 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Add support for Markdown documentation using Recommonmark
|
||||
from recommonmark.parser import CommonMarkParser
|
||||
|
||||
# Add support for auto-doc
|
||||
|
||||
|
||||
# Ensure that sanic is present in the path, to allow sphinx-apidoc to
|
||||
# autogenerate documentation from docstrings
|
||||
@ -20,26 +21,29 @@ sys.path.insert(0, root_directory)
|
||||
|
||||
import sanic
|
||||
|
||||
|
||||
# -- General configuration ------------------------------------------------
|
||||
|
||||
extensions = ['sphinx.ext.autodoc',
|
||||
'sphinx.ext.viewcode',
|
||||
'sphinx.ext.githubpages']
|
||||
extensions = [
|
||||
"sphinx.ext.autodoc",
|
||||
"m2r2",
|
||||
"enum_tools.autoenum",
|
||||
]
|
||||
|
||||
templates_path = ['_templates']
|
||||
templates_path = ["_templates"]
|
||||
|
||||
# Enable support for both Restructured Text and Markdown
|
||||
source_parsers = {'.md': CommonMarkParser}
|
||||
source_suffix = ['.rst', '.md']
|
||||
source_suffix = [".rst", ".md"]
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
master_doc = "index"
|
||||
|
||||
# General information about the project.
|
||||
project = 'Sanic'
|
||||
copyright = '2016, Sanic contributors'
|
||||
author = 'Sanic contributors'
|
||||
project = "Sanic"
|
||||
copyright = "2021, Sanic Community Organization"
|
||||
author = "Sanic Community Organization"
|
||||
|
||||
html_logo = "./_static/sanic-framework-logo-white-400x97.png"
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
@ -54,7 +58,7 @@ release = sanic.__version__
|
||||
#
|
||||
# This is also used if you do content translation via gettext catalogs.
|
||||
# Usually you set "language" from the command line for these cases.
|
||||
language = 'en'
|
||||
language = "en"
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
@ -62,32 +66,29 @@ language = 'en'
|
||||
#
|
||||
# modules.rst is generated by sphinx-apidoc but is unused. This suppresses
|
||||
# a warning about it.
|
||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', 'modules.rst']
|
||||
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store", "modules.rst"]
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
pygments_style = "sphinx"
|
||||
|
||||
# If true, `todo` and `todoList` produce output, else they produce nothing.
|
||||
todo_include_todos = False
|
||||
|
||||
|
||||
# -- Options for HTML output ----------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
html_theme = 'alabaster'
|
||||
html_theme = "sphinx_rtd_theme"
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
|
||||
html_static_path = ["_static"]
|
||||
html_css_files = ["custom.css"]
|
||||
# -- Options for HTMLHelp output ------------------------------------------
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'Sanicdoc'
|
||||
|
||||
htmlhelp_basename = "Sanicdoc"
|
||||
|
||||
# -- Options for LaTeX output ---------------------------------------------
|
||||
|
||||
@ -95,15 +96,12 @@ latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
#
|
||||
# 'papersize': 'letterpaper',
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#
|
||||
# 'pointsize': '10pt',
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#
|
||||
# 'preamble': '',
|
||||
|
||||
# Latex figure (float) alignment
|
||||
#
|
||||
# 'figure_align': 'htbp',
|
||||
@ -113,20 +111,20 @@ latex_elements = {
|
||||
# (source start file, target name, title,
|
||||
# author, documentclass [howto, manual, or own class]).
|
||||
latex_documents = [
|
||||
(master_doc, 'Sanic.tex', 'Sanic Documentation',
|
||||
'Sanic contributors', 'manual'),
|
||||
(
|
||||
master_doc,
|
||||
"Sanic.tex",
|
||||
"Sanic Documentation",
|
||||
"Sanic contributors",
|
||||
"manual",
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
# -- Options for manual page output ---------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
(master_doc, 'sanic', 'Sanic Documentation',
|
||||
[author], 1)
|
||||
]
|
||||
|
||||
man_pages = [(master_doc, "sanic", "Sanic Documentation", [author], 1)]
|
||||
|
||||
# -- Options for Texinfo output -------------------------------------------
|
||||
|
||||
@ -134,13 +132,17 @@ man_pages = [
|
||||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
(master_doc, 'Sanic', 'Sanic Documentation',
|
||||
author, 'Sanic', 'One line description of project.',
|
||||
'Miscellaneous'),
|
||||
(
|
||||
master_doc,
|
||||
"Sanic",
|
||||
"Sanic Documentation",
|
||||
author,
|
||||
"Sanic",
|
||||
"One line description of project.",
|
||||
"Miscellaneous",
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
|
||||
# -- Options for Epub output ----------------------------------------------
|
||||
|
||||
# Bibliographic Dublin Core info.
|
||||
@ -150,6 +152,18 @@ epub_publisher = author
|
||||
epub_copyright = copyright
|
||||
|
||||
# A list of files that should not be packed into the epub file.
|
||||
epub_exclude_files = ['search.html']
|
||||
epub_exclude_files = ["search.html"]
|
||||
|
||||
# -- Custom Settings -------------------------------------------------------
|
||||
|
||||
suppress_warnings = ["image.nonlocal_uri"]
|
||||
|
||||
|
||||
autodoc_typehints = "description"
|
||||
autodoc_default_options = {
|
||||
"member-order": "groupwise",
|
||||
}
|
||||
|
||||
html_theme_options = {
|
||||
"style_external_links": False,
|
||||
}
|
||||
|
@ -1,35 +0,0 @@
|
||||
# Contributing
|
||||
|
||||
Thank you for your interest! Sanic is always looking for contributors. If you
|
||||
don't feel comfortable contributing code, adding docstrings to the source files
|
||||
is very appreciated.
|
||||
|
||||
## Running tests
|
||||
|
||||
* `python -m pip install pytest`
|
||||
* `python -m pytest tests`
|
||||
|
||||
## Documentation
|
||||
|
||||
Sanic's documentation is built
|
||||
using [sphinx](http://www.sphinx-doc.org/en/1.5.1/). Guides are written in
|
||||
Markdown and can be found in the `docs` folder, while the module reference is
|
||||
automatically generated using `sphinx-apidoc`.
|
||||
|
||||
To generate the documentation from scratch:
|
||||
|
||||
```bash
|
||||
sphinx-apidoc -fo docs/_api/ sanic
|
||||
sphinx-build -b html docs docs/_build
|
||||
```
|
||||
|
||||
The HTML documentation will be created in the `docs/_build` folder.
|
||||
|
||||
## Warning
|
||||
|
||||
One of the main goals of Sanic is speed. Code that lowers the performance of
|
||||
Sanic without significant gains in usability, security, or features may not be
|
||||
merged. Please don't let this intimidate you! If you have any concerns about an
|
||||
idea, open an issue for discussion and help.
|
||||
|
||||
**Previous:** [Sanic extensions](extensions.md)
|
@ -1,79 +0,0 @@
|
||||
# Cookies
|
||||
|
||||
Cookies are pieces of data which persist inside a user's browser. Sanic can
|
||||
both read and write cookies, which are stored as key-value pairs.
|
||||
|
||||
## Reading cookies
|
||||
|
||||
A user's cookies can be accessed `Request` object's `cookie` dictionary.
|
||||
|
||||
```python
|
||||
from sanic.response import text
|
||||
|
||||
@app.route("/cookie")
|
||||
async def test(request):
|
||||
test_cookie = request.cookies.get('test')
|
||||
return text("Test cookie set to: {}".format(test_cookie))
|
||||
```
|
||||
|
||||
## Writing cookies
|
||||
|
||||
When returning a response, cookies can be set on the `Response` object.
|
||||
|
||||
```python
|
||||
from sanic.response import text
|
||||
|
||||
@app.route("/cookie")
|
||||
async def test(request):
|
||||
response = text("There's a cookie up in this response")
|
||||
response.cookies['test'] = 'It worked!'
|
||||
response.cookies['test']['domain'] = '.gotta-go-fast.com'
|
||||
response.cookies['test']['httponly'] = True
|
||||
return response
|
||||
```
|
||||
|
||||
## Deleting cookies
|
||||
|
||||
Cookies can be removed semantically or explicitly.
|
||||
|
||||
```python
|
||||
from sanic.response import text
|
||||
|
||||
@app.route("/cookie")
|
||||
async def test(request):
|
||||
response = text("Time to eat some cookies muahaha")
|
||||
|
||||
# This cookie will be set to expire in 0 seconds
|
||||
del response.cookies['kill_me']
|
||||
|
||||
# This cookie will self destruct in 5 seconds
|
||||
response.cookies['short_life'] = 'Glad to be here'
|
||||
response.cookies['short_life']['max-age'] = 5
|
||||
del response.cookies['favorite_color']
|
||||
|
||||
# This cookie will remain unchanged
|
||||
response.cookies['favorite_color'] = 'blue'
|
||||
response.cookies['favorite_color'] = 'pink'
|
||||
del response.cookies['favorite_color']
|
||||
|
||||
return response
|
||||
```
|
||||
|
||||
Response cookies can be set like dictionary values and have the following
|
||||
parameters available:
|
||||
|
||||
- `expires` (datetime): The time for the cookie to expire on the
|
||||
client's browser.
|
||||
- `path` (string): The subset of URLs to which this cookie applies. Defaults to /.
|
||||
- `comment` (string): A comment (metadata).
|
||||
- `domain` (string): Specifies the domain for which the cookie is valid. An
|
||||
explicitly specified domain must always start with a dot.
|
||||
- `max-age` (number): Number of seconds the cookie should live for.
|
||||
- `secure` (boolean): Specifies whether the cookie will only be sent via
|
||||
HTTPS.
|
||||
- `httponly` (boolean): Specifies whether the cookie cannot be read by
|
||||
Javascript.
|
||||
|
||||
**Previous:** [Class-based views](class_based_views.md)
|
||||
|
||||
**Next:** [Custom protocols](custom_protocol.md)
|
@ -1,76 +0,0 @@
|
||||
# Custom Protocols
|
||||
|
||||
*Note: this is advanced usage, and most readers will not need such functionality.*
|
||||
|
||||
You can change the behavior of Sanic's protocol by specifying a custom
|
||||
protocol, which should be a subclass
|
||||
of
|
||||
[asyncio.protocol](https://docs.python.org/3/library/asyncio-protocol.html#protocol-classes).
|
||||
This protocol can then be passed as the keyword argument `protocol` to the `sanic.run` method.
|
||||
|
||||
The constructor of the custom protocol class receives the following keyword
|
||||
arguments from Sanic.
|
||||
|
||||
- `loop`: an `asyncio`-compatible event loop.
|
||||
- `connections`: a `set` to store protocol objects. When Sanic receives
|
||||
`SIGINT` or `SIGTERM`, it executes `protocol.close_if_idle` for all protocol
|
||||
objects stored in this set.
|
||||
- `signal`: a `sanic.server.Signal` object with the `stopped` attribute. When
|
||||
Sanic receives `SIGINT` or `SIGTERM`, `signal.stopped` is assigned `True`.
|
||||
- `request_handler`: a coroutine that takes a `sanic.request.Request` object
|
||||
and a `response` callback as arguments.
|
||||
- `error_handler`: a `sanic.exceptions.Handler` which is called when exceptions
|
||||
are raised.
|
||||
- `request_timeout`: the number of seconds before a request times out.
|
||||
- `request_max_size`: an integer specifying the maximum size of a request, in bytes.
|
||||
|
||||
## Example
|
||||
|
||||
An error occurs in the default protocol if a handler function does not return
|
||||
an `HTTPResponse` object.
|
||||
|
||||
By overriding the `write_response` protocol method, if a handler returns a
|
||||
string it will be converted to an `HTTPResponse object`.
|
||||
|
||||
```python
|
||||
from sanic import Sanic
|
||||
from sanic.server import HttpProtocol
|
||||
from sanic.response import text
|
||||
|
||||
app = Sanic(__name__)
|
||||
|
||||
|
||||
class CustomHttpProtocol(HttpProtocol):
|
||||
|
||||
def __init__(self, *, loop, request_handler, error_handler,
|
||||
signal, connections, request_timeout, request_max_size):
|
||||
super().__init__(
|
||||
loop=loop, request_handler=request_handler,
|
||||
error_handler=error_handler, signal=signal,
|
||||
connections=connections, request_timeout=request_timeout,
|
||||
request_max_size=request_max_size)
|
||||
|
||||
def write_response(self, response):
|
||||
if isinstance(response, str):
|
||||
response = text(response)
|
||||
self.transport.write(
|
||||
response.output(self.request.version)
|
||||
)
|
||||
self.transport.close()
|
||||
|
||||
|
||||
@app.route('/')
|
||||
async def string(request):
|
||||
return 'string'
|
||||
|
||||
|
||||
@app.route('/1')
|
||||
async def response(request):
|
||||
return text('response')
|
||||
|
||||
app.run(host='0.0.0.0', port=8000, protocol=CustomHttpProtocol)
|
||||
```
|
||||
|
||||
**Previous:** [Cookies](cookies.md)
|
||||
|
||||
**Next:** [Testing](testing.md)
|
@ -1,59 +0,0 @@
|
||||
# Deploying
|
||||
|
||||
Deploying Sanic is made simple by the inbuilt webserver. After defining an
|
||||
instance of `sanic.Sanic`, we can call the `run` method with the following
|
||||
keyword arguments:
|
||||
|
||||
- `host` *(default `"127.0.0.1"`)*: Address to host the server on.
|
||||
- `port` *(default `8000`)*: Port to host the server on.
|
||||
- `debug` *(default `False`)*: Enables debug output (slows server).
|
||||
- `before_start` *(default `None`)*: Function or list of functions to be executed
|
||||
before the server starts accepting connections.
|
||||
- `after_start` *(default `None`)*: Function or list of functions to be executed
|
||||
after the server starts accepting connections.
|
||||
- `before_stop` *(default `None`)*: Function or list of functions to be
|
||||
executed when a stop signal is received before it is
|
||||
respected.
|
||||
- `after_stop` *(default `None`)*: Function or list of functions to be executed
|
||||
when all requests are complete.
|
||||
- `ssl` *(default `None`)*: `SSLContext` for SSL encryption of worker(s).
|
||||
- `sock` *(default `None`)*: Socket for the server to accept connections from.
|
||||
- `workers` *(default `1`)*: Number of worker processes to spawn.
|
||||
- `loop` *(default `None`)*: An `asyncio`-compatible event loop. If none is
|
||||
specified, Sanic creates its own event loop.
|
||||
- `protocol` *(default `HttpProtocol`)*: Subclass
|
||||
of
|
||||
[asyncio.protocol](https://docs.python.org/3/library/asyncio-protocol.html#protocol-classes).
|
||||
|
||||
## Workers
|
||||
|
||||
By default, Sanic listens in the main process using only one CPU core. To crank
|
||||
up the juice, just specify the number of workers in the `run` arguments.
|
||||
|
||||
```python
|
||||
app.run(host='0.0.0.0', port=1337, workers=4)
|
||||
```
|
||||
|
||||
Sanic will automatically spin up multiple processes and route traffic between
|
||||
them. We recommend as many workers as you have available cores.
|
||||
|
||||
## Running via command
|
||||
|
||||
If you like using command line arguments, you can launch a Sanic server by
|
||||
executing the module. For example, if you initialized Sanic as `app` in a file
|
||||
named `server.py`, you could run the server like so:
|
||||
|
||||
`python -m sanic server.app --host=0.0.0.0 --port=1337 --workers=4`
|
||||
|
||||
With this way of running sanic, it is not necessary to invoke `app.run` in your
|
||||
Python file. If you do, make sure you wrap it so that it only executes when
|
||||
directly run by the interpreter.
|
||||
|
||||
```python
|
||||
if __name__ == '__main__':
|
||||
app.run(host='0.0.0.0', port=1337, workers=4)
|
||||
```
|
||||
|
||||
**Previous:** [Request Data](request_data.md)
|
||||
|
||||
**Next:** [Static Files](static_files.md)
|
@ -1,49 +0,0 @@
|
||||
# Exceptions
|
||||
|
||||
Exceptions can be thrown from within request handlers and will automatically be
|
||||
handled by Sanic. Exceptions take a message as their first argument, and can
|
||||
also take a status code to be passed back in the HTTP response.
|
||||
|
||||
## Throwing an exception
|
||||
|
||||
To throw an exception, simply `raise` the relevant exception from the
|
||||
`sanic.exceptions` module.
|
||||
|
||||
```python
|
||||
from sanic.exceptions import ServerError
|
||||
|
||||
@app.route('/killme')
|
||||
def i_am_ready_to_die(request):
|
||||
raise ServerError("Something bad happened", status_code=500)
|
||||
```
|
||||
|
||||
## Handling exceptions
|
||||
|
||||
To override Sanic's default handling of an exception, the `@app.exception`
|
||||
decorator is used. The decorator expects a list of exceptions to handle as
|
||||
arguments. You can pass `SanicException` to catch them all! The decorated
|
||||
exception handler function must take a `Request` and `Exception` object as
|
||||
arguments.
|
||||
|
||||
```python
|
||||
from sanic.response import text
|
||||
from sanic.exceptions import NotFound
|
||||
|
||||
@app.exception(NotFound)
|
||||
def ignore_404s(request, exception):
|
||||
return text("Yep, I totally found the page: {}".format(request.url))
|
||||
```
|
||||
|
||||
## Useful exceptions
|
||||
|
||||
Some of the most useful exceptions are presented below:
|
||||
|
||||
- `NotFound`: called when a suitable route for the request isn't found.
|
||||
- `ServerError`: called when something goes wrong inside the server. This
|
||||
usually occurs if there is an exception raised in user code.
|
||||
|
||||
See the `sanic.exceptions` module for the full list of exceptions to throw.
|
||||
|
||||
**Previous:** [Middleware](middleware.md)
|
||||
|
||||
**Next:** [Blueprints](blueprints.md)
|
@ -1,12 +0,0 @@
|
||||
# Sanic Extensions
|
||||
|
||||
A list of Sanic extensions created by the community.
|
||||
|
||||
- [Sessions](https://github.com/subyraman/sanic_session): Support for sessions.
|
||||
Allows using redis, memcache or an in memory store.
|
||||
- [CORS](https://github.com/ashleysommer/sanic-cors): A port of flask-cors.
|
||||
- [Jinja2](https://github.com/lixxu/sanic-jinja2): Support for Jinja2 template.
|
||||
|
||||
**Previous:** [Testing](testing.md)
|
||||
|
||||
**Next:** [Contributing](contributing.md)
|
@ -1,29 +0,0 @@
|
||||
# Getting Started
|
||||
|
||||
Make sure you have both [pip](https://pip.pypa.io/en/stable/installing/) and at
|
||||
least version 3.5 of Python before starting. Sanic uses the new `async`/`await`
|
||||
syntax, so earlier versions of python won't work.
|
||||
|
||||
1. Install Sanic: `python3 -m pip install sanic`
|
||||
2. Create a file called `main.py` with the following code:
|
||||
|
||||
```python
|
||||
from sanic import Sanic
|
||||
from sanic.response import text
|
||||
|
||||
app = Sanic(__name__)
|
||||
|
||||
@app.route("/")
|
||||
async def test(request):
|
||||
return text('Hello world!')
|
||||
|
||||
app.run(host="0.0.0.0", port=8000, debug=True)
|
||||
```
|
||||
|
||||
3. Run the server: `python3 main.py`
|
||||
4. Open the address `http://0.0.0.0:8000` in your web browser. You should see
|
||||
the message *Hello world!*.
|
||||
|
||||
You now have a working Sanic server!
|
||||
|
||||
**Next:** [Routing](routing.md)
|
468
docs/index.html
Normal file
468
docs/index.html
Normal file
@ -0,0 +1,468 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta name="generator" content="Docutils 0.15.2: http://docutils.sourceforge.net/" />
|
||||
<title>index.rst</title>
|
||||
<style type="text/css">
|
||||
|
||||
/*
|
||||
:Author: David Goodger (goodger@python.org)
|
||||
:Id: $Id: html4css1.css 7952 2016-07-26 18:15:59Z milde $
|
||||
:Copyright: This stylesheet has been placed in the public domain.
|
||||
|
||||
Default cascading style sheet for the HTML output of Docutils.
|
||||
|
||||
See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
|
||||
customize this style sheet.
|
||||
*/
|
||||
|
||||
/* used to remove borders from tables and images */
|
||||
.borderless, table.borderless td, table.borderless th {
|
||||
border: 0 }
|
||||
|
||||
table.borderless td, table.borderless th {
|
||||
/* Override padding for "table.docutils td" with "! important".
|
||||
The right padding separates the table cells. */
|
||||
padding: 0 0.5em 0 0 ! important }
|
||||
|
||||
.first {
|
||||
/* Override more specific margin styles with "! important". */
|
||||
margin-top: 0 ! important }
|
||||
|
||||
.last, .with-subtitle {
|
||||
margin-bottom: 0 ! important }
|
||||
|
||||
.hidden {
|
||||
display: none }
|
||||
|
||||
.subscript {
|
||||
vertical-align: sub;
|
||||
font-size: smaller }
|
||||
|
||||
.superscript {
|
||||
vertical-align: super;
|
||||
font-size: smaller }
|
||||
|
||||
a.toc-backref {
|
||||
text-decoration: none ;
|
||||
color: black }
|
||||
|
||||
blockquote.epigraph {
|
||||
margin: 2em 5em ; }
|
||||
|
||||
dl.docutils dd {
|
||||
margin-bottom: 0.5em }
|
||||
|
||||
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Uncomment (and remove this text!) to get bold-faced definition list terms
|
||||
dl.docutils dt {
|
||||
font-weight: bold }
|
||||
*/
|
||||
|
||||
div.abstract {
|
||||
margin: 2em 5em }
|
||||
|
||||
div.abstract p.topic-title {
|
||||
font-weight: bold ;
|
||||
text-align: center }
|
||||
|
||||
div.admonition, div.attention, div.caution, div.danger, div.error,
|
||||
div.hint, div.important, div.note, div.tip, div.warning {
|
||||
margin: 2em ;
|
||||
border: medium outset ;
|
||||
padding: 1em }
|
||||
|
||||
div.admonition p.admonition-title, div.hint p.admonition-title,
|
||||
div.important p.admonition-title, div.note p.admonition-title,
|
||||
div.tip p.admonition-title {
|
||||
font-weight: bold ;
|
||||
font-family: sans-serif }
|
||||
|
||||
div.attention p.admonition-title, div.caution p.admonition-title,
|
||||
div.danger p.admonition-title, div.error p.admonition-title,
|
||||
div.warning p.admonition-title, .code .error {
|
||||
color: red ;
|
||||
font-weight: bold ;
|
||||
font-family: sans-serif }
|
||||
|
||||
/* Uncomment (and remove this text!) to get reduced vertical space in
|
||||
compound paragraphs.
|
||||
div.compound .compound-first, div.compound .compound-middle {
|
||||
margin-bottom: 0.5em }
|
||||
|
||||
div.compound .compound-last, div.compound .compound-middle {
|
||||
margin-top: 0.5em }
|
||||
*/
|
||||
|
||||
div.dedication {
|
||||
margin: 2em 5em ;
|
||||
text-align: center ;
|
||||
font-style: italic }
|
||||
|
||||
div.dedication p.topic-title {
|
||||
font-weight: bold ;
|
||||
font-style: normal }
|
||||
|
||||
div.figure {
|
||||
margin-left: 2em ;
|
||||
margin-right: 2em }
|
||||
|
||||
div.footer, div.header {
|
||||
clear: both;
|
||||
font-size: smaller }
|
||||
|
||||
div.line-block {
|
||||
display: block ;
|
||||
margin-top: 1em ;
|
||||
margin-bottom: 1em }
|
||||
|
||||
div.line-block div.line-block {
|
||||
margin-top: 0 ;
|
||||
margin-bottom: 0 ;
|
||||
margin-left: 1.5em }
|
||||
|
||||
div.sidebar {
|
||||
margin: 0 0 0.5em 1em ;
|
||||
border: medium outset ;
|
||||
padding: 1em ;
|
||||
background-color: #ffffee ;
|
||||
width: 40% ;
|
||||
float: right ;
|
||||
clear: right }
|
||||
|
||||
div.sidebar p.rubric {
|
||||
font-family: sans-serif ;
|
||||
font-size: medium }
|
||||
|
||||
div.system-messages {
|
||||
margin: 5em }
|
||||
|
||||
div.system-messages h1 {
|
||||
color: red }
|
||||
|
||||
div.system-message {
|
||||
border: medium outset ;
|
||||
padding: 1em }
|
||||
|
||||
div.system-message p.system-message-title {
|
||||
color: red ;
|
||||
font-weight: bold }
|
||||
|
||||
div.topic {
|
||||
margin: 2em }
|
||||
|
||||
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
|
||||
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
|
||||
margin-top: 0.4em }
|
||||
|
||||
h1.title {
|
||||
text-align: center }
|
||||
|
||||
h2.subtitle {
|
||||
text-align: center }
|
||||
|
||||
hr.docutils {
|
||||
width: 75% }
|
||||
|
||||
img.align-left, .figure.align-left, object.align-left, table.align-left {
|
||||
clear: left ;
|
||||
float: left ;
|
||||
margin-right: 1em }
|
||||
|
||||
img.align-right, .figure.align-right, object.align-right, table.align-right {
|
||||
clear: right ;
|
||||
float: right ;
|
||||
margin-left: 1em }
|
||||
|
||||
img.align-center, .figure.align-center, object.align-center {
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
table.align-center {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.align-left {
|
||||
text-align: left }
|
||||
|
||||
.align-center {
|
||||
clear: both ;
|
||||
text-align: center }
|
||||
|
||||
.align-right {
|
||||
text-align: right }
|
||||
|
||||
/* reset inner alignment in figures */
|
||||
div.align-right {
|
||||
text-align: inherit }
|
||||
|
||||
/* div.align-center * { */
|
||||
/* text-align: left } */
|
||||
|
||||
.align-top {
|
||||
vertical-align: top }
|
||||
|
||||
.align-middle {
|
||||
vertical-align: middle }
|
||||
|
||||
.align-bottom {
|
||||
vertical-align: bottom }
|
||||
|
||||
ol.simple, ul.simple {
|
||||
margin-bottom: 1em }
|
||||
|
||||
ol.arabic {
|
||||
list-style: decimal }
|
||||
|
||||
ol.loweralpha {
|
||||
list-style: lower-alpha }
|
||||
|
||||
ol.upperalpha {
|
||||
list-style: upper-alpha }
|
||||
|
||||
ol.lowerroman {
|
||||
list-style: lower-roman }
|
||||
|
||||
ol.upperroman {
|
||||
list-style: upper-roman }
|
||||
|
||||
p.attribution {
|
||||
text-align: right ;
|
||||
margin-left: 50% }
|
||||
|
||||
p.caption {
|
||||
font-style: italic }
|
||||
|
||||
p.credits {
|
||||
font-style: italic ;
|
||||
font-size: smaller }
|
||||
|
||||
p.label {
|
||||
white-space: nowrap }
|
||||
|
||||
p.rubric {
|
||||
font-weight: bold ;
|
||||
font-size: larger ;
|
||||
color: maroon ;
|
||||
text-align: center }
|
||||
|
||||
p.sidebar-title {
|
||||
font-family: sans-serif ;
|
||||
font-weight: bold ;
|
||||
font-size: larger }
|
||||
|
||||
p.sidebar-subtitle {
|
||||
font-family: sans-serif ;
|
||||
font-weight: bold }
|
||||
|
||||
p.topic-title {
|
||||
font-weight: bold }
|
||||
|
||||
pre.address {
|
||||
margin-bottom: 0 ;
|
||||
margin-top: 0 ;
|
||||
font: inherit }
|
||||
|
||||
pre.literal-block, pre.doctest-block, pre.math, pre.code {
|
||||
margin-left: 2em ;
|
||||
margin-right: 2em }
|
||||
|
||||
pre.code .ln { color: grey; } /* line numbers */
|
||||
pre.code, code { background-color: #eeeeee }
|
||||
pre.code .comment, code .comment { color: #5C6576 }
|
||||
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
|
||||
pre.code .literal.string, code .literal.string { color: #0C5404 }
|
||||
pre.code .name.builtin, code .name.builtin { color: #352B84 }
|
||||
pre.code .deleted, code .deleted { background-color: #DEB0A1}
|
||||
pre.code .inserted, code .inserted { background-color: #A3D289}
|
||||
|
||||
span.classifier {
|
||||
font-family: sans-serif ;
|
||||
font-style: oblique }
|
||||
|
||||
span.classifier-delimiter {
|
||||
font-family: sans-serif ;
|
||||
font-weight: bold }
|
||||
|
||||
span.interpreted {
|
||||
font-family: sans-serif }
|
||||
|
||||
span.option {
|
||||
white-space: nowrap }
|
||||
|
||||
span.pre {
|
||||
white-space: pre }
|
||||
|
||||
span.problematic {
|
||||
color: red }
|
||||
|
||||
span.section-subtitle {
|
||||
/* font-size relative to parent (h1..h6 element) */
|
||||
font-size: 80% }
|
||||
|
||||
table.citation {
|
||||
border-left: solid 1px gray;
|
||||
margin-left: 1px }
|
||||
|
||||
table.docinfo {
|
||||
margin: 2em 4em }
|
||||
|
||||
table.docutils {
|
||||
margin-top: 0.5em ;
|
||||
margin-bottom: 0.5em }
|
||||
|
||||
table.footnote {
|
||||
border-left: solid 1px black;
|
||||
margin-left: 1px }
|
||||
|
||||
table.docutils td, table.docutils th,
|
||||
table.docinfo td, table.docinfo th {
|
||||
padding-left: 0.5em ;
|
||||
padding-right: 0.5em ;
|
||||
vertical-align: top }
|
||||
|
||||
table.docutils th.field-name, table.docinfo th.docinfo-name {
|
||||
font-weight: bold ;
|
||||
text-align: left ;
|
||||
white-space: nowrap ;
|
||||
padding-left: 0 }
|
||||
|
||||
/* "booktabs" style (no vertical lines) */
|
||||
table.docutils.booktabs {
|
||||
border: 0px;
|
||||
border-top: 2px solid;
|
||||
border-bottom: 2px solid;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
table.docutils.booktabs * {
|
||||
border: 0px;
|
||||
}
|
||||
table.docutils.booktabs th {
|
||||
border-bottom: thin solid;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
|
||||
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
|
||||
font-size: 100% }
|
||||
|
||||
ul.auto-toc {
|
||||
list-style-type: none }
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="document">
|
||||
|
||||
|
||||
<div class="section" id="sanic">
|
||||
<h1>Sanic</h1>
|
||||
<p>Sanic is a Python 3.6+ 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.</p>
|
||||
<p>The goal of the project is to provide a simple way to get up and running a highly performant HTTP server that is easy to build, to expand, and ultimately to scale.</p>
|
||||
<p>Sanic is developed <a class="reference external" href="https://github.com/channelcat/sanic/">on GitHub</a>. Contributions are welcome!</p>
|
||||
<div class="section" id="sanic-aspires-to-be-simple">
|
||||
<h2>Sanic aspires to be simple</h2>
|
||||
<pre class="code python literal-block">
|
||||
<span class="keyword namespace">from</span> <span class="name namespace">sanic</span> <span class="keyword namespace">import</span> <span class="name">Sanic</span>
|
||||
<span class="keyword namespace">from</span> <span class="name namespace">sanic.response</span> <span class="keyword namespace">import</span> <span class="name">json</span>
|
||||
|
||||
<span class="name">app</span> <span class="operator">=</span> <span class="name">Sanic</span><span class="punctuation">()</span>
|
||||
|
||||
<span class="name decorator">@app.route</span><span class="punctuation">(</span><span class="literal string double">"/"</span><span class="punctuation">)</span>
|
||||
<span class="name">async</span> <span class="keyword">def</span> <span class="name function">test</span><span class="punctuation">(</span><span class="name">request</span><span class="punctuation">):</span>
|
||||
<span class="keyword">return</span> <span class="name">json</span><span class="punctuation">({</span><span class="literal string double">"hello"</span><span class="punctuation">:</span> <span class="literal string double">"world"</span><span class="punctuation">})</span>
|
||||
|
||||
<span class="keyword">if</span> <span class="name variable magic">__name__</span> <span class="operator">==</span> <span class="literal string double">"__main__"</span><span class="punctuation">:</span>
|
||||
<span class="name">app</span><span class="operator">.</span><span class="name">run</span><span class="punctuation">(</span><span class="name">host</span><span class="operator">=</span><span class="literal string double">"0.0.0.0"</span><span class="punctuation">,</span> <span class="name">port</span><span class="operator">=</span><span class="literal number integer">8000</span><span class="punctuation">)</span>
|
||||
</pre>
|
||||
<div class="admonition note">
|
||||
<p class="first admonition-title">Note</p>
|
||||
<p class="last">Sanic does not support Python 3.5 from version 19.6 and forward. However, version 18.12LTS is supported thru
|
||||
December 2020. Official Python support for version 3.5 is set to expire in September 2020.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="guides">
|
||||
<h1>Guides</h1>
|
||||
<div class="system-message">
|
||||
<p class="system-message-title">System Message: ERROR/3 (<tt class="docutils">E:/OneDrive/GitHub/sanic/docs/index.rst</tt>, line 6)</p>
|
||||
<p>Unknown directive type "toctree".</p>
|
||||
<pre class="literal-block">
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
sanic/getting_started
|
||||
sanic/config
|
||||
sanic/logging
|
||||
sanic/request_data
|
||||
sanic/response
|
||||
sanic/cookies
|
||||
sanic/routing
|
||||
sanic/blueprints
|
||||
sanic/static_files
|
||||
sanic/versioning
|
||||
sanic/exceptions
|
||||
sanic/middleware
|
||||
sanic/websocket
|
||||
sanic/decorators
|
||||
sanic/streaming
|
||||
sanic/class_based_views
|
||||
sanic/custom_protocol
|
||||
sanic/sockets
|
||||
sanic/ssl
|
||||
sanic/debug_mode
|
||||
sanic/testing
|
||||
sanic/deploying
|
||||
sanic/extensions
|
||||
sanic/examples
|
||||
sanic/changelog
|
||||
sanic/contributing
|
||||
sanic/api_reference
|
||||
sanic/asyncio_python37
|
||||
|
||||
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="module-documentation">
|
||||
<h1>Module Documentation</h1>
|
||||
<div class="system-message">
|
||||
<p class="system-message-title">System Message: ERROR/3 (<tt class="docutils">E:/OneDrive/GitHub/sanic/docs/index.rst</tt>, line 42)</p>
|
||||
<p>Unknown directive type "toctree".</p>
|
||||
<pre class="literal-block">
|
||||
.. toctree::
|
||||
|
||||
</pre>
|
||||
</div>
|
||||
<ul>
|
||||
<li><p class="first"><a href="#id1"><span class="problematic" id="id2">:ref:`genindex`</span></a></p>
|
||||
<div class="system-message" id="id1">
|
||||
<p class="system-message-title">System Message: ERROR/3 (<tt class="docutils">E:/OneDrive/GitHub/sanic/docs/index.rst</tt>, line 44); <em><a href="#id2">backlink</a></em></p>
|
||||
<p>Unknown interpreted text role "ref".</p>
|
||||
</div>
|
||||
</li>
|
||||
<li><p class="first"><a href="#id3"><span class="problematic" id="id4">:ref:`modindex`</span></a></p>
|
||||
<div class="system-message" id="id3">
|
||||
<p class="system-message-title">System Message: ERROR/3 (<tt class="docutils">E:/OneDrive/GitHub/sanic/docs/index.rst</tt>, line 45); <em><a href="#id4">backlink</a></em></p>
|
||||
<p>Unknown interpreted text role "ref".</p>
|
||||
</div>
|
||||
</li>
|
||||
<li><p class="first"><a href="#id5"><span class="problematic" id="id6">:ref:`search`</span></a></p>
|
||||
<div class="system-message" id="id5">
|
||||
<p class="system-message-title">System Message: ERROR/3 (<tt class="docutils">E:/OneDrive/GitHub/sanic/docs/index.rst</tt>, line 46); <em><a href="#id6">backlink</a></em></p>
|
||||
<p>Unknown interpreted text role "ref".</p>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
@ -1,25 +1,23 @@
|
||||
.. include:: ../README.rst
|
||||
|
||||
Guides
|
||||
======
|
||||
User Guide
|
||||
==========
|
||||
|
||||
To learn about using Sanic, checkout the `User Guide <https://sanicframework.org/guide/>`__.
|
||||
|
||||
API
|
||||
===
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:maxdepth: 3
|
||||
|
||||
getting_started
|
||||
routing
|
||||
request_data
|
||||
deploying
|
||||
static_files
|
||||
middleware
|
||||
exceptions
|
||||
blueprints
|
||||
class_based_views
|
||||
cookies
|
||||
custom_protocol
|
||||
testing
|
||||
extensions
|
||||
contributing
|
||||
👥 User Guide <https://sanicframework.org/guide/>
|
||||
sanic/api_reference
|
||||
💻 Source code <https://github.com/sanic-org/sanic/>
|
||||
sanic/changelog
|
||||
sanic/contributing
|
||||
❓ Support <https://community.sanicframework.org/>
|
||||
💬 Chat <https://discord.gg/FARQzAEMAA>
|
||||
|
||||
|
||||
Module Documentation
|
||||
@ -27,7 +25,5 @@ Module Documentation
|
||||
|
||||
.. toctree::
|
||||
|
||||
Module Reference <_api/sanic>
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`search`
|
||||
* :ref:`modindex`
|
||||
|
281
docs/make.bat
Normal file
281
docs/make.bat
Normal file
@ -0,0 +1,281 @@
|
||||
@ECHO OFF
|
||||
|
||||
REM Command file for Sphinx documentation
|
||||
|
||||
if "%SPHINXBUILD%" == "" (
|
||||
set SPHINXBUILD=sphinx-build
|
||||
)
|
||||
set BUILDDIR=_build
|
||||
set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
|
||||
set I18NSPHINXOPTS=%SPHINXOPTS% .
|
||||
if NOT "%PAPER%" == "" (
|
||||
set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
|
||||
set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
|
||||
)
|
||||
|
||||
if "%1" == "" goto help
|
||||
|
||||
if "%1" == "help" (
|
||||
:help
|
||||
echo.Please use `make ^<target^>` where ^<target^> is one of
|
||||
echo. html to make standalone HTML files
|
||||
echo. dirhtml to make HTML files named index.html in directories
|
||||
echo. singlehtml to make a single large HTML file
|
||||
echo. pickle to make pickle files
|
||||
echo. json to make JSON files
|
||||
echo. htmlhelp to make HTML files and a HTML help project
|
||||
echo. qthelp to make HTML files and a qthelp project
|
||||
echo. devhelp to make HTML files and a Devhelp project
|
||||
echo. epub to make an epub
|
||||
echo. epub3 to make an epub3
|
||||
echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
|
||||
echo. text to make text files
|
||||
echo. man to make manual pages
|
||||
echo. texinfo to make Texinfo files
|
||||
echo. gettext to make PO message catalogs
|
||||
echo. changes to make an overview over all changed/added/deprecated items
|
||||
echo. xml to make Docutils-native XML files
|
||||
echo. pseudoxml to make pseudoxml-XML files for display purposes
|
||||
echo. linkcheck to check all external links for integrity
|
||||
echo. doctest to run all doctests embedded in the documentation if enabled
|
||||
echo. coverage to run coverage check of the documentation if enabled
|
||||
echo. dummy to check syntax errors of document sources
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "clean" (
|
||||
for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
|
||||
del /q /s %BUILDDIR%\*
|
||||
goto end
|
||||
)
|
||||
|
||||
|
||||
REM Check if sphinx-build is available and fallback to Python version if any
|
||||
%SPHINXBUILD% 1>NUL 2>NUL
|
||||
if errorlevel 9009 goto sphinx_python
|
||||
goto sphinx_ok
|
||||
|
||||
:sphinx_python
|
||||
|
||||
set SPHINXBUILD=python -m sphinx.__init__
|
||||
%SPHINXBUILD% 2> nul
|
||||
if errorlevel 9009 (
|
||||
echo.
|
||||
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
||||
echo.installed, then set the SPHINXBUILD environment variable to point
|
||||
echo.to the full path of the 'sphinx-build' executable. Alternatively you
|
||||
echo.may add the Sphinx directory to PATH.
|
||||
echo.
|
||||
echo.If you don't have Sphinx installed, grab it from
|
||||
echo.http://sphinx-doc.org/
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
:sphinx_ok
|
||||
|
||||
|
||||
if "%1" == "html" (
|
||||
%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The HTML pages are in %BUILDDIR%/html.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "dirhtml" (
|
||||
%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "singlehtml" (
|
||||
%SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "pickle" (
|
||||
%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; now you can process the pickle files.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "json" (
|
||||
%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; now you can process the JSON files.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "htmlhelp" (
|
||||
%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; now you can run HTML Help Workshop with the ^
|
||||
.hhp project file in %BUILDDIR%/htmlhelp.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "qthelp" (
|
||||
%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; now you can run "qcollectiongenerator" with the ^
|
||||
.qhcp project file in %BUILDDIR%/qthelp, like this:
|
||||
echo.^> qcollectiongenerator %BUILDDIR%\qthelp\aiographite.qhcp
|
||||
echo.To view the help file:
|
||||
echo.^> assistant -collectionFile %BUILDDIR%\qthelp\aiographite.ghc
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "devhelp" (
|
||||
%SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "epub" (
|
||||
%SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The epub file is in %BUILDDIR%/epub.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "epub3" (
|
||||
%SPHINXBUILD% -b epub3 %ALLSPHINXOPTS% %BUILDDIR%/epub3
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The epub3 file is in %BUILDDIR%/epub3.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "latex" (
|
||||
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "latexpdf" (
|
||||
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
|
||||
cd %BUILDDIR%/latex
|
||||
make all-pdf
|
||||
cd %~dp0
|
||||
echo.
|
||||
echo.Build finished; the PDF files are in %BUILDDIR%/latex.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "latexpdfja" (
|
||||
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
|
||||
cd %BUILDDIR%/latex
|
||||
make all-pdf-ja
|
||||
cd %~dp0
|
||||
echo.
|
||||
echo.Build finished; the PDF files are in %BUILDDIR%/latex.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "text" (
|
||||
%SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The text files are in %BUILDDIR%/text.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "man" (
|
||||
%SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The manual pages are in %BUILDDIR%/man.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "texinfo" (
|
||||
%SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "gettext" (
|
||||
%SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "changes" (
|
||||
%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.The overview file is in %BUILDDIR%/changes.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "linkcheck" (
|
||||
%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Link check complete; look for any errors in the above output ^
|
||||
or in %BUILDDIR%/linkcheck/output.txt.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "doctest" (
|
||||
%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Testing of doctests in the sources finished, look at the ^
|
||||
results in %BUILDDIR%/doctest/output.txt.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "coverage" (
|
||||
%SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Testing of coverage in the sources finished, look at the ^
|
||||
results in %BUILDDIR%/coverage/python.txt.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "xml" (
|
||||
%SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The XML files are in %BUILDDIR%/xml.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "pseudoxml" (
|
||||
%SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.
|
||||
goto end
|
||||
)
|
||||
|
||||
if "%1" == "dummy" (
|
||||
%SPHINXBUILD% -b dummy %ALLSPHINXOPTS% %BUILDDIR%/dummy
|
||||
if errorlevel 1 exit /b 1
|
||||
echo.
|
||||
echo.Build finished. Dummy builder generates no files.
|
||||
goto end
|
||||
)
|
||||
|
||||
:end
|
@ -1,70 +0,0 @@
|
||||
# Middleware
|
||||
|
||||
Middleware are functions which are executed before or after requests to the
|
||||
server. They can be used to modify the *request to* or *response from*
|
||||
user-defined handler functions.
|
||||
|
||||
There are two types of middleware: request and response. Both are declared
|
||||
using the `@app.middleware` decorator, with the decorator's parameter being a
|
||||
string representing its type: `'request'` or `'response'`. Response middleware
|
||||
receives both the request and the response as arguments.
|
||||
|
||||
|
||||
The simplest middleware doesn't modify the request or response at all:
|
||||
|
||||
```python
|
||||
@app.middleware('request')
|
||||
async def print_on_request(request):
|
||||
print("I print when a request is received by the server")
|
||||
|
||||
@app.middleware('response')
|
||||
async def print_on_response(request, response):
|
||||
print("I print when a response is returned by the server")
|
||||
```
|
||||
|
||||
## Modifying the request or response
|
||||
|
||||
Middleware can modify the request or response parameter it is given, *as long
|
||||
as it does not return it*. The following example shows a practical use-case for
|
||||
this.
|
||||
|
||||
```python
|
||||
app = Sanic(__name__)
|
||||
|
||||
@app.middleware('response')
|
||||
async def custom_banner(request, response):
|
||||
response.headers["Server"] = "Fake-Server"
|
||||
|
||||
@app.middleware('response')
|
||||
async def prevent_xss(request, response):
|
||||
response.headers["x-xss-protection"] = "1; mode=block"
|
||||
|
||||
app.run(host="0.0.0.0", port=8000)
|
||||
```
|
||||
|
||||
The above code will apply the two middleware in order. First, the middleware
|
||||
**custom_banner** will change the HTTP response header *Server* to
|
||||
*Fake-Server*, and the second middleware **prevent_xss** will add the HTTP
|
||||
header for preventing Cross-Site-Scripting (XSS) attacks. These two functions
|
||||
are invoked *after* a user function returns a response.
|
||||
|
||||
## Responding early
|
||||
|
||||
If middleware returns a `HTTPResponse` object, the request will stop processing
|
||||
and the response will be returned. If this occurs to a request before the
|
||||
relevant user route handler is reached, the handler will never be called.
|
||||
Returning a response will also prevent any further middleware from running.
|
||||
|
||||
```python
|
||||
@app.middleware('request')
|
||||
async def halt_request(request):
|
||||
return text('I halted the request')
|
||||
|
||||
@app.middleware('response')
|
||||
async def halt_response(request, response):
|
||||
return text('I halted the response')
|
||||
```
|
||||
|
||||
**Previous:** [Static Files](static_files.md)
|
||||
|
||||
**Next:** [Exceptions](exceptions.md)
|
@ -1,95 +0,0 @@
|
||||
# Request Data
|
||||
|
||||
When an endpoint receives a HTTP request, the route function is passed a
|
||||
`Request` object.
|
||||
|
||||
The following variables are accessible as properties on `Request` objects:
|
||||
|
||||
- `json` (any) - JSON body
|
||||
|
||||
```python
|
||||
from sanic.response import json
|
||||
|
||||
@app.route("/json")
|
||||
def post_json(request):
|
||||
return json({ "received": True, "message": request.json })
|
||||
```
|
||||
|
||||
- `args` (dict) - Query string variables. A query string is the section of a
|
||||
URL that resembles `?key1=value1&key2=value2`. If that URL were to be parsed,
|
||||
the `args` dictionary would look like `{'key1': 'value1', 'key2': 'value2'}`.
|
||||
The request's `query_string` variable holds the unparsed string value.
|
||||
|
||||
```python
|
||||
from sanic.response import json
|
||||
|
||||
@app.route("/query_string")
|
||||
def query_string(request):
|
||||
return json({ "parsed": True, "args": request.args, "url": request.url, "query_string": request.query_string })
|
||||
```
|
||||
|
||||
- `files` (dictionary of `File` objects) - List of files that have a name, body, and type
|
||||
|
||||
```python
|
||||
from sanic.response import json
|
||||
|
||||
@app.route("/files")
|
||||
def post_json(request):
|
||||
test_file = request.files.get('test')
|
||||
|
||||
file_parameters = {
|
||||
'body': test_file.body,
|
||||
'name': test_file.name,
|
||||
'type': test_file.type,
|
||||
}
|
||||
|
||||
return json({ "received": True, "file_names": request.files.keys(), "test_file_parameters": file_parameters })
|
||||
```
|
||||
|
||||
- `form` (dict) - Posted form variables.
|
||||
|
||||
```python
|
||||
from sanic.response import json
|
||||
|
||||
@app.route("/form")
|
||||
def post_json(request):
|
||||
return json({ "received": True, "form_data": request.form, "test": request.form.get('test') })
|
||||
```
|
||||
|
||||
- `body` (bytes) - Posted raw body. This property allows retrieval of the
|
||||
request's raw data, regardless of content type.
|
||||
|
||||
```python
|
||||
from sanic.response import text
|
||||
|
||||
@app.route("/users", methods=["POST",])
|
||||
def create_user(request):
|
||||
return text("You are trying to create a user with the following POST: %s" % request.body)
|
||||
```
|
||||
|
||||
- `ip` (str) - IP address of the requester.
|
||||
|
||||
## Accessing values using `get` and `getlist`
|
||||
|
||||
The request properties which return a dictionary actually return a subclass of
|
||||
`dict` called `RequestParameters`. The key difference when using this object is
|
||||
the distinction between the `get` and `getlist` methods.
|
||||
|
||||
- `get(key, default=None)` operates as normal, except that when the value of
|
||||
the given key is a list, *only the first item is returned*.
|
||||
- `getlist(key, default=None)` operates as normal, *returning the entire list*.
|
||||
|
||||
```python
|
||||
from sanic.request import RequestParameters
|
||||
|
||||
args = RequestParameters()
|
||||
args['titles'] = ['Post 1', 'Post 2']
|
||||
|
||||
args.get('titles') # => 'Post 1'
|
||||
|
||||
args.getlist('titles') # => ['Post 1', 'Post 2']
|
||||
```
|
||||
|
||||
**Previous:** [Routing](routing.md)
|
||||
|
||||
**Next:** [Deploying](deploying.md)
|
111
docs/routing.md
111
docs/routing.md
@ -1,111 +0,0 @@
|
||||
# Routing
|
||||
|
||||
Routing allows the user to specify handler functions for different URL endpoints.
|
||||
|
||||
A basic route looks like the following, where `app` is an instance of the
|
||||
`Sanic` class:
|
||||
|
||||
```python
|
||||
from sanic.response import json
|
||||
|
||||
@app.route("/")
|
||||
async def test(request):
|
||||
return json({ "hello": "world" })
|
||||
```
|
||||
|
||||
When the url `http://server.url/` is accessed (the base url of the server), the
|
||||
final `/` is matched by the router to the handler function, `test`, which then
|
||||
returns a JSON object.
|
||||
|
||||
Sanic handler functions must be defined using the `async def` syntax, as they
|
||||
are asynchronous functions.
|
||||
|
||||
## Request parameters
|
||||
|
||||
Sanic comes with a basic router that supports request parameters.
|
||||
|
||||
To specify a parameter, surround it with angle quotes like so: `<PARAM>`.
|
||||
Request parameters will be passed to the route handler functions as keyword
|
||||
arguments.
|
||||
|
||||
```python
|
||||
from sanic.response import text
|
||||
|
||||
@app.route('/tag/<tag>')
|
||||
async def tag_handler(request, tag):
|
||||
return text('Tag - {}'.format(tag))
|
||||
```
|
||||
|
||||
To specify a type for the parameter, add a `:type` after the parameter name,
|
||||
inside the quotes. If the parameter does not match the specified type, Sanic
|
||||
will throw a `NotFound` exception, resulting in a `404: Page not found` error
|
||||
on the URL.
|
||||
|
||||
```python
|
||||
from sanic.response import text
|
||||
|
||||
@app.route('/number/<integer_arg:int>')
|
||||
async def integer_handler(request, integer_arg):
|
||||
return text('Integer - {}'.format(integer_arg))
|
||||
|
||||
@app.route('/number/<number_arg:number>')
|
||||
async def number_handler(request, number_arg):
|
||||
return text('Number - {}'.format(number_arg))
|
||||
|
||||
@app.route('/person/<name:[A-z]>')
|
||||
async def person_handler(request, name):
|
||||
return text('Person - {}'.format(name))
|
||||
|
||||
@app.route('/folder/<folder_id:[A-z0-9]{0,4}>')
|
||||
async def folder_handler(request, folder_id):
|
||||
return text('Folder - {}'.format(folder_id))
|
||||
|
||||
```
|
||||
|
||||
## HTTP request types
|
||||
|
||||
By default, a route defined on a URL will be used for all requests to that URL.
|
||||
However, the `@app.route` decorator accepts an optional parameter, `methods`,
|
||||
which restricts the handler function to the HTTP methods in the given list.
|
||||
|
||||
```python
|
||||
from sanic.response import text
|
||||
|
||||
@app.route('/post')
|
||||
async def post_handler(request, methods=['POST']):
|
||||
return text('POST request - {}'.format(request.json))
|
||||
|
||||
@app.route('/get')
|
||||
async def GET_handler(request, methods=['GET']):
|
||||
return text('GET request - {}'.format(request.args))
|
||||
|
||||
```
|
||||
|
||||
## The `add_route` method
|
||||
|
||||
As we have seen, routes are often specified using the `@app.route` decorator.
|
||||
However, this decorator is really just a wrapper for the `app.add_route`
|
||||
method, which is used as follows:
|
||||
|
||||
```python
|
||||
from sanic.response import text
|
||||
|
||||
# Define the handler functions
|
||||
async def handler1(request):
|
||||
return text('OK')
|
||||
|
||||
async def handler2(request, name):
|
||||
return text('Folder - {}'.format(name))
|
||||
|
||||
async def person_handler2(request, name):
|
||||
return text('Person - {}'.format(name))
|
||||
|
||||
# Add each handler function as a route
|
||||
app.add_route(handler1, '/test')
|
||||
app.add_route(handler2, '/folder/<name>')
|
||||
app.add_route(person_handler2, '/person/<name:[A-z]>', methods=['GET'])
|
||||
```
|
||||
|
||||
**Previous:** [Getting Started](getting_started.md)
|
||||
|
||||
**Next:** [Request Data](request_data.md)
|
33
docs/sanic/api/app.rst
Normal file
33
docs/sanic/api/app.rst
Normal file
@ -0,0 +1,33 @@
|
||||
Application
|
||||
===========
|
||||
|
||||
sanic.app
|
||||
---------
|
||||
|
||||
.. automodule:: sanic.app
|
||||
:members:
|
||||
:show-inheritance:
|
||||
:inherited-members:
|
||||
|
||||
sanic.config
|
||||
------------
|
||||
|
||||
.. automodule:: sanic.config
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
sanic.application.constants
|
||||
---------------------------
|
||||
|
||||
.. automodule:: sanic.application.constants
|
||||
:exclude-members: StrEnum
|
||||
:members:
|
||||
:show-inheritance:
|
||||
:inherited-members:
|
||||
|
||||
sanic.application.state
|
||||
-----------------------
|
||||
|
||||
.. automodule:: sanic.application.state
|
||||
:members:
|
||||
:show-inheritance:
|
17
docs/sanic/api/blueprints.rst
Normal file
17
docs/sanic/api/blueprints.rst
Normal file
@ -0,0 +1,17 @@
|
||||
Blueprints
|
||||
==========
|
||||
|
||||
sanic.blueprints
|
||||
----------------
|
||||
|
||||
.. automodule:: sanic.blueprints
|
||||
:members:
|
||||
:show-inheritance:
|
||||
:inherited-members:
|
||||
|
||||
sanic.blueprint_group
|
||||
---------------------
|
||||
|
||||
.. automodule:: sanic.blueprint_group
|
||||
:members:
|
||||
:special-members:
|
48
docs/sanic/api/core.rst
Normal file
48
docs/sanic/api/core.rst
Normal file
@ -0,0 +1,48 @@
|
||||
Core
|
||||
====
|
||||
|
||||
sanic.cookies
|
||||
-------------
|
||||
|
||||
.. automodule:: sanic.cookies
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
|
||||
sanic.handlers
|
||||
--------------
|
||||
|
||||
.. automodule:: sanic.handlers
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
|
||||
sanic.headers
|
||||
--------------
|
||||
|
||||
.. automodule:: sanic.headers
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
|
||||
sanic.request
|
||||
-------------
|
||||
|
||||
.. automodule:: sanic.request
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
sanic.response
|
||||
--------------
|
||||
|
||||
.. automodule:: sanic.response
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
|
||||
sanic.views
|
||||
-----------
|
||||
|
||||
.. automodule:: sanic.views
|
||||
:members:
|
||||
:show-inheritance:
|
16
docs/sanic/api/exceptions.rst
Normal file
16
docs/sanic/api/exceptions.rst
Normal file
@ -0,0 +1,16 @@
|
||||
Exceptions
|
||||
==========
|
||||
|
||||
sanic.errorpages
|
||||
----------------
|
||||
|
||||
.. automodule:: sanic.errorpages
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
sanic.exceptions
|
||||
----------------
|
||||
|
||||
.. automodule:: sanic.exceptions
|
||||
:members:
|
||||
:show-inheritance:
|
18
docs/sanic/api/router.rst
Normal file
18
docs/sanic/api/router.rst
Normal file
@ -0,0 +1,18 @@
|
||||
Routing
|
||||
=======
|
||||
|
||||
sanic_routing models
|
||||
--------------------
|
||||
|
||||
.. autoclass:: sanic_routing.route::Route
|
||||
:members:
|
||||
|
||||
.. autoclass:: sanic_routing.group::RouteGroup
|
||||
:members:
|
||||
|
||||
sanic.router
|
||||
------------
|
||||
|
||||
.. automodule:: sanic.router
|
||||
:members:
|
||||
:show-inheritance:
|
18
docs/sanic/api/server.rst
Normal file
18
docs/sanic/api/server.rst
Normal file
@ -0,0 +1,18 @@
|
||||
Sanic Server
|
||||
============
|
||||
|
||||
sanic.http
|
||||
----------
|
||||
|
||||
.. automodule:: sanic.http
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
|
||||
sanic.server
|
||||
------------
|
||||
|
||||
.. automodule:: sanic.server
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
16
docs/sanic/api/utility.rst
Normal file
16
docs/sanic/api/utility.rst
Normal file
@ -0,0 +1,16 @@
|
||||
Utility
|
||||
=======
|
||||
|
||||
sanic.compat
|
||||
------------
|
||||
|
||||
.. automodule:: sanic.compat
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
sanic.log
|
||||
---------
|
||||
|
||||
.. automodule:: sanic.log
|
||||
:members:
|
||||
:show-inheritance:
|
13
docs/sanic/api_reference.rst
Normal file
13
docs/sanic/api_reference.rst
Normal file
@ -0,0 +1,13 @@
|
||||
📑 API Reference
|
||||
================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
api/app
|
||||
api/blueprints
|
||||
api/core
|
||||
api/exceptions
|
||||
api/router
|
||||
api/server
|
||||
api/utility
|
16
docs/sanic/changelog.rst
Normal file
16
docs/sanic/changelog.rst
Normal file
@ -0,0 +1,16 @@
|
||||
📜 Changelog
|
||||
============
|
||||
|
||||
| 🔶 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
|
4
docs/sanic/contributing.rst
Normal file
4
docs/sanic/contributing.rst
Normal file
@ -0,0 +1,4 @@
|
||||
♥️ Contributing
|
||||
===============
|
||||
|
||||
.. include:: ../../CONTRIBUTING.rst
|
66
docs/sanic/releases/21/21.12.md
Normal file
66
docs/sanic/releases/21/21.12.md
Normal 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
|
50
docs/sanic/releases/21/21.9.md
Normal file
50
docs/sanic/releases/21/21.9.md
Normal file
@ -0,0 +1,50 @@
|
||||
## 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
|
||||
- [#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
|
||||
- [#2160](https://github.com/sanic-org/sanic/pull/2160) Add new 17 signals into server and request lifecycles
|
||||
- [#2162](https://github.com/sanic-org/sanic/pull/2162) Smarter `auto` fallback formatting upon exception
|
||||
- [#2184](https://github.com/sanic-org/sanic/pull/2184) Introduce implementation for copying a Blueprint
|
||||
- [#2200](https://github.com/sanic-org/sanic/pull/2200) Accept header parsing
|
||||
- [#2207](https://github.com/sanic-org/sanic/pull/2207) Log remote address if available
|
||||
- [#2209](https://github.com/sanic-org/sanic/pull/2209) Add convenience methods to BP groups
|
||||
- [#2216](https://github.com/sanic-org/sanic/pull/2216) Add default messages to SanicExceptions
|
||||
- [#2225](https://github.com/sanic-org/sanic/pull/2225) Type annotation convenience for annotated handlers with path parameters
|
||||
- [#2236](https://github.com/sanic-org/sanic/pull/2236) Allow Falsey (but not-None) responses from route handlers
|
||||
- [#2238](https://github.com/sanic-org/sanic/pull/2238) Add `exception` decorator to Blueprint Groups
|
||||
- [#2244](https://github.com/sanic-org/sanic/pull/2244) Explicit static directive for serving file or dir (ex: `static(..., resource_type="file")`)
|
||||
- [#2245](https://github.com/sanic-org/sanic/pull/2245) Close HTTP loop when connection task cancelled
|
||||
|
||||
### Bugfixes
|
||||
- [#2188](https://github.com/sanic-org/sanic/pull/2188) Fix the handling of the end of a chunked request
|
||||
- [#2195](https://github.com/sanic-org/sanic/pull/2195) Resolve unexpected error handling on static requests
|
||||
- [#2208](https://github.com/sanic-org/sanic/pull/2208) Make blueprint-based exceptions attach and trigger in a more intuitive manner
|
||||
- [#2211](https://github.com/sanic-org/sanic/pull/2211) Fixed for handling exceptions of asgi app call
|
||||
- [#2213](https://github.com/sanic-org/sanic/pull/2213) Fix bug where ws exceptions not being logged
|
||||
- [#2231](https://github.com/sanic-org/sanic/pull/2231) Cleaner closing of tasks by using `abort()` in strategic places to avoid dangling sockets
|
||||
- [#2247](https://github.com/sanic-org/sanic/pull/2247) Fix logging of auto-reload status in debug mode
|
||||
- [#2246](https://github.com/sanic-org/sanic/pull/2246) Account for BP with exception handler but no routes
|
||||
|
||||
### Developer infrastructure
|
||||
- [#2194](https://github.com/sanic-org/sanic/pull/2194) HTTP unit tests with raw client
|
||||
- [#2199](https://github.com/sanic-org/sanic/pull/2199) Switch to codeclimate
|
||||
- [#2214](https://github.com/sanic-org/sanic/pull/2214) Try Reopening Windows Tests
|
||||
- [#2229](https://github.com/sanic-org/sanic/pull/2229) Refactor `HttpProtocol` into a base class
|
||||
- [#2230](https://github.com/sanic-org/sanic/pull/2230) Refactor `server.py` into multi-file module
|
||||
|
||||
### Miscellaneous
|
||||
- [#2173](https://github.com/sanic-org/sanic/pull/2173) Remove Duplicated Dependencies and PEP 517 Support
|
||||
- [#2193](https://github.com/sanic-org/sanic/pull/2193), [#2196](https://github.com/sanic-org/sanic/pull/2196), [#2217](https://github.com/sanic-org/sanic/pull/2217) Type annotation changes
|
||||
|
||||
|
||||
|
55
docs/sanic/releases/22/22.12.md
Normal file
55
docs/sanic/releases/22/22.12.md
Normal 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
|
||||
|
52
docs/sanic/releases/22/22.3.md
Normal file
52
docs/sanic/releases/22/22.3.md
Normal 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`
|
54
docs/sanic/releases/22/22.6.md
Normal file
54
docs/sanic/releases/22/22.6.md
Normal 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
|
||||
|
74
docs/sanic/releases/22/22.9.md
Normal file
74
docs/sanic/releases/22/22.9.md
Normal 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
|
53
docs/sanic/releases/23/23.3.md
Normal file
53
docs/sanic/releases/23/23.3.md
Normal 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
|
33
docs/sanic/releases/23/23.6.md
Normal file
33
docs/sanic/releases/23/23.6.md
Normal 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:
|
@ -1,23 +0,0 @@
|
||||
# Static Files
|
||||
|
||||
Static files and directories, such as an image file, are served by Sanic when
|
||||
registered with the `app.static` method. The method takes an endpoint URL and a
|
||||
filename. The file specified will then be accessible via the given endpoint.
|
||||
|
||||
```python
|
||||
from sanic import Sanic
|
||||
app = Sanic(__name__)
|
||||
|
||||
# Serves files from the static folder to the URL /static
|
||||
app.static('/static', './static')
|
||||
|
||||
# Serves the file /home/ubuntu/test.png when the URL /the_best.png
|
||||
# is requested
|
||||
app.static('/the_best.png', '/home/ubuntu/test.png')
|
||||
|
||||
app.run(host="0.0.0.0", port=8000)
|
||||
```
|
||||
|
||||
**Previous:** [Deploying](deploying.md)
|
||||
|
||||
**Next:** [Middleware](middleware.md)
|
@ -1,55 +0,0 @@
|
||||
# Testing
|
||||
|
||||
Sanic endpoints can be tested locally using the `sanic.utils` module, which
|
||||
depends on the additional [aiohttp](https://aiohttp.readthedocs.io/en/stable/)
|
||||
library. The `sanic_endpoint_test` function runs a local server, issues a
|
||||
configurable request to an endpoint, and returns the result. It takes the
|
||||
following arguments:
|
||||
|
||||
- `app` An instance of a Sanic app.
|
||||
- `method` *(default `'get'`)* A string representing the HTTP method to use.
|
||||
- `uri` *(default `'/'`)* A string representing the endpoint to test.
|
||||
- `gather_request` *(default `True`)* A boolean which determines whether the
|
||||
original request will be returned by the function. If set to `True`, the
|
||||
return value is a tuple of `(request, response)`, if `False` only the
|
||||
response is returned.
|
||||
- `loop` *(default `None`)* The event loop to use.
|
||||
- `debug` *(default `False`)* A boolean which determines whether to run the
|
||||
server in debug mode.
|
||||
|
||||
The function further takes the `*request_args` and `**request_kwargs`, which
|
||||
are passed directly to the aiohttp ClientSession request. For example, to
|
||||
supply data with a GET request, `method` would be `get` and the keyword
|
||||
argument `params={'value', 'key'}` would be supplied. More information about
|
||||
the available arguments to aiohttp can be found
|
||||
[in the documentation for ClientSession](https://aiohttp.readthedocs.io/en/stable/client_reference.html#client-session).
|
||||
|
||||
Below is a complete example of an endpoint test,
|
||||
using [pytest](http://doc.pytest.org/en/latest/). The test checks that the
|
||||
`/challenge` endpoint responds to a GET request with a supplied challenge
|
||||
string.
|
||||
|
||||
```python
|
||||
import pytest
|
||||
import aiohttp
|
||||
from sanic.utils import sanic_endpoint_test
|
||||
|
||||
# Import the Sanic app, usually created with Sanic(__name__)
|
||||
from external_server import app
|
||||
|
||||
def test_endpoint_challenge():
|
||||
# Create the challenge data
|
||||
request_data = {'challenge': 'dummy_challenge'}
|
||||
|
||||
# Send the request to the endpoint, using the default `get` method
|
||||
request, response = sanic_endpoint_test(app,
|
||||
uri='/challenge',
|
||||
params=request_data)
|
||||
|
||||
# Assert that the server responds with the challenge string
|
||||
assert response.text == request_data['challenge']
|
||||
```
|
||||
|
||||
**Previous:** [Custom protocols](custom_protocol.md)
|
||||
|
||||
**Next:** [Sanic extensions](extensions.md)
|
@ -1,11 +1,8 @@
|
||||
FROM python:3.5
|
||||
MAINTAINER Channel Cat <channelcat@gmail.com>
|
||||
FROM sanicframework/sanic:LTS
|
||||
|
||||
ADD . /code
|
||||
RUN pip3 install git+https://github.com/channelcat/sanic
|
||||
RUN mkdir /srv
|
||||
COPY . /srv
|
||||
|
||||
EXPOSE 8000
|
||||
WORKDIR /srv
|
||||
|
||||
WORKDIR /code
|
||||
|
||||
CMD ["python", "simple_server.py"]
|
||||
CMD ["sanic", "simple_server.app"]
|
||||
|
19
examples/add_task_sanic.py
Normal file
19
examples/add_task_sanic.py
Normal file
@ -0,0 +1,19 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import asyncio
|
||||
|
||||
from sanic import Sanic
|
||||
|
||||
|
||||
app = Sanic("Example")
|
||||
|
||||
|
||||
async def notify_server_started_after_five_seconds():
|
||||
await asyncio.sleep(5)
|
||||
print("Server successfully started!")
|
||||
|
||||
|
||||
app.add_task(notify_server_started_after_five_seconds())
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(host="0.0.0.0", port=8000)
|
@ -1,28 +0,0 @@
|
||||
from sanic import Sanic
|
||||
from sanic.response import json
|
||||
|
||||
import aiohttp
|
||||
|
||||
app = Sanic(__name__)
|
||||
|
||||
async def fetch(session, url):
|
||||
"""
|
||||
Use session object to perform 'get' request on url
|
||||
"""
|
||||
async with session.get(url) as response:
|
||||
return await response.json()
|
||||
|
||||
|
||||
@app.route("/")
|
||||
async def test(request):
|
||||
"""
|
||||
Download and serve example JSON
|
||||
"""
|
||||
url = "https://api.github.com/repos/channelcat/sanic"
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
response = await fetch(session, url)
|
||||
return json(response)
|
||||
|
||||
|
||||
app.run(host="0.0.0.0", port=8000, workers=2)
|
29
examples/amending_request_object.py
Normal file
29
examples/amending_request_object.py
Normal file
@ -0,0 +1,29 @@
|
||||
from random import randint
|
||||
|
||||
from sanic import Sanic
|
||||
from sanic.response import text
|
||||
|
||||
|
||||
app = Sanic("Example")
|
||||
|
||||
|
||||
@app.middleware("request")
|
||||
def append_request(request):
|
||||
request.ctx.num = randint(0, 100)
|
||||
|
||||
|
||||
@app.get("/pop")
|
||||
def pop_handler(request):
|
||||
return text(request.ctx.num)
|
||||
|
||||
|
||||
@app.get("/key_exist")
|
||||
def key_exist_handler(request):
|
||||
# Check the key is exist or not
|
||||
if hasattr(request.ctx, "num"):
|
||||
return text("num 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)
|
44
examples/authorized_sanic.py
Normal file
44
examples/authorized_sanic.py
Normal file
@ -0,0 +1,44 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from functools import wraps
|
||||
|
||||
from sanic import Sanic
|
||||
from sanic.response import json
|
||||
|
||||
|
||||
app = Sanic("Example")
|
||||
|
||||
|
||||
def check_request_for_authorization_status(request):
|
||||
# Note: Define your check, for instance cookie, session.
|
||||
flag = True
|
||||
return flag
|
||||
|
||||
|
||||
def authorized(f):
|
||||
@wraps(f)
|
||||
async def decorated_function(request, *args, **kwargs):
|
||||
# run some method that checks the request
|
||||
# for the client's authorization status
|
||||
is_authorized = check_request_for_authorization_status(request)
|
||||
|
||||
if is_authorized:
|
||||
# the user is authorized.
|
||||
# run the handler method and return the response
|
||||
response = await f(request, *args, **kwargs)
|
||||
return response
|
||||
else:
|
||||
# the user is not authorized.
|
||||
return json({"status": "not_authorized"}, 403)
|
||||
|
||||
return decorated_function
|
||||
|
||||
|
||||
@app.route("/")
|
||||
@authorized
|
||||
async def test(request):
|
||||
return json({"status": "authorized"})
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(host="0.0.0.0", port=8000)
|
54
examples/blueprint_middlware_execution_order.py
Normal file
54
examples/blueprint_middlware_execution_order.py
Normal file
@ -0,0 +1,54 @@
|
||||
from sanic import Blueprint, Sanic
|
||||
from sanic.response import text
|
||||
|
||||
|
||||
"""
|
||||
Demonstrates that blueprint request middleware are executed in the order they
|
||||
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
|
||||
"""
|
||||
|
||||
app = Sanic("Example")
|
||||
|
||||
bp = Blueprint("bp_example")
|
||||
|
||||
|
||||
@bp.on_request
|
||||
def request_middleware_1(request):
|
||||
print("1")
|
||||
|
||||
|
||||
@bp.on_request
|
||||
def request_middleware_2(request):
|
||||
print("2")
|
||||
|
||||
|
||||
@bp.on_request
|
||||
def request_middleware_3(request):
|
||||
print("3")
|
||||
|
||||
|
||||
@bp.on_response
|
||||
def resp_middleware_4(request, response):
|
||||
print("4")
|
||||
|
||||
|
||||
@bp.on_response
|
||||
def resp_middleware_5(request, response):
|
||||
print("5")
|
||||
|
||||
|
||||
@bp.on_response
|
||||
def resp_middleware_6(request, response):
|
||||
print("6")
|
||||
|
||||
|
||||
@bp.route("/")
|
||||
def pop_handler(request):
|
||||
return text("hello world")
|
||||
|
||||
|
||||
app.blueprint(bp, url_prefix="/bp")
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(host="0.0.0.0", port=8000, debug=True, auto_reload=False)
|
@ -1,24 +1,41 @@
|
||||
from sanic import Sanic
|
||||
from sanic import Blueprint
|
||||
from sanic.response import json, text
|
||||
from sanic import Blueprint, Sanic
|
||||
from sanic.response import file, json
|
||||
|
||||
|
||||
app = Sanic(__name__)
|
||||
blueprint = Blueprint('name', url_prefix='/my_blueprint')
|
||||
blueprint2 = Blueprint('name', url_prefix='/my_blueprint2')
|
||||
app = Sanic("Example")
|
||||
blueprint = Blueprint("bp_example", url_prefix="/my_blueprint")
|
||||
blueprint2 = Blueprint("bp_example2", url_prefix="/my_blueprint2")
|
||||
blueprint3 = Blueprint("bp_example3", url_prefix="/my_blueprint3")
|
||||
|
||||
|
||||
@blueprint.route('/foo')
|
||||
@blueprint.route("/foo")
|
||||
async def foo(request):
|
||||
return json({'msg': 'hi from blueprint'})
|
||||
return json({"msg": "hi from blueprint"})
|
||||
|
||||
|
||||
@blueprint2.route('/foo')
|
||||
@blueprint2.route("/foo")
|
||||
async def foo2(request):
|
||||
return json({'msg': 'hi from blueprint2'})
|
||||
return json({"msg": "hi from blueprint2"})
|
||||
|
||||
|
||||
app.register_blueprint(blueprint)
|
||||
app.register_blueprint(blueprint2)
|
||||
@blueprint3.route("/foo")
|
||||
async def index(request):
|
||||
return await file("websocket.html")
|
||||
|
||||
app.run(host="0.0.0.0", port=8000, debug=True)
|
||||
|
||||
@app.websocket("/feed")
|
||||
async def foo3(request, ws):
|
||||
while True:
|
||||
data = "hello!"
|
||||
print("Sending: " + data)
|
||||
await ws.send(data)
|
||||
data = await ws.recv()
|
||||
print("Received: " + data)
|
||||
|
||||
|
||||
app.blueprint(blueprint)
|
||||
app.blueprint(blueprint2)
|
||||
app.blueprint(blueprint3)
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(host="0.0.0.0", port=9999, debug=True)
|
||||
|
@ -1,41 +0,0 @@
|
||||
"""
|
||||
Example of caching using aiocache package. To run it you will need a Redis
|
||||
instance running in localhost:6379.
|
||||
|
||||
Running this example you will see that the first call lasts 3 seconds and
|
||||
the rest are instant because the value is retrieved from the Redis.
|
||||
|
||||
If you want more info about the package check
|
||||
https://github.com/argaen/aiocache
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import aiocache
|
||||
|
||||
from sanic import Sanic
|
||||
from sanic.response import json
|
||||
from sanic.log import log
|
||||
from aiocache import cached
|
||||
from aiocache.serializers import JsonSerializer
|
||||
|
||||
app = Sanic(__name__)
|
||||
|
||||
aiocache.settings.set_defaults(
|
||||
class_="aiocache.RedisCache"
|
||||
)
|
||||
|
||||
|
||||
@cached(key="my_custom_key", serializer=JsonSerializer())
|
||||
async def expensive_call():
|
||||
log.info("Expensive has been called")
|
||||
await asyncio.sleep(3)
|
||||
return {"test": True}
|
||||
|
||||
|
||||
@app.route("/")
|
||||
async def test(request):
|
||||
log.info("Received GET /")
|
||||
return json(await expensive_call())
|
||||
|
||||
|
||||
app.run(host="0.0.0.0", port=8000, loop=asyncio.get_event_loop())
|
22
examples/delayed_response.py
Normal file
22
examples/delayed_response.py
Normal file
@ -0,0 +1,22 @@
|
||||
from asyncio import sleep
|
||||
|
||||
from sanic import Sanic, response
|
||||
|
||||
|
||||
app = Sanic("DelayedResponseApp", strict_slashes=True)
|
||||
app.config.AUTO_EXTEND = False
|
||||
|
||||
|
||||
@app.get("/")
|
||||
async def handler(request):
|
||||
return response.redirect("/sleep/3")
|
||||
|
||||
|
||||
@app.get("/sleep/<t:float>")
|
||||
async def handler2(request, t=0.3):
|
||||
await sleep(t)
|
||||
return response.text(f"Slept {t:.1f} seconds.\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(host="0.0.0.0", port=8000)
|
@ -1,23 +1,23 @@
|
||||
"""
|
||||
Example intercepting uncaught exceptions using Sanic's error handler framework.
|
||||
|
||||
This may be useful for developers wishing to use Sentry, Airbrake, etc.
|
||||
or a custom system to log and monitor unexpected errors in production.
|
||||
|
||||
First we create our own class inheriting from Handler in sanic.exceptions,
|
||||
and pass in an instance of it when we create our Sanic instance. Inside this
|
||||
class' default handler, we can do anything including sending exceptions to
|
||||
an external service.
|
||||
"""
|
||||
from sanic.exceptions import Handler, SanicException
|
||||
from sanic.exceptions import SanicException
|
||||
from sanic.handlers import ErrorHandler
|
||||
|
||||
|
||||
"""
|
||||
Imports and code relevant for our CustomHandler class
|
||||
(Ordinarily this would be in a separate file)
|
||||
"""
|
||||
|
||||
|
||||
class CustomHandler(Handler):
|
||||
|
||||
class CustomHandler(ErrorHandler):
|
||||
def default(self, request, exception):
|
||||
# Here, we have access to the exception object
|
||||
# and can do anything with it (log, send to external service, etc)
|
||||
@ -38,20 +38,18 @@ server's error_handler to an instance of our CustomHandler
|
||||
"""
|
||||
|
||||
from sanic import Sanic
|
||||
from sanic.response import json
|
||||
|
||||
app = Sanic(__name__)
|
||||
|
||||
handler = CustomHandler(sanic=app)
|
||||
app.error_handler = handler
|
||||
handler = CustomHandler()
|
||||
app = Sanic("Example", error_handler=handler)
|
||||
|
||||
|
||||
@app.route("/")
|
||||
async def test(request):
|
||||
# Here, something occurs which causes an unexpected exception
|
||||
# This exception will flow to our custom handler.
|
||||
1 / 0
|
||||
return json({"test": True})
|
||||
raise SanicException("You Broke It!")
|
||||
|
||||
|
||||
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)
|
||||
|
13
examples/hello_world.py
Normal file
13
examples/hello_world.py
Normal file
@ -0,0 +1,13 @@
|
||||
from sanic import Sanic, response
|
||||
|
||||
|
||||
app = Sanic("Example")
|
||||
|
||||
|
||||
@app.route("/")
|
||||
async def test(request):
|
||||
return response.json({"test": True})
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(host="0.0.0.0", port=8000)
|
73
examples/http_redirect.py
Normal file
73
examples/http_redirect.py
Normal file
@ -0,0 +1,73 @@
|
||||
from sanic import Sanic, response, text
|
||||
from sanic.handlers import ErrorHandler
|
||||
from sanic.server.async_server import AsyncioServer
|
||||
|
||||
|
||||
HTTP_PORT = 9999
|
||||
HTTPS_PORT = 8888
|
||||
|
||||
http = Sanic("http")
|
||||
http.config.SERVER_NAME = f"localhost:{HTTP_PORT}"
|
||||
https = Sanic("https")
|
||||
https.config.SERVER_NAME = f"localhost:{HTTPS_PORT}"
|
||||
|
||||
|
||||
@https.get("/foo")
|
||||
def foo(request):
|
||||
return text("foo")
|
||||
|
||||
|
||||
@https.get("/bar")
|
||||
def bar(request):
|
||||
return text("bar")
|
||||
|
||||
|
||||
@http.get("/<path:path>")
|
||||
def proxy(request, path):
|
||||
url = request.app.url_for(
|
||||
"proxy",
|
||||
path=path,
|
||||
_server=https.config.SERVER_NAME,
|
||||
_external=True,
|
||||
_scheme="https",
|
||||
)
|
||||
return response.redirect(url)
|
||||
|
||||
|
||||
@https.main_process_start
|
||||
async def start(app, _):
|
||||
http_server = await http.create_server(
|
||||
port=HTTP_PORT, return_asyncio_server=True
|
||||
)
|
||||
app.add_task(runner(http, http_server))
|
||||
app.ctx.http_server = http_server
|
||||
app.ctx.http = http
|
||||
|
||||
|
||||
@https.main_process_stop
|
||||
async def stop(app, _):
|
||||
await app.ctx.http_server.before_stop()
|
||||
await app.ctx.http_server.close()
|
||||
for connection in app.ctx.http_server.connections:
|
||||
connection.close_if_idle()
|
||||
await app.ctx.http_server.after_stop()
|
||||
app.ctx.http = False
|
||||
|
||||
|
||||
async def runner(app: Sanic, app_server: AsyncioServer):
|
||||
app.is_running = True
|
||||
try:
|
||||
app.signalize()
|
||||
app.finalize()
|
||||
ErrorHandler.finalize(app.error_handler)
|
||||
app_server.init = True
|
||||
|
||||
await app_server.before_start()
|
||||
await app_server.after_start()
|
||||
await app_server.serve_forever()
|
||||
finally:
|
||||
app.is_running = False
|
||||
app.is_stopping = True
|
||||
|
||||
if __name__ == "__main__":
|
||||
https.run(port=HTTPS_PORT, debug=True)
|
@ -1,18 +0,0 @@
|
||||
## To use this example:
|
||||
# curl -d '{"name": "John Doe"}' localhost:8000
|
||||
|
||||
from sanic import Sanic
|
||||
from sanic.response import html
|
||||
from jinja2 import Template
|
||||
|
||||
template = Template('Hello {{ name }}!')
|
||||
|
||||
app = Sanic(__name__)
|
||||
|
||||
@app.route('/')
|
||||
async def test(request):
|
||||
data = request.json
|
||||
return html(template.render(**data))
|
||||
|
||||
|
||||
app.run(host="0.0.0.0", port=8000)
|
@ -1,24 +1,30 @@
|
||||
import asyncio
|
||||
|
||||
import httpx
|
||||
|
||||
from sanic import Sanic
|
||||
from sanic.response import json
|
||||
|
||||
import asyncio
|
||||
import aiohttp
|
||||
|
||||
app = Sanic(__name__)
|
||||
app = Sanic("Example")
|
||||
|
||||
sem = None
|
||||
|
||||
def init(sanic, loop):
|
||||
|
||||
@app.before_server_start
|
||||
def init(sanic, _):
|
||||
global sem
|
||||
CONCURRENCY_PER_WORKER = 4
|
||||
sem = asyncio.Semaphore(CONCURRENCY_PER_WORKER, loop=loop)
|
||||
concurrency_per_worker = 4
|
||||
sem = asyncio.Semaphore(concurrency_per_worker)
|
||||
|
||||
|
||||
async def bounded_fetch(session, url):
|
||||
"""
|
||||
Use session object to perform 'get' request on url
|
||||
"""
|
||||
async with sem, session.get(url) as response:
|
||||
return await response.json()
|
||||
async with sem:
|
||||
response = await session.get(url)
|
||||
return response.json()
|
||||
|
||||
|
||||
@app.route("/")
|
||||
@ -26,11 +32,12 @@ async def test(request):
|
||||
"""
|
||||
Download and serve example JSON
|
||||
"""
|
||||
url = "https://api.github.com/repos/channelcat/sanic"
|
||||
url = "https://api.github.com/repos/sanic-org/sanic"
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with httpx.AsyncClient() as session:
|
||||
response = await bounded_fetch(session, url)
|
||||
return json(response)
|
||||
|
||||
|
||||
app.run(host="0.0.0.0", port=8000, workers=2, before_start=init)
|
||||
if __name__ == "__main__":
|
||||
app.run(host="0.0.0.0", port=8000, workers=2)
|
||||
|
74
examples/log_request_id.py
Normal file
74
examples/log_request_id.py
Normal file
@ -0,0 +1,74 @@
|
||||
import logging
|
||||
|
||||
from contextvars import ContextVar
|
||||
|
||||
from sanic import Sanic, response
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class RequestIdFilter(logging.Filter):
|
||||
def filter(self, record):
|
||||
try:
|
||||
record.request_id = app.ctx.request_id.get(None) or "n/a"
|
||||
except AttributeError:
|
||||
record.request_id = "n/a"
|
||||
return True
|
||||
|
||||
|
||||
LOG_SETTINGS = {
|
||||
"version": 1,
|
||||
"disable_existing_loggers": False,
|
||||
"handlers": {
|
||||
"console": {
|
||||
"class": "logging.StreamHandler",
|
||||
"level": "DEBUG",
|
||||
"formatter": "default",
|
||||
"filters": ["requestid"],
|
||||
},
|
||||
},
|
||||
"filters": {
|
||||
"requestid": {
|
||||
"()": RequestIdFilter,
|
||||
},
|
||||
},
|
||||
"formatters": {
|
||||
"default": {
|
||||
"format": "%(asctime)s %(levelname)s %(name)s:%(lineno)d %(request_id)s | %(message)s",
|
||||
},
|
||||
},
|
||||
"loggers": {
|
||||
"": {"level": "DEBUG", "handlers": ["console"], "propagate": True},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
app = Sanic("Example", log_config=LOG_SETTINGS)
|
||||
|
||||
|
||||
@app.on_request
|
||||
async def set_request_id(request):
|
||||
request.app.ctx.request_id.set(request.id)
|
||||
log.info(f"Setting {request.id=}")
|
||||
|
||||
|
||||
@app.on_response
|
||||
async def set_request_header(request, response):
|
||||
response.headers["X-Request-ID"] = request.id
|
||||
|
||||
|
||||
@app.route("/")
|
||||
async def test(request):
|
||||
log.debug("X-Request-ID: %s", request.id)
|
||||
log.info("Hello from test!")
|
||||
return response.json({"test": True})
|
||||
|
||||
|
||||
@app.before_server_start
|
||||
def setup(app, loop):
|
||||
app.ctx.request_id = ContextVar("request_id")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(port=9999, debug=True)
|
60
examples/logdna_example.py
Normal file
60
examples/logdna_example.py
Normal file
@ -0,0 +1,60 @@
|
||||
import logging
|
||||
import socket
|
||||
|
||||
from os import getenv
|
||||
from platform import node
|
||||
from uuid import getnode as get_mac
|
||||
|
||||
from logdna import LogDNAHandler
|
||||
|
||||
from sanic import Sanic
|
||||
from sanic.request import Request
|
||||
from sanic.response import json
|
||||
|
||||
|
||||
log = logging.getLogger("logdna")
|
||||
log.setLevel(logging.INFO)
|
||||
|
||||
|
||||
def get_my_ip_address(remote_server="google.com"):
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
|
||||
s.connect((remote_server, 80))
|
||||
return s.getsockname()[0]
|
||||
|
||||
|
||||
def get_mac_address():
|
||||
h = iter(hex(get_mac())[2:].zfill(12))
|
||||
return ":".join(i + next(h) for i in h)
|
||||
|
||||
|
||||
logdna_options = {
|
||||
"app": __name__,
|
||||
"index_meta": True,
|
||||
"hostname": node(),
|
||||
"ip": get_my_ip_address(),
|
||||
"mac": get_mac_address(),
|
||||
}
|
||||
|
||||
logdna_handler = LogDNAHandler(
|
||||
getenv("LOGDNA_API_KEY"), options=logdna_options
|
||||
)
|
||||
|
||||
logdna = logging.getLogger(__name__)
|
||||
logdna.setLevel(logging.INFO)
|
||||
logdna.addHandler(logdna_handler)
|
||||
|
||||
app = Sanic("Example")
|
||||
|
||||
|
||||
@app.middleware
|
||||
def log_request(request: Request):
|
||||
logdna.info("I was Here with a new Request to URL: {}".format(request.url))
|
||||
|
||||
|
||||
@app.route("/")
|
||||
def default(request):
|
||||
return json({"response": "I was here"})
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(host="0.0.0.0", port=getenv("PORT", 8080))
|
30
examples/modify_header_example.py
Normal file
30
examples/modify_header_example.py
Normal file
@ -0,0 +1,30 @@
|
||||
"""
|
||||
Modify header or status in response
|
||||
"""
|
||||
|
||||
from sanic import Sanic, response
|
||||
|
||||
|
||||
app = Sanic("Example")
|
||||
|
||||
|
||||
@app.route("/")
|
||||
def handle_request(request):
|
||||
return response.json(
|
||||
{"message": "Hello world!"},
|
||||
headers={"X-Served-By": "sanic"},
|
||||
status=200,
|
||||
)
|
||||
|
||||
|
||||
@app.route("/unauthorized")
|
||||
def handle_request(request):
|
||||
return response.json(
|
||||
{"message": "You are not authorized"},
|
||||
headers={"X-Served-By": "sanic"},
|
||||
status=404,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(host="0.0.0.0", port=8000, debug=True)
|
@ -1,23 +1,24 @@
|
||||
from sanic import Sanic
|
||||
from sanic.response import text
|
||||
import json
|
||||
import logging
|
||||
|
||||
from sanic import Sanic, text
|
||||
|
||||
|
||||
logging_format = "[%(asctime)s] %(process)d-%(levelname)s "
|
||||
logging_format += "%(module)s::%(funcName)s():l%(lineno)d: "
|
||||
logging_format += "%(message)s"
|
||||
|
||||
logging.basicConfig(
|
||||
format=logging_format,
|
||||
level=logging.DEBUG
|
||||
)
|
||||
logging.basicConfig(format=logging_format, level=logging.DEBUG)
|
||||
log = logging.getLogger()
|
||||
|
||||
# Set logger to override default basicConfig
|
||||
sanic = Sanic()
|
||||
@sanic.route("/")
|
||||
app = Sanic("app")
|
||||
|
||||
|
||||
@app.route("/")
|
||||
def test(request):
|
||||
log.info("received request; responding with 'hey'")
|
||||
return text("hey")
|
||||
|
||||
sanic.run(host="0.0.0.0", port=8000)
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(host="0.0.0.0", port=8000)
|
||||
|
53
examples/pytest_xdist.py
Normal file
53
examples/pytest_xdist.py
Normal file
@ -0,0 +1,53 @@
|
||||
"""pytest-xdist example for sanic server
|
||||
|
||||
Install testing tools:
|
||||
|
||||
$ pip install pytest pytest-xdist
|
||||
|
||||
Run with xdist params:
|
||||
|
||||
$ pytest examples/pytest_xdist.py -n 8 # 8 workers
|
||||
"""
|
||||
import re
|
||||
|
||||
import pytest
|
||||
|
||||
from sanic_testing import SanicTestClient
|
||||
from sanic_testing.testing import PORT as PORT_BASE
|
||||
|
||||
from sanic import Sanic
|
||||
from sanic.response import text
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def test_port(worker_id):
|
||||
m = re.search(r"[0-9]+", worker_id)
|
||||
if m:
|
||||
num_id = m.group(0)
|
||||
else:
|
||||
num_id = 0
|
||||
port = PORT_BASE + int(num_id)
|
||||
return port
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def app():
|
||||
app = Sanic("Example")
|
||||
|
||||
@app.route("/")
|
||||
async def index(request):
|
||||
return text("OK")
|
||||
|
||||
return app
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def client(app, test_port):
|
||||
return SanicTestClient(app, test_port)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("run_id", range(100))
|
||||
def test_index(client, run_id):
|
||||
request, response = client._sanic_endpoint_test("get", "/")
|
||||
assert response.status == 200
|
||||
assert response.text == "OK"
|
33
examples/raygun_example.py
Normal file
33
examples/raygun_example.py
Normal file
@ -0,0 +1,33 @@
|
||||
from os import getenv
|
||||
|
||||
from raygun4py.raygunprovider import RaygunSender
|
||||
|
||||
from sanic import Sanic
|
||||
from sanic.exceptions import SanicException
|
||||
from sanic.handlers import ErrorHandler
|
||||
|
||||
|
||||
class RaygunExceptionReporter(ErrorHandler):
|
||||
def __init__(self, raygun_api_key=None):
|
||||
super().__init__()
|
||||
if raygun_api_key is None:
|
||||
raygun_api_key = getenv("RAYGUN_API_KEY")
|
||||
|
||||
self.sender = RaygunSender(raygun_api_key)
|
||||
|
||||
def default(self, request, exception):
|
||||
self.sender.send_exception(exception=exception)
|
||||
return super().default(request, exception)
|
||||
|
||||
|
||||
raygun_error_reporter = RaygunExceptionReporter()
|
||||
app = Sanic("Example", error_handler=raygun_error_reporter)
|
||||
|
||||
|
||||
@app.route("/raise")
|
||||
async def test(request):
|
||||
raise SanicException("You Broke It!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(host="0.0.0.0", port=getenv("PORT", 8080))
|
18
examples/redirect_example.py
Normal file
18
examples/redirect_example.py
Normal file
@ -0,0 +1,18 @@
|
||||
from sanic import Sanic, response
|
||||
|
||||
|
||||
app = Sanic("Example")
|
||||
|
||||
|
||||
@app.route("/")
|
||||
def handle_request(request):
|
||||
return response.redirect("/redirect")
|
||||
|
||||
|
||||
@app.route("/redirect")
|
||||
async def test(request):
|
||||
return response.json({"Redirected": True})
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(host="0.0.0.0", port=8000)
|
10
examples/request_stream/client.py
Normal file
10
examples/request_stream/client.py
Normal file
@ -0,0 +1,10 @@
|
||||
import requests
|
||||
|
||||
# Warning: This is a heavy process.
|
||||
|
||||
data = ""
|
||||
for i in range(1, 250000):
|
||||
data += str(i)
|
||||
|
||||
r = requests.post("http://0.0.0.0:8000/stream", data=data)
|
||||
print(r.text)
|
63
examples/request_stream/server.py
Normal file
63
examples/request_stream/server.py
Normal file
@ -0,0 +1,63 @@
|
||||
from sanic import Sanic
|
||||
from sanic.blueprints import Blueprint
|
||||
from sanic.response import stream, text
|
||||
from sanic.views import HTTPMethodView
|
||||
from sanic.views import stream as stream_decorator
|
||||
|
||||
|
||||
bp = Blueprint("bp_example")
|
||||
app = Sanic("Example")
|
||||
|
||||
|
||||
class SimpleView(HTTPMethodView):
|
||||
@stream_decorator
|
||||
async def post(self, request):
|
||||
result = ""
|
||||
while True:
|
||||
body = await request.stream.get()
|
||||
if body is None:
|
||||
break
|
||||
result += body.decode("utf-8")
|
||||
return text(result)
|
||||
|
||||
|
||||
@app.post("/stream", stream=True)
|
||||
async def handler(request):
|
||||
async def streaming(response):
|
||||
while True:
|
||||
body = await request.stream.get()
|
||||
if body is None:
|
||||
break
|
||||
body = body.decode("utf-8").replace("1", "A")
|
||||
await response.write(body)
|
||||
|
||||
return stream(streaming)
|
||||
|
||||
|
||||
@bp.put("/bp_stream", stream=True)
|
||||
async def bp_handler(request):
|
||||
result = ""
|
||||
while True:
|
||||
body = await request.stream.get()
|
||||
if body is None:
|
||||
break
|
||||
result += body.decode("utf-8").replace("1", "A")
|
||||
return text(result)
|
||||
|
||||
|
||||
async def post_handler(request):
|
||||
result = ""
|
||||
while True:
|
||||
body = await request.stream.get()
|
||||
if body is None:
|
||||
break
|
||||
result += body.decode("utf-8")
|
||||
return text(result)
|
||||
|
||||
|
||||
app.blueprint(bp)
|
||||
app.add_route(SimpleView.as_view(), "/method_view")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(host="0.0.0.0", port=8000)
|
@ -1,21 +1,24 @@
|
||||
from sanic import Sanic
|
||||
import asyncio
|
||||
from sanic.response import text
|
||||
|
||||
from sanic import Sanic, response
|
||||
from sanic.config import Config
|
||||
from sanic.exceptions import RequestTimeout
|
||||
|
||||
|
||||
Config.REQUEST_TIMEOUT = 1
|
||||
app = Sanic(__name__)
|
||||
app = Sanic("Example")
|
||||
|
||||
|
||||
@app.route('/')
|
||||
@app.route("/")
|
||||
async def test(request):
|
||||
await asyncio.sleep(3)
|
||||
return text('Hello, world!')
|
||||
return response.text("Hello, world!")
|
||||
|
||||
|
||||
@app.exception(RequestTimeout)
|
||||
def timeout(request, exception):
|
||||
return 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)
|
||||
|
28
examples/rollbar_example.py
Normal file
28
examples/rollbar_example.py
Normal file
@ -0,0 +1,28 @@
|
||||
from os import getenv
|
||||
|
||||
import rollbar
|
||||
|
||||
from sanic import Sanic
|
||||
from sanic.exceptions import SanicException
|
||||
from sanic.handlers import ErrorHandler
|
||||
|
||||
|
||||
rollbar.init(getenv("ROLLBAR_API_KEY"))
|
||||
|
||||
|
||||
class RollbarExceptionHandler(ErrorHandler):
|
||||
def default(self, request, exception):
|
||||
rollbar.report_message(str(exception))
|
||||
return super().default(request, exception)
|
||||
|
||||
|
||||
app = Sanic("Example", error_handler=RollbarExceptionHandler())
|
||||
|
||||
|
||||
@app.route("/raise")
|
||||
def create_error(request):
|
||||
raise SanicException("I was here and I don't like where I am")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(host="0.0.0.0", port=getenv("PORT", 8080))
|
89
examples/run_asgi.py
Normal file
89
examples/run_asgi.py
Normal file
@ -0,0 +1,89 @@
|
||||
"""
|
||||
1. Create a simple Sanic app
|
||||
0. Run with an ASGI server:
|
||||
$ uvicorn run_asgi:app
|
||||
or
|
||||
$ hypercorn run_asgi:app
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from sanic import Sanic, response
|
||||
|
||||
|
||||
app = Sanic("Example")
|
||||
|
||||
|
||||
@app.route("/text")
|
||||
def handler_text(request):
|
||||
return response.text("Hello")
|
||||
|
||||
|
||||
@app.route("/json")
|
||||
def handler_json(request):
|
||||
return response.json({"foo": "bar"})
|
||||
|
||||
|
||||
@app.websocket("/ws")
|
||||
async def handler_ws(request, ws):
|
||||
name = "<someone>"
|
||||
while True:
|
||||
data = f"Hello {name}"
|
||||
await ws.send(data)
|
||||
name = await ws.recv()
|
||||
|
||||
if not name:
|
||||
break
|
||||
|
||||
|
||||
@app.route("/file")
|
||||
async def handler_file(request):
|
||||
return await response.file(Path("../") / "setup.py")
|
||||
|
||||
|
||||
@app.route("/file_stream")
|
||||
async def handler_file_stream(request):
|
||||
return await response.file_stream(
|
||||
Path("../") / "setup.py", chunk_size=1024
|
||||
)
|
||||
|
||||
|
||||
@app.post("/stream", stream=True)
|
||||
async def handler_stream(request):
|
||||
while True:
|
||||
body = await request.stream.read()
|
||||
if body is None:
|
||||
break
|
||||
body = body.decode("utf-8").replace("1", "A")
|
||||
await response.write(body)
|
||||
return response.stream(body)
|
||||
|
||||
|
||||
@app.before_server_start
|
||||
async def listener_before_server_start(*args, **kwargs):
|
||||
print("before_server_start")
|
||||
|
||||
|
||||
@app.after_server_start
|
||||
async def listener_after_server_start(*args, **kwargs):
|
||||
print("after_server_start")
|
||||
|
||||
|
||||
@app.before_server_stop
|
||||
async def listener_before_server_stop(*args, **kwargs):
|
||||
print("before_server_stop")
|
||||
|
||||
|
||||
@app.after_server_stop
|
||||
async def listener_after_server_stop(*args, **kwargs):
|
||||
print("after_server_stop")
|
||||
|
||||
|
||||
@app.on_request
|
||||
async def print_on_request(request):
|
||||
print("print_on_request")
|
||||
|
||||
|
||||
@app.on_response
|
||||
async def print_on_response(request, response):
|
||||
print("print_on_response")
|
@ -1,20 +1,30 @@
|
||||
from sanic import Sanic
|
||||
from sanic.response import json
|
||||
from multiprocessing import Event
|
||||
from signal import signal, SIGINT
|
||||
import asyncio
|
||||
|
||||
app = Sanic(__name__)
|
||||
import uvloop
|
||||
|
||||
from sanic import Sanic, response
|
||||
|
||||
|
||||
app = Sanic("Example")
|
||||
|
||||
|
||||
@app.route("/")
|
||||
async def test(request):
|
||||
return json({"answer": "42"})
|
||||
return response.json({"answer": "42"})
|
||||
|
||||
server = app.create_server(host="0.0.0.0", port=8001)
|
||||
loop = asyncio.get_event_loop()
|
||||
task = asyncio.ensure_future(server)
|
||||
signal(SIGINT, lambda s, f: loop.close())
|
||||
try:
|
||||
loop.run_forever()
|
||||
except:
|
||||
loop.stop()
|
||||
|
||||
async def main():
|
||||
server = await app.create_server(
|
||||
port=8000, host="0.0.0.0", return_asyncio_server=True
|
||||
)
|
||||
|
||||
if server is None:
|
||||
return
|
||||
|
||||
await server.startup()
|
||||
await server.serve_forever()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.set_event_loop(uvloop.new_event_loop())
|
||||
asyncio.run(main())
|
||||
|
68
examples/run_async_advanced.py
Normal file
68
examples/run_async_advanced.py
Normal file
@ -0,0 +1,68 @@
|
||||
import asyncio
|
||||
|
||||
from signal import SIGINT, signal
|
||||
|
||||
import uvloop
|
||||
|
||||
from sanic import Sanic, response
|
||||
from sanic.server import AsyncioServer
|
||||
|
||||
|
||||
app = Sanic("Example")
|
||||
|
||||
|
||||
@app.before_server_start
|
||||
async def before_server_start(app, loop):
|
||||
print("Async Server starting")
|
||||
|
||||
|
||||
@app.after_server_start
|
||||
async def after_server_start(app, loop):
|
||||
print("Async Server started")
|
||||
|
||||
|
||||
@app.before_server_stop
|
||||
async def before_server_stop(app, loop):
|
||||
print("Async Server stopping")
|
||||
|
||||
|
||||
@app.after_server_stop
|
||||
async def after_server_stop(app, loop):
|
||||
print("Async Server stopped")
|
||||
|
||||
|
||||
@app.route("/")
|
||||
async def test(request):
|
||||
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())
|
||||
|
||||
# When using app.run(), this actually triggers before the serv_coro.
|
||||
# But, in this example, we are using the convenience method, even if it is
|
||||
# out of order.
|
||||
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
|
||||
close_task = server.close()
|
||||
loop.run_until_complete(close_task)
|
||||
|
||||
# Complete all tasks on the loop
|
||||
for connection in server.connections:
|
||||
connection.close_if_idle()
|
||||
loop.run_until_complete(server.after_stop())
|
@ -1,65 +0,0 @@
|
||||
""" To run this example you need additional aiopg package
|
||||
|
||||
"""
|
||||
import os
|
||||
import asyncio
|
||||
|
||||
import uvloop
|
||||
import aiopg
|
||||
|
||||
from sanic import Sanic
|
||||
from sanic.response import json
|
||||
|
||||
database_name = os.environ['DATABASE_NAME']
|
||||
database_host = os.environ['DATABASE_HOST']
|
||||
database_user = os.environ['DATABASE_USER']
|
||||
database_password = os.environ['DATABASE_PASSWORD']
|
||||
|
||||
connection = 'postgres://{0}:{1}@{2}/{3}'.format(database_user,
|
||||
database_password,
|
||||
database_host,
|
||||
database_name)
|
||||
|
||||
|
||||
async def get_pool():
|
||||
return await aiopg.create_pool(connection)
|
||||
|
||||
app = Sanic(name=__name__)
|
||||
|
||||
async def prepare_db(app, loop):
|
||||
"""
|
||||
Let's create some table and add some data
|
||||
"""
|
||||
async with aiopg.create_pool(connection) as pool:
|
||||
async with pool.acquire() as conn:
|
||||
async with conn.cursor() as cur:
|
||||
await cur.execute('DROP TABLE IF EXISTS sanic_polls')
|
||||
await cur.execute("""CREATE TABLE sanic_polls (
|
||||
id serial primary key,
|
||||
question varchar(50),
|
||||
pub_date timestamp
|
||||
);""")
|
||||
for i in range(0, 100):
|
||||
await cur.execute("""INSERT INTO sanic_polls
|
||||
(id, question, pub_date) VALUES ({}, {}, now())
|
||||
""".format(i, i))
|
||||
|
||||
|
||||
@app.route("/")
|
||||
async def handle(request):
|
||||
result = []
|
||||
async def test_select():
|
||||
async with aiopg.create_pool(connection) as pool:
|
||||
async with pool.acquire() as conn:
|
||||
async with conn.cursor() as cur:
|
||||
await cur.execute("SELECT question, pub_date FROM sanic_polls")
|
||||
async for row in cur:
|
||||
result.append({"question": row[0], "pub_date": row[1]})
|
||||
res = await test_select()
|
||||
return json({'polls': result})
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(host='0.0.0.0',
|
||||
port=8000,
|
||||
debug=True,
|
||||
before_start=prepare_db)
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user