Compare commits
1737 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
36e4a824f3 | ||
|
|
954f5684e4 | ||
|
|
9fcd34c9f3 | ||
|
|
1d49747264 | ||
|
|
7374c85dde | ||
|
|
dcbede0cb0 | ||
|
|
9368fb3f3c | ||
|
|
06ea505ce2 | ||
|
|
2ac89889f4 | ||
|
|
689362089e | ||
|
|
258d68b6ff | ||
|
|
a31e6b7346 | ||
|
|
e4e4bf6543 | ||
|
|
b21425d6df | ||
|
|
83dbcb222a | ||
|
|
7ef2946fb5 | ||
|
|
91c6b3fecf | ||
|
|
4cae5d8e41 | ||
|
|
a197702e2c | ||
|
|
4774385abd | ||
|
|
560c119e3d | ||
|
|
3a9d54f3da | ||
|
|
a29f88ce6f | ||
|
|
c34d6e81fd | ||
|
|
fbb6f0bc4c | ||
|
|
7b0088693e | ||
|
|
a411a2434b | ||
|
|
daca74d93a | ||
|
|
f00ad424ee | ||
|
|
22d924701a | ||
|
|
089cb86dd2 | ||
|
|
c17f379390 | ||
|
|
27be933840 | ||
|
|
d98eb69a35 | ||
|
|
12e95c93b4 | ||
|
|
e82db2ca3a | ||
|
|
d3b78fd18a | ||
|
|
663198d7b4 | ||
|
|
976459f7cb | ||
|
|
5e621a2801 | ||
|
|
4e652d3f68 | ||
|
|
3d03098a97 | ||
|
|
798e006f43 | ||
|
|
407eb76b27 | ||
|
|
ac5664d228 | ||
|
|
23df07d799 | ||
|
|
4b8bde97d4 | ||
|
|
0292047b22 | ||
|
|
c77a520343 | ||
|
|
9b74a90dd3 | ||
|
|
5880befcd2 | ||
|
|
4f79d5b59a | ||
|
|
fe3b215d3a | ||
|
|
5559ef42b5 | ||
|
|
3709c4a9a8 | ||
|
|
709f83f6a3 | ||
|
|
30da640ffe | ||
|
|
25642fd1fd | ||
|
|
809d5a8869 | ||
|
|
eca5fd1dfd | ||
|
|
eb58d862cc | ||
|
|
64dd0809c2 | ||
|
|
97bddc1f61 | ||
|
|
ad68a12645 | ||
|
|
2579ce9f18 | ||
|
|
607d1948b8 | ||
|
|
218880c7fd | ||
|
|
917000097f | ||
|
|
96a01e420b | ||
|
|
da6d075dfd | ||
|
|
70d04b5a26 | ||
|
|
88a65bb374 | ||
|
|
6a64969009 | ||
|
|
adf363679d | ||
|
|
c2705ffd9c | ||
|
|
dbd4c28825 | ||
|
|
330123258e | ||
|
|
85793d6c22 | ||
|
|
2c1b30d050 | ||
|
|
1292419ddf | ||
|
|
4dd52ca9c7 | ||
|
|
55c6255657 | ||
|
|
ed1c9e953e | ||
|
|
edebd37044 | ||
|
|
daf1510a4b | ||
|
|
d8259eb119 | ||
|
|
38b4c1e19b | ||
|
|
9822a03515 | ||
|
|
49b7e7bc8f | ||
|
|
b228ca3d87 | ||
|
|
ff64079a51 | ||
|
|
1dfd7cd555 | ||
|
|
d44f1c6523 | ||
|
|
c56c5ec7c4 | ||
|
|
0f83958247 | ||
|
|
7fea7cf156 | ||
|
|
24824ff666 | ||
|
|
53b8f08218 | ||
|
|
5addaf833b | ||
|
|
85c5d93cbd | ||
|
|
85cc710464 | ||
|
|
284273e3c5 | ||
|
|
f17d986948 | ||
|
|
d6009c0aeb | ||
|
|
2b42a803a2 | ||
|
|
211cce038a | ||
|
|
a7b67c99f9 | ||
|
|
a758915893 | ||
|
|
e974128863 | ||
|
|
f04c5e6964 | ||
|
|
c07b201ce3 | ||
|
|
adeea00707 | ||
|
|
7bf3be8dfa | ||
|
|
a42c4d54a3 | ||
|
|
184ec3c545 | ||
|
|
a5f9742398 | ||
|
|
52df9eed45 | ||
|
|
e7e5380776 | ||
|
|
bbaf13333f | ||
|
|
57e7286948 | ||
|
|
7fff56f517 | ||
|
|
73d6504063 | ||
|
|
cbb6c36692 | ||
|
|
fb54159861 | ||
|
|
bc143499cf | ||
|
|
941efd4a36 | ||
|
|
0109e496f6 | ||
|
|
11c45eeba3 | ||
|
|
b78b5a210b | ||
|
|
e785166507 | ||
|
|
410e5ab7ed | ||
|
|
bfffe87d4c | ||
|
|
73ce26c3e8 | ||
|
|
41ec5760a2 | ||
|
|
2732c4db66 | ||
|
|
c94d2a77db | ||
|
|
315ebc1176 | ||
|
|
7d5d187458 | ||
|
|
c7c8dc38ea | ||
|
|
2ae36c8dd5 | ||
|
|
5ea0ab8ea2 | ||
|
|
da60039486 | ||
|
|
08c480b3b3 | ||
|
|
f51a23839a | ||
|
|
04b070fa26 | ||
|
|
75a8327cfd | ||
|
|
165af0a090 | ||
|
|
235c52fa10 | ||
|
|
f61172b8dd | ||
|
|
959052fb8d | ||
|
|
5b525e9797 | ||
|
|
60a11a730e | ||
|
|
6b361ce06b | ||
|
|
6b054f8f38 | ||
|
|
f2674c5bb4 | ||
|
|
54c3f87af9 | ||
|
|
ea08f155d8 | ||
|
|
b394a994e6 | ||
|
|
dcbe86bd15 | ||
|
|
d5b7a05ab2 | ||
|
|
d22bfcd4cf | ||
|
|
4fec712f32 | ||
|
|
18ffe1eaf6 | ||
|
|
bc098406af | ||
|
|
ab81496641 | ||
|
|
70602a196a | ||
|
|
6748a09341 | ||
|
|
22c48a738b | ||
|
|
2eab96a32a | ||
|
|
f49dbfd3e4 | ||
|
|
7b21d43d4c | ||
|
|
4f7156f2c3 | ||
|
|
10bdf61a0f | ||
|
|
4995a775df | ||
|
|
07c7d5730a | ||
|
|
470e2b8d17 | ||
|
|
a20bcff8dc | ||
|
|
e13373f838 | ||
|
|
7522c4bcdb | ||
|
|
2c31603042 | ||
|
|
6c44dd4bb8 | ||
|
|
98ae718976 | ||
|
|
c62b03bcfd | ||
|
|
a9b99b3489 | ||
|
|
8aa161a437 | ||
|
|
df201ed152 | ||
|
|
ce08bc704e | ||
|
|
9efc1ebeeb | ||
|
|
6f2014d353 | ||
|
|
c7a53888a1 | ||
|
|
62c56e08c4 | ||
|
|
8f37c82f61 | ||
|
|
39e7208366 | ||
|
|
227838c472 | ||
|
|
99ce7ed0e4 | ||
|
|
1d610e44b3 | ||
|
|
e8b91cd38a | ||
|
|
9e831e915f | ||
|
|
5e8cb74018 | ||
|
|
2778b7c23f | ||
|
|
96800fb673 | ||
|
|
8f2bc008ad | ||
|
|
9b5549313e | ||
|
|
68150d4caf | ||
|
|
74721b48f0 | ||
|
|
d273f87a41 | ||
|
|
52ccd66735 | ||
|
|
dffe303482 | ||
|
|
52c060f718 | ||
|
|
c5a5576522 | ||
|
|
bca18041b0 | ||
|
|
b337d21058 | ||
|
|
e63ead4208 | ||
|
|
2fec0b206c | ||
|
|
111e5bd312 | ||
|
|
db49548d71 | ||
|
|
f93dd6e826 | ||
|
|
7e53070307 | ||
|
|
c77b099cbb | ||
|
|
eeb5f95a0f | ||
|
|
40b78fa2ea | ||
|
|
4e6384da32 | ||
|
|
2c5d652493 | ||
|
|
176fdfa000 | ||
|
|
2d31dce826 | ||
|
|
29a94bd102 | ||
|
|
0f2ae2b933 | ||
|
|
8a6cdf1e2a | ||
|
|
326ad3f43c | ||
|
|
a791997041 | ||
|
|
4fe0aebab7 | ||
|
|
a8956feba1 | ||
|
|
28d5a4d718 | ||
|
|
5353f306fe | ||
|
|
321bd74b95 | ||
|
|
66af0e55ef | ||
|
|
3d357273b5 | ||
|
|
926ab92118 | ||
|
|
088b58dbce | ||
|
|
7621b3d96a | ||
|
|
e165f3aef4 | ||
|
|
d718ecf6d3 | ||
|
|
0ce27278d2 | ||
|
|
07c8f19bfd | ||
|
|
0d2100ed17 | ||
|
|
e3535f9971 | ||
|
|
422e05e28d | ||
|
|
f567ab9068 | ||
|
|
6d126e1013 | ||
|
|
c7da8c2aa3 | ||
|
|
bb16048ad7 | ||
|
|
767ad19b10 | ||
|
|
a2f495b9ff | ||
|
|
4a1766c252 | ||
|
|
255c8d66af | ||
|
|
bea5876e46 | ||
|
|
9101439d7b | ||
|
|
67ed36910d | ||
|
|
4e894892bc | ||
|
|
d64ecfb244 | ||
|
|
860a25c390 | ||
|
|
273123f6b8 | ||
|
|
fc605b575b | ||
|
|
a936b0c610 | ||
|
|
4f42c64203 | ||
|
|
eb1182a10e | ||
|
|
a363642a32 | ||
|
|
57add386c9 | ||
|
|
823e279e0d | ||
|
|
11c15ddfeb | ||
|
|
224c639bf9 | ||
|
|
f48802acbb | ||
|
|
b7278186c4 | ||
|
|
ccf125bf30 | ||
|
|
db0fe9436e | ||
|
|
2c7f57ad5b | ||
|
|
f958b6500b | ||
|
|
346d1abaff | ||
|
|
19610a9e46 | ||
|
|
aee16df63b | ||
|
|
61182249cb | ||
|
|
c12a5d874c | ||
|
|
5e22cc9eec | ||
|
|
5fdce4c331 | ||
|
|
adb7dd99c2 | ||
|
|
b739390955 | ||
|
|
db461112c7 | ||
|
|
7320e311a0 | ||
|
|
a855756017 | ||
|
|
be508c6184 | ||
|
|
6ab71ed7cf | ||
|
|
87d5f5b9a9 | ||
|
|
98a7f9fcf0 | ||
|
|
0e59442f6c | ||
|
|
b90a4f1f4a | ||
|
|
ad36383951 | ||
|
|
6b422a05f3 | ||
|
|
d5e321b792 | ||
|
|
d2030595dc | ||
|
|
d7209a9570 | ||
|
|
1af8f95785 | ||
|
|
3435d2ff15 | ||
|
|
ecc057dd48 | ||
|
|
3207af8827 | ||
|
|
94e80b3da9 | ||
|
|
f32dddc71c | ||
|
|
484a7cc9a7 | ||
|
|
a4bada52d0 | ||
|
|
4df377cfbf | ||
|
|
233be7a0fa | ||
|
|
f622b1cade | ||
|
|
5fcc999b7d | ||
|
|
da3a0ddfe2 | ||
|
|
5e059be1b3 | ||
|
|
bae6ee888f | ||
|
|
81b3c85f51 | ||
|
|
08d3185e87 | ||
|
|
6000e80acf | ||
|
|
7b5e176d1a | ||
|
|
5a48a0fe6b | ||
|
|
700fc7d928 | ||
|
|
8fdab74cc8 | ||
|
|
24ec38d6a0 | ||
|
|
c2f65dd1cf | ||
|
|
63ff4185f9 | ||
|
|
708d62d717 | ||
|
|
c275573147 | ||
|
|
12a1c4940d | ||
|
|
77f6c72cf9 | ||
|
|
8a66990c61 | ||
|
|
05eebe36ab | ||
|
|
1d5abfadd7 | ||
|
|
399aa8531c | ||
|
|
b55ccae72a | ||
|
|
089f6a1c50 | ||
|
|
b64f848d2c | ||
|
|
452b78f243 | ||
|
|
78ced0093a | ||
|
|
d61198941a | ||
|
|
b97165db75 | ||
|
|
4104f29956 | ||
|
|
66743d4f9d | ||
|
|
258311d098 | ||
|
|
5308db0637 | ||
|
|
59fd6aa104 | ||
|
|
560383fe14 | ||
|
|
6277036567 | ||
|
|
ce27ddeba1 | ||
|
|
54e05a2824 | ||
|
|
9a12f34bbe | ||
|
|
54ff9b2972 | ||
|
|
6edfd78c9f | ||
|
|
4431ada1a2 | ||
|
|
24653528cc | ||
|
|
33d8886226 | ||
|
|
04920b3076 | ||
|
|
6ee5dcc0ec | ||
|
|
c4bfd367e2 | ||
|
|
29f1bd22d7 | ||
|
|
8d9519df09 | ||
|
|
7af0271f47 | ||
|
|
be6ec06894 | ||
|
|
beedaa4eff | ||
|
|
bb9937593d | ||
|
|
541bc8dfc2 | ||
|
|
3652ecd9e0 | ||
|
|
14232513fd | ||
|
|
3252f2bc54 | ||
|
|
438edcdf01 | ||
|
|
b7c1290528 | ||
|
|
8037487165 | ||
|
|
e8076d9114 | ||
|
|
ecc4a38b7c | ||
|
|
24c6508d39 | ||
|
|
98cef9fcca | ||
|
|
0c97a411b4 | ||
|
|
faef9a0fce | ||
|
|
293041a290 | ||
|
|
153433f612 | ||
|
|
65271c105f | ||
|
|
a6007373b5 | ||
|
|
3205b53c7c | ||
|
|
9f9550247a | ||
|
|
90967ccb9a | ||
|
|
5bb30270d2 | ||
|
|
1d8b53f782 | ||
|
|
bc5dd3894b | ||
|
|
318592511c | ||
|
|
80cf589a26 | ||
|
|
cc80a47f5b | ||
|
|
3f6b243cec | ||
|
|
72c85e80c8 | ||
|
|
0ec7f713d6 | ||
|
|
a64588f87a | ||
|
|
0e0e8ddcdc | ||
|
|
04994df59f | ||
|
|
6a12b191f7 | ||
|
|
9e9acfb3fe | ||
|
|
0273664009 | ||
|
|
a887e179b8 | ||
|
|
aa6d4c3e92 | ||
|
|
6d49a1e4b7 | ||
|
|
826514b8eb | ||
|
|
c49ce2e1eb | ||
|
|
3237fff4b8 | ||
|
|
6d266f6363 | ||
|
|
17e146ad94 | ||
|
|
80ae10f402 | ||
|
|
7d399e80c3 | ||
|
|
b1385919be | ||
|
|
6a5277d341 | ||
|
|
dcc86ebfce | ||
|
|
180ff8853c | ||
|
|
8a72b74161 | ||
|
|
7255be9626 | ||
|
|
f215de030e | ||
|
|
65bc763634 | ||
|
|
a0db890853 | ||
|
|
11da927fd9 | ||
|
|
cb825687a5 | ||
|
|
51bf0fdd90 | ||
|
|
0da5788efb | ||
|
|
326484a8d1 | ||
|
|
1ce4d95de9 | ||
|
|
1fb188636e | ||
|
|
f62580b947 | ||
|
|
47a89da558 | ||
|
|
09789f2a34 | ||
|
|
aea13d5a10 | ||
|
|
15a0d4afd4 | ||
|
|
f39c71609e | ||
|
|
3c85ec953f | ||
|
|
6b9acd565c | ||
|
|
46b328854a | ||
|
|
f38f3a745a | ||
|
|
b1bdd97564 | ||
|
|
d67c47b81f | ||
|
|
7bfbcfab87 | ||
|
|
8933d75443 | ||
|
|
cc55d7b3cf | ||
|
|
aca7d5637d | ||
|
|
ec2b2394dc | ||
|
|
a7ae372f2f | ||
|
|
4bcd4be6b7 | ||
|
|
8e33b7b3e2 | ||
|
|
4be9f52142 | ||
|
|
bda295d37f | ||
|
|
4d6ae8273a | ||
|
|
c8cf4694c6 | ||
|
|
935ee742b4 | ||
|
|
9930b0fe8e | ||
|
|
ed8ddb6a3a | ||
|
|
84e11a1e82 | ||
|
|
367e1df785 | ||
|
|
2be71d323d | ||
|
|
d614d6bf8c | ||
|
|
32d2f47ed1 | ||
|
|
c9b6110dec | ||
|
|
7dbb2f7e05 | ||
|
|
a3e4013f89 | ||
|
|
fb20cbbf1e | ||
|
|
c49757a459 | ||
|
|
18e703bc93 | ||
|
|
fc74a114b3 | ||
|
|
40f31c3078 | ||
|
|
31fd9c8791 | ||
|
|
38f02e04f4 | ||
|
|
e7264776bd | ||
|
|
ae07dead24 | ||
|
|
0f477df86a | ||
|
|
55332be325 | ||
|
|
b80baaf359 | ||
|
|
2abb7513dc | ||
|
|
d0bf462866 | ||
|
|
4911012cf4 | ||
|
|
57e926c791 | ||
|
|
859d63902e | ||
|
|
0a00e1b608 | ||
|
|
1d7281fe07 | ||
|
|
e374853c75 | ||
|
|
97c830190f | ||
|
|
bda08b11c6 | ||
|
|
47af817c8f | ||
|
|
afd63b1617 | ||
|
|
ca12e8ebb7 | ||
|
|
8646edca6f | ||
|
|
a6367dac74 | ||
|
|
3fbfbad79f | ||
|
|
726d3f4fa9 | ||
|
|
7b31099252 | ||
|
|
723091b903 | ||
|
|
dcd25d8f07 | ||
|
|
1e160c199b | ||
|
|
ec8ca69195 | ||
|
|
b7b753b96c | ||
|
|
0afeb1d11c | ||
|
|
4cf8b78c6c | ||
|
|
f3b8f57054 | ||
|
|
c990bba94a | ||
|
|
adedb2a64e | ||
|
|
e1aedecdc6 | ||
|
|
37badc3ce8 | ||
|
|
70f906c51c | ||
|
|
8705dd39c4 | ||
|
|
9532cba45d | ||
|
|
0bc7356ce1 | ||
|
|
f07fb2b607 | ||
|
|
721abdc381 | ||
|
|
0867dce42c | ||
|
|
0ec9192cf2 | ||
|
|
345f18442c | ||
|
|
5f4072423e | ||
|
|
8728c3e4cd | ||
|
|
eb33b8c809 | ||
|
|
79ebf6c39c | ||
|
|
73739a29f4 | ||
|
|
ddc7accaa2 | ||
|
|
cd1b68cf6e | ||
|
|
74e0329820 | ||
|
|
2d460685b1 | ||
|
|
64bc45874d | ||
|
|
427e5dc4f3 | ||
|
|
0c5f18ab91 | ||
|
|
d48deba273 | ||
|
|
2b2a764143 | ||
|
|
83d777bebf | ||
|
|
b4345cb4eb | ||
|
|
2e8fe7b2f2 | ||
|
|
9659b11a45 | ||
|
|
f139024b1c | ||
|
|
a4ea5df5d5 | ||
|
|
44ffe6c6d6 | ||
|
|
cfa863c357 | ||
|
|
db7d2d2d68 | ||
|
|
a67195e059 | ||
|
|
7e6e68aed9 | ||
|
|
a1d0eda789 | ||
|
|
5bba529dfc | ||
|
|
9a4d370ea2 | ||
|
|
6931b25293 | ||
|
|
84c007d34f | ||
|
|
182ce3dd15 | ||
|
|
49498a323b | ||
|
|
f7d9956c0f | ||
|
|
59f81950fe | ||
|
|
8239765a44 | ||
|
|
a05c0c6b72 | ||
|
|
9cb1a7a52d | ||
|
|
e1e4e82096 | ||
|
|
b626a93879 | ||
|
|
87698a2d5d | ||
|
|
c67bfe551d | ||
|
|
c9a5660cfd | ||
|
|
c24f8c8199 | ||
|
|
fa0ceb62f2 | ||
|
|
694183ce22 | ||
|
|
04c21387db | ||
|
|
67c4c7bac2 | ||
|
|
e39d78a0ad | ||
|
|
a30b6e723c | ||
|
|
daf6966c89 | ||
|
|
c4f754cda3 | ||
|
|
d2a6f5c572 | ||
|
|
a361ef6368 | ||
|
|
c2688e4f06 | ||
|
|
e8178f7bfa | ||
|
|
4909e8ff86 | ||
|
|
6a6c83789f | ||
|
|
29676a273a | ||
|
|
15f2ca2c24 | ||
|
|
7ed89d3f9d | ||
|
|
b8b410014d | ||
|
|
7ee9ceb71e | ||
|
|
cac56a2b53 | ||
|
|
c4b39ba2f3 | ||
|
|
e7f8ae0166 | ||
|
|
1432cddb13 | ||
|
|
c83f661427 | ||
|
|
31022056ae | ||
|
|
a5f30b157d | ||
|
|
0bc6be604d | ||
|
|
2f67e0fe4a | ||
|
|
bc3ec352f0 | ||
|
|
b8eb83940c | ||
|
|
3cdcc729a7 | ||
|
|
5cdfeae2e8 | ||
|
|
736b5f9ff8 | ||
|
|
2ad4d8d717 | ||
|
|
b10b6d4af1 | ||
|
|
4c288bc97e | ||
|
|
4256fc6304 | ||
|
|
c690f529f2 | ||
|
|
fc03d0dfab | ||
|
|
2a33c17854 | ||
|
|
704b68948f | ||
|
|
9c02f07f9b | ||
|
|
1ee22e1736 | ||
|
|
6650764e97 | ||
|
|
604de4b1dc | ||
|
|
c4c7f504be | ||
|
|
ed5b240417 | ||
|
|
6a392afbb6 | ||
|
|
41d4f62909 | ||
|
|
24df8fc89d | ||
|
|
aa040c085c | ||
|
|
a18ae3d752 | ||
|
|
b945f988f9 | ||
|
|
a6cd8f212e | ||
|
|
f425c117e2 | ||
|
|
ba2b3094d1 | ||
|
|
129568f7f4 | ||
|
|
ab93222bd6 | ||
|
|
d5527264f0 | ||
|
|
3f195248dc | ||
|
|
dca8cf013b | ||
|
|
761e02ef67 | ||
|
|
428d9430bc | ||
|
|
4ddb3f73ba | ||
|
|
dd2423ebde | ||
|
|
a15da89dbb | ||
|
|
c1d01f6999 | ||
|
|
3502cc951b | ||
|
|
f45bc5995c | ||
|
|
9abe28130d | ||
|
|
88069bd417 | ||
|
|
f822891065 | ||
|
|
74c256872b | ||
|
|
94a23a3e24 | ||
|
|
d93760d8bd | ||
|
|
ef85270d9a | ||
|
|
39fc0c6bad | ||
|
|
0b4b61146f | ||
|
|
99b34f7148 | ||
|
|
3a35977d5f | ||
|
|
78b42b9ac2 | ||
|
|
261e4a6cf2 | ||
|
|
8f13f5b6d6 | ||
|
|
6d6d986fc5 | ||
|
|
9da947a279 | ||
|
|
3812a52e96 | ||
|
|
8bfc0581e7 | ||
|
|
a748770199 | ||
|
|
4a0f658897 | ||
|
|
2a9d16d011 | ||
|
|
836866dc19 | ||
|
|
cabda59353 | ||
|
|
d7b6c1f670 | ||
|
|
fa1ee70668 | ||
|
|
bb1f83c265 | ||
|
|
74e5263c88 | ||
|
|
eeebb5adc7 | ||
|
|
4d69165ab6 | ||
|
|
910179f9b0 | ||
|
|
229dcbb5ce | ||
|
|
bd26928fdb | ||
|
|
4bc0e4943d | ||
|
|
79032ca5f1 | ||
|
|
7464e17c5e | ||
|
|
631b6dd546 | ||
|
|
09112cfc47 | ||
|
|
d9e56c7f4b | ||
|
|
677a046848 | ||
|
|
d8fe178aa6 | ||
|
|
6fcf6d00bd | ||
|
|
55cdb420a0 | ||
|
|
1a68768e6b | ||
|
|
1373f70dc6 | ||
|
|
8e3128b989 | ||
|
|
43bc7330ce | ||
|
|
d36a379a0f | ||
|
|
43ef559de3 | ||
|
|
dcdcac2f3a | ||
|
|
95b666871c | ||
|
|
3d4acf0ca0 | ||
|
|
3e932aa103 | ||
|
|
b5ec83b697 | ||
|
|
29697fffb4 | ||
|
|
9a294a6401 | ||
|
|
d951a763fb | ||
|
|
066a35dd32 | ||
|
|
618fdc8600 | ||
|
|
d178653b5f | ||
|
|
9f99425aaf | ||
|
|
35c6595202 | ||
|
|
9daddd1271 | ||
|
|
2a54cfa5ce | ||
|
|
cc66213e57 | ||
|
|
5dfd2126a6 | ||
|
|
e7c82f9c47 | ||
|
|
3dc6db9d0c | ||
|
|
75e92090ee | ||
|
|
c7da6d74a8 | ||
|
|
0d8c8ba71b | ||
|
|
9c3deeee96 | ||
|
|
2c78771238 | ||
|
|
c34c84b690 | ||
|
|
7c01fbcd06 | ||
|
|
8811ae037a | ||
|
|
00be8d24ac | ||
|
|
e7b2ce9a50 | ||
|
|
73b9bacbf7 | ||
|
|
ce6ad90ecf | ||
|
|
00bdc2b448 | ||
|
|
212b72a1fe | ||
|
|
56a95f6ff6 | ||
|
|
746455d103 | ||
|
|
e0c157f7ee | ||
|
|
86bf3f205f | ||
|
|
2d3f72574d | ||
|
|
d94634b1be | ||
|
|
1d07857b1d | ||
|
|
b0b27785ae | ||
|
|
77cdefccee | ||
|
|
062a096f40 | ||
|
|
5b2e9e946b | ||
|
|
22b6296830 | ||
|
|
00b07c863e | ||
|
|
36af821edf | ||
|
|
a1c478bc93 | ||
|
|
43d2fff317 | ||
|
|
e9af7c23ae | ||
|
|
de16718b39 | ||
|
|
c779ac4f5f | ||
|
|
6ab3cb8d0c | ||
|
|
87584e89fe | ||
|
|
e3eaafb56e | ||
|
|
e3df23374c | ||
|
|
a0458efef6 | ||
|
|
795d3e25aa | ||
|
|
45b2c99c1f | ||
|
|
a6a7a57380 | ||
|
|
4984753dbf | ||
|
|
21aaf0ef9a | ||
|
|
85f79e1a23 | ||
|
|
52c54b2ce1 | ||
|
|
0d87b22314 | ||
|
|
723a3a6ffd | ||
|
|
714ccefeca | ||
|
|
9a1b25fce4 | ||
|
|
cb4ee20117 | ||
|
|
01dec18891 | ||
|
|
b17bb9ed56 | ||
|
|
292c7e5c5d | ||
|
|
f3bb9b2566 | ||
|
|
ac1bb97679 | ||
|
|
cfd5783a98 | ||
|
|
bfdd37110c | ||
|
|
6f9ef11286 | ||
|
|
98ca00d545 | ||
|
|
30427a2090 | ||
|
|
ef95998d79 | ||
|
|
4c08e3a2ba | ||
|
|
3e5ca2902e | ||
|
|
7d53a129cf | ||
|
|
633f421031 | ||
|
|
5d8e35653f | ||
|
|
095651be9e | ||
|
|
02a0851252 | ||
|
|
363be75e84 | ||
|
|
2869ddf50c | ||
|
|
add53e190c | ||
|
|
900e11850a | ||
|
|
4bf7415a96 | ||
|
|
76a6b4f2b5 | ||
|
|
4f01c68a4b | ||
|
|
48fe96588c | ||
|
|
187d7179f6 | ||
|
|
134aebe601 | ||
|
|
a7859c6947 | ||
|
|
58f3536a8f | ||
|
|
7096b2c4b1 | ||
|
|
67310ab496 | ||
|
|
69f9845ef2 | ||
|
|
c356c6da5f | ||
|
|
03b410066b | ||
|
|
4696156278 | ||
|
|
58ecacd271 | ||
|
|
5775d1e9b1 | ||
|
|
91044c4d76 | ||
|
|
979e0adbac | ||
|
|
cbebdae698 | ||
|
|
e0dad45481 | ||
|
|
82c2e0366c | ||
|
|
d597db67de | ||
|
|
0b2f809f9b | ||
|
|
89463cb77c | ||
|
|
94233fc8c0 | ||
|
|
6751f68560 | ||
|
|
c757882808 | ||
|
|
2f1d1d6256 | ||
|
|
9b44bf2818 | ||
|
|
cafe68e1ed | ||
|
|
9dfcb90c92 | ||
|
|
d7482cd765 | ||
|
|
e4c4fd5771 | ||
|
|
e9d0000fc1 | ||
|
|
50df54e4c7 | ||
|
|
559a8458c4 | ||
|
|
60b845ebab | ||
|
|
f8cb0b0dd5 | ||
|
|
ca2bfbb0ac | ||
|
|
1626aff602 | ||
|
|
ab6a8b0330 | ||
|
|
c99f07d3e4 | ||
|
|
c2810ffdd2 | ||
|
|
9e686d93b6 | ||
|
|
96d39c87a8 | ||
|
|
6916697a98 | ||
|
|
84c722044a | ||
|
|
57a95e82b7 | ||
|
|
2589328485 | ||
|
|
3635583ce2 | ||
|
|
5544d09477 | ||
|
|
12d3f4fdf0 | ||
|
|
b46bfcfa63 | ||
|
|
0a01248b5c | ||
|
|
64ab59817d | ||
|
|
f6be300c42 | ||
|
|
abcb6c9677 | ||
|
|
ab36542260 | ||
|
|
46433e9807 | ||
|
|
762382e436 | ||
|
|
7f2a0f4806 | ||
|
|
ed42e92928 | ||
|
|
cec5f74110 | ||
|
|
c9e000b9ce | ||
|
|
ae547270e9 | ||
|
|
ab1fbef29a | ||
|
|
99fa3c36ab | ||
|
|
fe4003b3c9 | ||
|
|
aa801c431a | ||
|
|
4f03a769d4 | ||
|
|
9c50b8fc1c | ||
|
|
97298e06fe | ||
|
|
a9878c018b | ||
|
|
9139af1c62 | ||
|
|
b0a144d0c0 | ||
|
|
2ea77c2782 | ||
|
|
dcd1a1e0b6 | ||
|
|
9398630a8f | ||
|
|
2f3c87dcb8 | ||
|
|
ebc0d30dd1 | ||
|
|
9934528c8f | ||
|
|
6f6e3289da | ||
|
|
db3187fd7a | ||
|
|
45174bf9a1 | ||
|
|
bab5a65e6e | ||
|
|
6e23239567 | ||
|
|
aab1d9935e | ||
|
|
96c97dec09 | ||
|
|
7a2d5fb6df | ||
|
|
c7f2ab8e7a | ||
|
|
b06df0a792 | ||
|
|
3ba37d2afe | ||
|
|
88bcf78439 | ||
|
|
fb89745408 | ||
|
|
48766754b8 | ||
|
|
5e40aa22af | ||
|
|
3351a8677e | ||
|
|
81be290ec8 | ||
|
|
72cae9ce2b | ||
|
|
696d7c620d | ||
|
|
5d31ce1031 | ||
|
|
8c6f1d96de | ||
|
|
9e2e1de2fc | ||
|
|
21d32ee067 | ||
|
|
c6e3fc8072 | ||
|
|
3ecebcdf8d | ||
|
|
83e9680649 | ||
|
|
61f62e6005 | ||
|
|
8cb950671f | ||
|
|
a4f63e0390 | ||
|
|
60ac0c5092 | ||
|
|
c45c81938a | ||
|
|
ab76703532 | ||
|
|
9252be9c9e | ||
|
|
1888df3440 | ||
|
|
d506af1b1f | ||
|
|
31aad113ff | ||
|
|
1232d69860 | ||
|
|
a52a7db6c9 | ||
|
|
e044b00047 | ||
|
|
a406c297aa | ||
|
|
eb36135cfe | ||
|
|
ca8e6217fe | ||
|
|
69e2300608 | ||
|
|
00e2aac937 | ||
|
|
39f9363296 | ||
|
|
92acd05d9b | ||
|
|
fac630379d | ||
|
|
5512a66881 | ||
|
|
eb5dd9f5ef | ||
|
|
a25d234cdd | ||
|
|
542cf30e48 | ||
|
|
fdab801fbb | ||
|
|
e322f32e94 | ||
|
|
2381044d04 | ||
|
|
b5c1a4f1f0 | ||
|
|
410a324ec4 | ||
|
|
587a49c1bf | ||
|
|
11195f1083 | ||
|
|
bd56d19b16 | ||
|
|
a74e266474 | ||
|
|
127df57a06 | ||
|
|
1e5dd43022 | ||
|
|
2482cd4f53 | ||
|
|
9158d3b0b8 | ||
|
|
a0aa8de6bc | ||
|
|
8ddbad9ccd | ||
|
|
f976d5bb88 | ||
|
|
50374e3cfe | ||
|
|
7b28a9057d | ||
|
|
a0bb10b8d2 | ||
|
|
ef6c2b9e4a | ||
|
|
918fc07dd9 | ||
|
|
ef7d01f0a0 | ||
|
|
ff6290d4c7 | ||
|
|
3fd24c1f6f | ||
|
|
2b0b77cc1a | ||
|
|
c395b13f9e | ||
|
|
cb4f742543 | ||
|
|
2ec1193095 | ||
|
|
5050a18a00 | ||
|
|
dba2be9311 | ||
|
|
eede1a3685 | ||
|
|
69f71b4d94 | ||
|
|
2b8b47817a | ||
|
|
4dd14ed039 | ||
|
|
411dbb37e2 | ||
|
|
a03719b010 | ||
|
|
5334e28f6e | ||
|
|
19d7a0ef78 | ||
|
|
5c0b9a6af3 | ||
|
|
e3c014f9aa | ||
|
|
b9729a0879 | ||
|
|
56414782ca | ||
|
|
50bf3bba73 | ||
|
|
f59574f611 | ||
|
|
0d4e71bfe1 | ||
|
|
26555a85f1 | ||
|
|
f70abe634a | ||
|
|
9f4f559f59 | ||
|
|
45fc77d926 | ||
|
|
188bd17ff1 | ||
|
|
c6f297719e | ||
|
|
a8f4ee1b66 | ||
|
|
c419931f55 | ||
|
|
c3d0880e72 | ||
|
|
d023d94371 | ||
|
|
5589915b38 | ||
|
|
b109b092a9 | ||
|
|
65b0eef303 | ||
|
|
7d264d7dc5 | ||
|
|
f5ed54b031 | ||
|
|
cd4ab005a9 | ||
|
|
41aaaf7fa0 | ||
|
|
2f5a2ab82e | ||
|
|
e75d575361 | ||
|
|
4eb5e9455b | ||
|
|
08a283af5e | ||
|
|
425a62686f | ||
|
|
81c7ff7f8d | ||
|
|
ce7b884b73 | ||
|
|
6578b493c8 | ||
|
|
0a300d007d | ||
|
|
c9a1f7ad65 | ||
|
|
598afa0083 | ||
|
|
d80f41f57d | ||
|
|
7abcf57f54 | ||
|
|
faaa5594b2 | ||
|
|
cb7cd1e79b | ||
|
|
190dd4df86 | ||
|
|
8886328822 | ||
|
|
0ef1e65f6a | ||
|
|
b707bf443a | ||
|
|
b655a9db30 | ||
|
|
af34b8c9e7 | ||
|
|
8122973f8a | ||
|
|
7dc26a1f41 | ||
|
|
4843590c4a | ||
|
|
aa13521d42 | ||
|
|
3897a51801 | ||
|
|
642ca6ea68 | ||
|
|
ad13dbc826 | ||
|
|
6f79cb8a23 | ||
|
|
b6a8ccd2cf | ||
|
|
ef557b3ff2 | ||
|
|
702d973b1f | ||
|
|
c15f74cea6 | ||
|
|
761d7e1652 | ||
|
|
48ee204dd5 | ||
|
|
beae1249f7 | ||
|
|
e06dad62f6 | ||
|
|
1be65b1b69 | ||
|
|
7c5f17a55e | ||
|
|
e37e87140e | ||
|
|
d6496c8e29 | ||
|
|
1fd8654719 | ||
|
|
925674761e | ||
|
|
426a1e25b7 | ||
|
|
6f7d99ce4b | ||
|
|
bc094dbe6a | ||
|
|
694eb84f41 | ||
|
|
17c46b2ddd | ||
|
|
528db86f85 | ||
|
|
436c7afc83 | ||
|
|
9a134da31b | ||
|
|
751d85f3de | ||
|
|
fdac8a5404 | ||
|
|
aab064fee1 | ||
|
|
de4b2807d3 | ||
|
|
08ad8aabfe | ||
|
|
981a94df68 | ||
|
|
afe77feef3 | ||
|
|
d03df1fff3 | ||
|
|
66f2ac6696 | ||
|
|
e103747c54 | ||
|
|
a025ee3f25 | ||
|
|
660994efc7 | ||
|
|
9e50ad55ae | ||
|
|
0fb5c2f034 | ||
|
|
1a1c7ea618 | ||
|
|
93a7f08bbc | ||
|
|
990a95fbca | ||
|
|
0122a4f34f | ||
|
|
6389c07530 | ||
|
|
b1a00ebc4c | ||
|
|
633449a36c | ||
|
|
7b0c82dfdc | ||
|
|
a841cfabb5 | ||
|
|
9504de9dd0 | ||
|
|
9e00becf3a | ||
|
|
79b3cecc3d | ||
|
|
a65683a65c | ||
|
|
2952e6a323 | ||
|
|
c627b5e773 | ||
|
|
edac7e3a56 | ||
|
|
c5ca175004 | ||
|
|
9d36623db1 | ||
|
|
624ed4de72 | ||
|
|
f16524ea1d | ||
|
|
776bf09fdf | ||
|
|
2e10fc24a1 | ||
|
|
9fe21310bb | ||
|
|
9486b6cf57 | ||
|
|
c8ddb948f6 | ||
|
|
04c6a85518 | ||
|
|
47e4bd5059 | ||
|
|
6b0c8cdac1 | ||
|
|
d086a512d7 | ||
|
|
282d8621dd | ||
|
|
c8bf420483 | ||
|
|
e679a85b80 | ||
|
|
b831e8507c | ||
|
|
46b39e2698 | ||
|
|
ea66c68553 | ||
|
|
372066983f | ||
|
|
564bb27efa | ||
|
|
a2258dd05d | ||
|
|
4e8787b915 | ||
|
|
7860c1de87 | ||
|
|
7620cb70db | ||
|
|
15a3e82823 | ||
|
|
48f2afbf90 | ||
|
|
f8cdc78ce1 | ||
|
|
b7501776a1 | ||
|
|
218534a9f2 | ||
|
|
2bd7aed1a4 | ||
|
|
e21e003f62 | ||
|
|
ea93a52d7d | ||
|
|
df806c8035 | ||
|
|
1b552d0b01 | ||
|
|
e609dddd60 | ||
|
|
86009452fb | ||
|
|
a2e79eefc9 | ||
|
|
3c36d043e5 | ||
|
|
7a5aa570c0 | ||
|
|
1f40c77f1d | ||
|
|
0ae0f5957f | ||
|
|
303a1e6c41 | ||
|
|
860431f7e0 | ||
|
|
225ff3ea6c | ||
|
|
ded812b0cd | ||
|
|
21def4e05c | ||
|
|
c5ed6c5e77 | ||
|
|
58a08a1d73 | ||
|
|
44bc286c03 | ||
|
|
8ddf80c3ea | ||
|
|
b2b60450f7 | ||
|
|
fb82e3cba6 | ||
|
|
5a1c5eb5c0 | ||
|
|
6d65595a3c | ||
|
|
1cf43e08e3 | ||
|
|
6f7762538b | ||
|
|
174fe4453a | ||
|
|
5684f48f70 | ||
|
|
6d637f0fdb | ||
|
|
3f6cdbd8b3 | ||
|
|
166a2a6207 | ||
|
|
4346498c85 | ||
|
|
7685851594 | ||
|
|
f8c881b887 | ||
|
|
1a40d9b976 | ||
|
|
b4094b35ef | ||
|
|
22933a8cb4 | ||
|
|
56c8bddf6b | ||
|
|
3a78f501e9 | ||
|
|
f4a2c35691 | ||
|
|
3b83d0c75b | ||
|
|
42a6da2da3 | ||
|
|
34ff7d73a7 | ||
|
|
6a4bf9eec1 | ||
|
|
aa1d34dc51 | ||
|
|
ca0033c11a | ||
|
|
9d9108fe25 | ||
|
|
24aab7a08d | ||
|
|
313a70da6e | ||
|
|
7c7b583603 | ||
|
|
e1dce5c39a | ||
|
|
6f852d0d22 | ||
|
|
4a37b627ae | ||
|
|
d01d26e521 | ||
|
|
c58ff55e19 | ||
|
|
048709c8e7 | ||
|
|
d7199c8785 | ||
|
|
e4373eba1c | ||
|
|
6d32d34233 | ||
|
|
f6e4e8ab9d | ||
|
|
b5bdcb4602 | ||
|
|
fafc132dcb | ||
|
|
53eef278ef | ||
|
|
3261a9451a | ||
|
|
84aa8706c0 | ||
|
|
a430c43736 | ||
|
|
ae5ad9e1ae | ||
|
|
d4b540eb7d | ||
|
|
13c0eef54c | ||
|
|
4820737188 | ||
|
|
31ce7d61cd | ||
|
|
78e82a3ef7 | ||
|
|
d9133e9369 | ||
|
|
7e40882e02 | ||
|
|
9c91bb3ce2 | ||
|
|
9830fd8a80 | ||
|
|
afc13b9390 | ||
|
|
ef3a82a282 | ||
|
|
9f0da9b770 | ||
|
|
c3f923d0e0 | ||
|
|
2305b056c3 | ||
|
|
aa6dd09c2c | ||
|
|
66d9d4fa8d | ||
|
|
892ae95509 | ||
|
|
06cf349bb8 | ||
|
|
9a2adfba4d | ||
|
|
187f6ce409 | ||
|
|
5119657547 | ||
|
|
50b7dcbab3 | ||
|
|
e18ed45c88 | ||
|
|
a0afb6f375 | ||
|
|
5dfa67ed5a | ||
|
|
858cc9cace | ||
|
|
0826be48ed | ||
|
|
f2b1dc45bd | ||
|
|
e248e09399 | ||
|
|
625595cb1a | ||
|
|
4550beb695 | ||
|
|
f5f51cd09c | ||
|
|
c0f583fdc1 | ||
|
|
3e507a70ca | ||
|
|
7a3b8bbb89 | ||
|
|
bb3217b350 | ||
|
|
e7b16b5ef2 | ||
|
|
3127c304ec | ||
|
|
85c35bd615 | ||
|
|
29c09a92c4 | ||
|
|
922e91b278 | ||
|
|
7a73171edc | ||
|
|
6e1b72096d | ||
|
|
8e62bf6b17 | ||
|
|
f9ccca97e4 | ||
|
|
28507154fa | ||
|
|
9fa8b0f7ad | ||
|
|
7b3bb7b13f | ||
|
|
c6d8beeb9e | ||
|
|
72078148c0 | ||
|
|
c9796f85c7 | ||
|
|
ae53c32048 | ||
|
|
e145f9c6ec | ||
|
|
d0f19f7905 | ||
|
|
01e804a6e1 | ||
|
|
6fe9235f86 | ||
|
|
7023145f79 | ||
|
|
ba6db2e307 | ||
|
|
c224832acc | ||
|
|
49cbb77528 | ||
|
|
35eb582bf3 | ||
|
|
d426b58e57 | ||
|
|
5261aeb7c7 | ||
|
|
7b696e076a | ||
|
|
5fa7d2efe7 | ||
|
|
f353d126d1 | ||
|
|
f8ca80ce89 | ||
|
|
a44c722860 | ||
|
|
34027d8d87 | ||
|
|
399b851ed7 | ||
|
|
92bed66194 | ||
|
|
63893a427b | ||
|
|
aac67289e5 | ||
|
|
1b10e085d8 | ||
|
|
6a4e7e948d | ||
|
|
5205cd4ea9 | ||
|
|
270eb2df2a | ||
|
|
491ea32803 | ||
|
|
cd0086bc4b | ||
|
|
5d0104f67e | ||
|
|
0ce270d1f3 | ||
|
|
6f5870a791 | ||
|
|
8e589daaf2 | ||
|
|
a960236117 | ||
|
|
f7adb2c813 | ||
|
|
d81aa70106 | ||
|
|
aa6fbf2f5a | ||
|
|
7161776824 | ||
|
|
e22021d531 | ||
|
|
bc90801c2a | ||
|
|
fe2d744b53 | ||
|
|
9f606a8cbb | ||
|
|
7ab934f6bc | ||
|
|
6b1c4e9769 | ||
|
|
255461d895 | ||
|
|
3257b7574e | ||
|
|
714b0a467a | ||
|
|
9409be6e34 | ||
|
|
63b306743f | ||
|
|
67b0b7e30d | ||
|
|
c2920e2bd9 | ||
|
|
141fde1d8e | ||
|
|
99afbb277d | ||
|
|
a541c2ac8b | ||
|
|
36872e7bd4 | ||
|
|
a82cc31af8 | ||
|
|
10a36cb60e | ||
|
|
8796b2a784 | ||
|
|
bf982718cf | ||
|
|
0d594b8c0f | ||
|
|
83f7efa047 | ||
|
|
0cfce9a0ec | ||
|
|
9889ca8bca | ||
|
|
cfda270899 | ||
|
|
69e6d59ac8 | ||
|
|
10425fb9b1 | ||
|
|
b5518e23f5 | ||
|
|
afc907fd0d | ||
|
|
19b905eeef | ||
|
|
1b5f21e015 | ||
|
|
1c3052377b | ||
|
|
6fbdeb80c7 | ||
|
|
f64fff6476 | ||
|
|
9c186ccfe8 | ||
|
|
6e0fb11717 | ||
|
|
9039534eee | ||
|
|
a7b02b3a07 | ||
|
|
636f195bb5 | ||
|
|
8648750997 | ||
|
|
1ca199f9b3 | ||
|
|
64a5d7a018 | ||
|
|
5f8d3ea2fc | ||
|
|
3ace642ef3 | ||
|
|
1ce0f774c9 | ||
|
|
5bd959fbec | ||
|
|
4240ace597 | ||
|
|
f3551c8b90 | ||
|
|
e93704fbfd | ||
|
|
08e459e823 | ||
|
|
7c5261407d | ||
|
|
d8c37f4372 | ||
|
|
d5aadba4d3 | ||
|
|
6414df9756 | ||
|
|
25884c433f | ||
|
|
6fe7f45725 | ||
|
|
2baaa8fd8e | ||
|
|
89475e5d1e | ||
|
|
c121e8cea7 | ||
|
|
1492160887 | ||
|
|
d906c71e99 | ||
|
|
fc82dd50e3 | ||
|
|
80d9519a9c | ||
|
|
2889ea4dd9 | ||
|
|
d575de5159 | ||
|
|
57b19fda54 | ||
|
|
22d82e70b3 | ||
|
|
48325dbfb8 | ||
|
|
2ddbceeaa9 | ||
|
|
19f458f9cd | ||
|
|
15b2241b51 | ||
|
|
52adf2ec21 | ||
|
|
905e5c23e8 | ||
|
|
80a4e62096 | ||
|
|
2846abaefe | ||
|
|
6fb1101f70 | ||
|
|
2f0c62f5e6 | ||
|
|
f8e63d3991 | ||
|
|
1403d35e2a | ||
|
|
3a5532b4ed | ||
|
|
f8f0caf5c6 | ||
|
|
9c1e7f6cdc | ||
|
|
8b72f6abd7 | ||
|
|
28262c34c6 | ||
|
|
531671c9c5 | ||
|
|
2c88e8a0aa | ||
|
|
79ac96f123 | ||
|
|
e1b3a053ba | ||
|
|
95e5843ad5 | ||
|
|
3c9b85469e | ||
|
|
c93a2d76d5 | ||
|
|
d7ac6f5bcf | ||
|
|
e2b7d1056c | ||
|
|
47f0e799db | ||
|
|
2889da67cb | ||
|
|
7f87f3dd93 | ||
|
|
cb13128cf0 | ||
|
|
188f4785ad | ||
|
|
8bfce88e39 | ||
|
|
98fbb6a3a7 | ||
|
|
467d2e2a43 | ||
|
|
0c198dffc3 | ||
|
|
ec044a24e1 | ||
|
|
83b2f8f0f1 | ||
|
|
36e6ec45a9 | ||
|
|
0d63f1021d | ||
|
|
d124214129 | ||
|
|
9be01564f2 | ||
|
|
77db3d5ede | ||
|
|
1ccf063f80 | ||
|
|
26a6cc0f94 | ||
|
|
10b0d6b33f | ||
|
|
1a8549debb | ||
|
|
b8ddd52a0d | ||
|
|
b373e7a456 | ||
|
|
77237093da | ||
|
|
f405c6f19e | ||
|
|
e6e75e5547 | ||
|
|
c791f6312b | ||
|
|
03db9194d8 | ||
|
|
f74cce164e | ||
|
|
1c3b53c5db | ||
|
|
34eb0dad15 | ||
|
|
7df5db7b0c | ||
|
|
07000942da | ||
|
|
afda0ed9f2 | ||
|
|
078a3c3631 | ||
|
|
e00a4a4b44 | ||
|
|
6f09b4b5ed | ||
|
|
39887ee4df | ||
|
|
5c6a0f0c12 | ||
|
|
fab26dcbe9 | ||
|
|
ec27677f95 | ||
|
|
85b430a366 | ||
|
|
f3ed1322a6 | ||
|
|
5a7a4ab4c5 | ||
|
|
8ab4967703 | ||
|
|
16f51ef799 | ||
|
|
32272da9ac | ||
|
|
64213fc021 | ||
|
|
f92e820b4b | ||
|
|
85dce2c836 | ||
|
|
6d5ccdefe2 | ||
|
|
5f03ad3004 | ||
|
|
08693ae917 | ||
|
|
1a79d2d235 | ||
|
|
dc3e9c0cc3 | ||
|
|
61fbae8664 | ||
|
|
00f5a3e55c | ||
|
|
9ddef0144f | ||
|
|
285a873ce9 | ||
|
|
c6c6408c3f | ||
|
|
6979265fa6 | ||
|
|
85b8fab268 | ||
|
|
70b58c82d9 | ||
|
|
ecb3450f19 | ||
|
|
06fd6aca27 | ||
|
|
020331522b | ||
|
|
46d8e90f29 | ||
|
|
510c38cc63 | ||
|
|
3dfc12e8d8 | ||
|
|
ef52e3e4a3 | ||
|
|
25ab05e6a2 | ||
|
|
7029674775 | ||
|
|
9f7c602a84 | ||
|
|
3316604822 | ||
|
|
33145c3699 | ||
|
|
6d9d79c70d | ||
|
|
83d358d2c4 | ||
|
|
fd62210f58 | ||
|
|
5fea7caba2 | ||
|
|
705e52684a | ||
|
|
82d69cd06c | ||
|
|
e4e66186c3 | ||
|
|
1748bb02eb | ||
|
|
6e7869ec49 | ||
|
|
4846e25e92 | ||
|
|
49b7341a49 | ||
|
|
dcd3b5c8f8 | ||
|
|
0c7cbe2d11 | ||
|
|
9e7d3a6b69 | ||
|
|
3cd615a1d6 | ||
|
|
aee3f3fee9 | ||
|
|
76abbe9062 | ||
|
|
8d9501598f | ||
|
|
adeaf27e76 | ||
|
|
b496d8b7cb | ||
|
|
3c00658772 | ||
|
|
dbe76bb75d | ||
|
|
571e638e2a | ||
|
|
bfd4dc6d30 | ||
|
|
ee089488ad | ||
|
|
3fe23bf899 | ||
|
|
ff3fb96896 | ||
|
|
0ee1b0b5d9 | ||
|
|
192f4ae0b2 | ||
|
|
b473e7c97c | ||
|
|
fdf5d11b51 | ||
|
|
01621485fd | ||
|
|
15f0fc2d24 | ||
|
|
451c1f87f3 | ||
|
|
6355f12176 | ||
|
|
d55ee5b917 | ||
|
|
93261454e6 | ||
|
|
fb5f04a8c7 | ||
|
|
8b28c218c9 | ||
|
|
6c828c3bba | ||
|
|
e783424fbb | ||
|
|
510268e914 | ||
|
|
096ac67b97 | ||
|
|
296bd2f4cc | ||
|
|
7eebe22cf5 | ||
|
|
d1b36ced14 | ||
|
|
963f68abf4 | ||
|
|
c52ffa8ae7 | ||
|
|
5ecbf1e333 | ||
|
|
b379bd4a78 | ||
|
|
557e2147dd | ||
|
|
78bcf1d8c6 | ||
|
|
9f7ac04aaf | ||
|
|
81ba6c24ef | ||
|
|
055cdc2625 | ||
|
|
64206c13c2 | ||
|
|
eb42655c46 | ||
|
|
14f56363a4 | ||
|
|
1936ca8a2e | ||
|
|
fdba0d2526 | ||
|
|
d887d32f4d | ||
|
|
7068d0983a | ||
|
|
7f206913e1 | ||
|
|
c676c2c4f3 | ||
|
|
54ae1cb0e9 | ||
|
|
2fab8a31ae | ||
|
|
7d2ba3e1fc | ||
|
|
da8865bd64 | ||
|
|
eb41e7e417 | ||
|
|
d42eac90f6 | ||
|
|
a7cf23a234 | ||
|
|
959c64e8ce | ||
|
|
d1a21022e0 | ||
|
|
50eb6e579f | ||
|
|
27a36f1446 | ||
|
|
2622739771 | ||
|
|
e4c70eff77 | ||
|
|
de0af03862 | ||
|
|
9b62405f34 | ||
|
|
cab8121d8f | ||
|
|
28a3eb106c | ||
|
|
cd3a93a9c1 | ||
|
|
0855252f8c | ||
|
|
d6b974f821 | ||
|
|
11798fd01b | ||
|
|
b2464883ce | ||
|
|
f325b8b5be | ||
|
|
bbb273bb76 | ||
|
|
dc11cdb4a4 | ||
|
|
15a49e7297 | ||
|
|
22987b6817 | ||
|
|
7a444c5dab | ||
|
|
373f0dd82e | ||
|
|
3b57c19e3b | ||
|
|
aa0c9493c6 | ||
|
|
f7e33f240e | ||
|
|
65de6ccc0f | ||
|
|
8ad9b41850 | ||
|
|
6def8a4a48 | ||
|
|
a0e2aca770 | ||
|
|
7c20611cdd | ||
|
|
06495e791b | ||
|
|
2676973cd7 | ||
|
|
1c6dc15420 | ||
|
|
76467527d6 | ||
|
|
5d60cce242 | ||
|
|
070a0c1c75 | ||
|
|
1035efc7d6 | ||
|
|
c5a5d9b30b | ||
|
|
967b235035 | ||
|
|
8c6baeedab | ||
|
|
bdf7083cfd | ||
|
|
8efea0ccbb | ||
|
|
4e8b020494 | ||
|
|
84a4669e54 | ||
|
|
7e312c421e | ||
|
|
598c56c498 | ||
|
|
de464c03e1 | ||
|
|
e0d7d402e1 | ||
|
|
ad10d12177 | ||
|
|
baaf864237 | ||
|
|
983af125ac | ||
|
|
a1fb8f143f | ||
|
|
5bfe236fb5 | ||
|
|
6d3f87ee07 | ||
|
|
0885e68beb | ||
|
|
fe55f92cdc | ||
|
|
8952a17871 | ||
|
|
1f1b65a625 | ||
|
|
e071734b4e | ||
|
|
7be45a4c5b | ||
|
|
cf3089a8f8 | ||
|
|
1a9059dd3a | ||
|
|
1a6621ed84 | ||
|
|
5bf8cc225f | ||
|
|
7eeb0c8d22 | ||
|
|
fd0f89ebf3 | ||
|
|
414b30b2ec | ||
|
|
eb865f8c24 | ||
|
|
045abd6c04 | ||
|
|
632f85b653 | ||
|
|
b146a13f63 | ||
|
|
216d97c21a | ||
|
|
ce7ee648b3 | ||
|
|
6638432457 | ||
|
|
253570784c | ||
|
|
fd0a608449 | ||
|
|
2a388c4dc0 | ||
|
|
fc0aa7ac97 | ||
|
|
6ff7004db0 | ||
|
|
37551e6798 | ||
|
|
36e6fc8ab8 | ||
|
|
89d1487b2e | ||
|
|
d25ee22e34 | ||
|
|
ba118b4543 | ||
|
|
2db3c9a72e | ||
|
|
0db95259db | ||
|
|
f31a8ce4bc | ||
|
|
daf9ec790a | ||
|
|
a3d423e5ec | ||
|
|
3f8ff0d181 | ||
|
|
75a9b74650 | ||
|
|
569c88d721 | ||
|
|
21c3df31de | ||
|
|
59b0b85835 | ||
|
|
165f059a73 | ||
|
|
eb96acec46 | ||
|
|
2fa8eb3cfe | ||
|
|
7b09a0904c | ||
|
|
6f2fdc5ac4 | ||
|
|
19a0db6d14 | ||
|
|
f32f19162c | ||
|
|
1580aa30cb | ||
|
|
a40c381ffd | ||
|
|
65a0a4b585 | ||
|
|
77e33c4de6 | ||
|
|
d0d7b796c4 | ||
|
|
6ef9dafe4d | ||
|
|
4e854ee496 | ||
|
|
aab20ad431 | ||
|
|
a12a34100a | ||
|
|
4a1acc8b5f | ||
|
|
cf5b1d0283 | ||
|
|
7ba35c4d4f | ||
|
|
b82cca7283 | ||
|
|
5f6586ae6d | ||
|
|
38a0d404b7 | ||
|
|
7fbb4f78da | ||
|
|
e403e9ea97 | ||
|
|
84cbfc0698 | ||
|
|
3fe5eecc2b | ||
|
|
718afcc107 | ||
|
|
8074cbdc47 | ||
|
|
47e165d6d5 | ||
|
|
7d7e8eef23 | ||
|
|
0a3e91b782 | ||
|
|
1f5a5dce43 | ||
|
|
634e901966 | ||
|
|
2b517a18d2 | ||
|
|
cdc5a49333 | ||
|
|
fe992ca992 | ||
|
|
d74a8626e0 | ||
|
|
3349c4914d | ||
|
|
2254adf845 | ||
|
|
26ec470afb | ||
|
|
efbd721f20 | ||
|
|
aec07a1cb5 | ||
|
|
9075da126c | ||
|
|
92f648b04e | ||
|
|
ecd07d6804 | ||
|
|
710ac0db51 | ||
|
|
a54f6ee648 | ||
|
|
3ee366a199 | ||
|
|
ee28a13009 | ||
|
|
18a7751c7b | ||
|
|
a753fd601d | ||
|
|
fcc0d6ebd9 | ||
|
|
f6d5daac46 | ||
|
|
d4f443d8d0 | ||
|
|
3207777cd4 | ||
|
|
bcb4f2fd93 | ||
|
|
347383b1bc | ||
|
|
eec29a8fc3 | ||
|
|
6eeb912a63 | ||
|
|
c59e019f5c | ||
|
|
c347921830 | ||
|
|
c2f2d9754b | ||
|
|
42817f032a | ||
|
|
8075be8261 | ||
|
|
fd348ba8cb | ||
|
|
9eae42b9e1 | ||
|
|
330a3e3ddb | ||
|
|
2344cd6ebf | ||
|
|
51c87e6199 | ||
|
|
eea31f29a5 | ||
|
|
8e2fb4633e | ||
|
|
295e1e759f | ||
|
|
5b1d47f7e4 | ||
|
|
cdf0b5bc79 | ||
|
|
c432d8b2bc | ||
|
|
d091bb00c0 | ||
|
|
846ee2c62e | ||
|
|
2062d984ab | ||
|
|
ff2f71379b | ||
|
|
0d04b40d13 | ||
|
|
fcac7f11cf | ||
|
|
7e3b8abf99 | ||
|
|
e69b49bd3d | ||
|
|
756902cca1 | ||
|
|
8b5f760b72 | ||
|
|
c43edfc7c0 | ||
|
|
024f0d384c | ||
|
|
171aabc87d | ||
|
|
f2f027d1fb | ||
|
|
cd8a374504 | ||
|
|
2ae740dd49 | ||
|
|
524fd0bc8c | ||
|
|
57d628ca74 | ||
|
|
662c245795 | ||
|
|
1263d3bd14 | ||
|
|
a0a61acdec | ||
|
|
7673835b3d | ||
|
|
96b4dcafc3 | ||
|
|
3d61fc4428 | ||
|
|
57f9623b08 | ||
|
|
037ae3f024 | ||
|
|
aad99260d9 | ||
|
|
e047dd6a32 | ||
|
|
29d33203d0 | ||
|
|
bac908ef35 | ||
|
|
d5f88dafaf | ||
|
|
56e75eace5 | ||
|
|
9311a823a7 | ||
|
|
64ba43411f | ||
|
|
909b9751ee | ||
|
|
07556da918 | ||
|
|
b074a2eec9 | ||
|
|
bcde664f9a | ||
|
|
982663d063 | ||
|
|
986871bf4e | ||
|
|
28d09ae0fc | ||
|
|
5da342e4dd | ||
|
|
aa1d4cb840 | ||
|
|
e6ac789554 | ||
|
|
ab6e630194 | ||
|
|
a3e4395a42 | ||
|
|
900fa2f795 | ||
|
|
5ceb0a59dd | ||
|
|
0834da832a | ||
|
|
2c66746a37 | ||
|
|
8d5234e4c7 | ||
|
|
38eb5d3b49 | ||
|
|
25a5d30311 | ||
|
|
a3415fc6dc | ||
|
|
ef434ea998 | ||
|
|
d49cfb35d4 | ||
|
|
980168d084 | ||
|
|
2659f0a5e6 | ||
|
|
b5d9084f15 | ||
|
|
0586188f22 | ||
|
|
bfa9aceb03 | ||
|
|
a671e47921 | ||
|
|
240a11052b | ||
|
|
a83d6d2528 | ||
|
|
1feb69d595 | ||
|
|
a250997f75 | ||
|
|
4f6b310513 | ||
|
|
1234eb0f9f | ||
|
|
3ddf7fd2c2 | ||
|
|
07caa44224 | ||
|
|
8f422d2b5e | ||
|
|
aac0f585b9 | ||
|
|
829aa65e64 | ||
|
|
7df10cd8e0 | ||
|
|
468705df17 | ||
|
|
200ce9bd39 | ||
|
|
0c0b31a789 | ||
|
|
4bceeccfff | ||
|
|
413778afc1 | ||
|
|
941bc9ff60 | ||
|
|
a3f07829ca | ||
|
|
599f32c7a4 | ||
|
|
2ceae5f8a5 | ||
|
|
6429bf19a0 | ||
|
|
40eadfc52a | ||
|
|
12666af6b5 | ||
|
|
2ec150af02 | ||
|
|
db061cf736 | ||
|
|
96726f6a04 | ||
|
|
553b26f857 | ||
|
|
b33e89935a | ||
|
|
353d891561 | ||
|
|
33db535108 | ||
|
|
0adbaf270a | ||
|
|
b7f6fae9b3 | ||
|
|
a0a49e1c9e | ||
|
|
fb4aab5d89 | ||
|
|
7d0293d37c | ||
|
|
3628cbceaf | ||
|
|
722d5e610a | ||
|
|
d694eb443a | ||
|
|
c478e5d52f | ||
|
|
901661c1a4 | ||
|
|
008d3388b9 | ||
|
|
e4c7033a34 | ||
|
|
73d997f0fc |
|
|
@ -1,85 +0,0 @@
|
|||
trigger:
|
||||
- 'master'
|
||||
- '*.x'
|
||||
|
||||
jobs:
|
||||
- job: Tests
|
||||
variables:
|
||||
vmImage: 'ubuntu-latest'
|
||||
python.version: '3.8'
|
||||
TOXENV: 'py,coverage-ci'
|
||||
hasTestResults: 'true'
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
Python 3.8 Linux:
|
||||
vmImage: 'ubuntu-latest'
|
||||
Python 3.8 Windows:
|
||||
vmImage: 'windows-latest'
|
||||
Python 3.8 Mac:
|
||||
vmImage: 'macos-latest'
|
||||
PyPy 3 Linux:
|
||||
python.version: 'pypy3'
|
||||
Python 3.7 Linux:
|
||||
python.version: '3.7'
|
||||
Python 3.6 Linux:
|
||||
python.version: '3.6'
|
||||
Python 3.5 Linux:
|
||||
python.version: '3.5'
|
||||
Python 2.7 Linux:
|
||||
python.version: '2.7'
|
||||
Python 2.7 Windows:
|
||||
python.version: '2.7'
|
||||
vmImage: 'windows-latest'
|
||||
Docs:
|
||||
TOXENV: 'docs'
|
||||
hasTestResults: 'false'
|
||||
Style:
|
||||
TOXENV: 'style'
|
||||
hasTestResults: 'false'
|
||||
VersionRange:
|
||||
TOXENV: 'devel,lowest,coverage-ci'
|
||||
|
||||
pool:
|
||||
vmImage: $[ variables.vmImage ]
|
||||
|
||||
steps:
|
||||
- task: UsePythonVersion@0
|
||||
inputs:
|
||||
versionSpec: $(python.version)
|
||||
displayName: Use Python $(python.version)
|
||||
|
||||
- script: pip --disable-pip-version-check install -U tox
|
||||
displayName: Install tox
|
||||
|
||||
- script: tox -s false -- --junitxml=test-results.xml tests examples
|
||||
displayName: Run tox
|
||||
|
||||
- task: PublishTestResults@2
|
||||
inputs:
|
||||
testResultsFiles: test-results.xml
|
||||
testRunTitle: $(Agent.JobName)
|
||||
condition: eq(variables['hasTestResults'], 'true')
|
||||
displayName: Publish test results
|
||||
|
||||
- task: PublishCodeCoverageResults@1
|
||||
inputs:
|
||||
codeCoverageTool: Cobertura
|
||||
summaryFileLocation: coverage.xml
|
||||
condition: eq(variables['hasTestResults'], 'true')
|
||||
displayName: Publish coverage results
|
||||
|
||||
# Test on the nightly version of Python.
|
||||
# Use a container since Azure Pipelines may not have the latest build.
|
||||
- job: FlaskOnNightly
|
||||
pool:
|
||||
vmImage: ubuntu-latest
|
||||
container: python:rc-stretch
|
||||
steps:
|
||||
- script: |
|
||||
echo "##vso[task.prependPath]$HOME/.local/bin"
|
||||
pip --disable-pip-version-check install --user -U tox
|
||||
displayName: Install tox
|
||||
|
||||
- script: tox -e nightly
|
||||
displayName: Run tox
|
||||
17
.devcontainer/devcontainer.json
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"name": "pallets/flask",
|
||||
"image": "mcr.microsoft.com/devcontainers/python:3",
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"settings": {
|
||||
"python.defaultInterpreterPath": "${workspaceFolder}/.venv",
|
||||
"python.terminal.activateEnvInCurrentTerminal": true,
|
||||
"python.terminal.launchArgs": [
|
||||
"-X",
|
||||
"dev"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"onCreateCommand": ".devcontainer/on-create-command.sh"
|
||||
}
|
||||
7
.devcontainer/on-create-command.sh
Executable file
|
|
@ -0,0 +1,7 @@
|
|||
#!/bin/bash
|
||||
set -e
|
||||
python3 -m venv --upgrade-deps .venv
|
||||
. .venv/bin/activate
|
||||
pip install -r requirements/dev.txt
|
||||
pip install -e .
|
||||
pre-commit install --install-hooks
|
||||
13
.editorconfig
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
max_line_length = 88
|
||||
|
||||
[*.{css,html,js,json,jsx,scss,ts,tsx,yaml,yml}]
|
||||
indent_size = 2
|
||||
33
.github/ISSUE_TEMPLATE.md
vendored
|
|
@ -1,33 +0,0 @@
|
|||
**This issue tracker is a tool to address bugs in Flask itself.
|
||||
Please use the #pocoo IRC channel on freenode or Stack Overflow for general
|
||||
questions about using Flask or issues not related to Flask.**
|
||||
|
||||
If you'd like to report a bug in Flask, fill out the template below. Provide
|
||||
any extra information that may be useful / related to your problem.
|
||||
Ideally, create an [MCVE](https://stackoverflow.com/help/mcve), which helps us
|
||||
understand the problem and helps check that it is not caused by something in
|
||||
your code.
|
||||
|
||||
---
|
||||
|
||||
### Expected Behavior
|
||||
|
||||
Tell us what should happen.
|
||||
|
||||
```python
|
||||
Paste a minimal example that causes the problem.
|
||||
```
|
||||
|
||||
### Actual Behavior
|
||||
|
||||
Tell us what happens instead.
|
||||
|
||||
```pytb
|
||||
Paste the full traceback if there was an exception.
|
||||
```
|
||||
|
||||
### Environment
|
||||
|
||||
* Python version:
|
||||
* Flask version:
|
||||
* Werkzeug version:
|
||||
27
.github/ISSUE_TEMPLATE/bug-report.md
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
---
|
||||
name: Bug report
|
||||
about: Report a bug in Flask (not other projects which depend on Flask)
|
||||
---
|
||||
|
||||
<!--
|
||||
This issue tracker is a tool to address bugs in Flask itself. Please use
|
||||
GitHub Discussions or the Pallets Discord for questions about your own code.
|
||||
|
||||
Replace this comment with a clear outline of what the bug is.
|
||||
-->
|
||||
|
||||
<!--
|
||||
Describe how to replicate the bug.
|
||||
|
||||
Include a minimal reproducible example that demonstrates the bug.
|
||||
Include the full traceback if there was an exception.
|
||||
-->
|
||||
|
||||
<!--
|
||||
Describe the expected behavior that should have happened but didn't.
|
||||
-->
|
||||
|
||||
Environment:
|
||||
|
||||
- Python version:
|
||||
- Flask version:
|
||||
11
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Security issue
|
||||
url: https://github.com/pallets/flask/security/advisories/new
|
||||
about: Do not report security issues publicly. Create a private advisory.
|
||||
- name: Questions on GitHub Discussions
|
||||
url: https://github.com/pallets/flask/discussions/
|
||||
about: Ask questions about your own code on the Discussions tab.
|
||||
- name: Questions on Discord
|
||||
url: https://discord.gg/pallets
|
||||
about: Ask questions about your own code on our Discord chat.
|
||||
15
.github/ISSUE_TEMPLATE/feature-request.md
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
---
|
||||
name: Feature request
|
||||
about: Suggest a new feature for Flask
|
||||
---
|
||||
|
||||
<!--
|
||||
Replace this comment with a description of what the feature should do.
|
||||
Include details such as links to relevant specs or previous discussions.
|
||||
-->
|
||||
|
||||
<!--
|
||||
Replace this comment with an example of the problem which this feature
|
||||
would resolve. Is this problem solvable without changes to Flask, such
|
||||
as by subclassing or using an extension?
|
||||
-->
|
||||
16
.github/PULL_REQUEST_TEMPLATE.md
vendored
|
|
@ -1,16 +0,0 @@
|
|||
Describe what this patch does to fix the issue.
|
||||
|
||||
Link to any relevant issues or pull requests.
|
||||
|
||||
<!--
|
||||
Commit checklist:
|
||||
|
||||
* add tests that fail without the patch
|
||||
* ensure all tests pass with ``pytest``
|
||||
* add documentation to the relevant docstrings or pages
|
||||
* add ``versionadded`` or ``versionchanged`` directives to relevant docstrings
|
||||
* add a changelog entry if this patch changes code
|
||||
|
||||
Tests, coverage, and docs will be run automatically when you submit the pull
|
||||
request, but running them yourself can save time.
|
||||
-->
|
||||
25
.github/pull_request_template.md
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
<!--
|
||||
Before opening a PR, open a ticket describing the issue or feature the
|
||||
PR will address. An issue is not required for fixing typos in
|
||||
documentation, or other simple non-code changes.
|
||||
|
||||
Replace this comment with a description of the change. Describe how it
|
||||
addresses the linked ticket.
|
||||
-->
|
||||
|
||||
<!--
|
||||
Link to relevant issues or previous PRs, one per line. Use "fixes" to
|
||||
automatically close an issue.
|
||||
|
||||
fixes #<issue number>
|
||||
-->
|
||||
|
||||
<!--
|
||||
Ensure each step in CONTRIBUTING.rst is complete, especially the following:
|
||||
|
||||
- Add tests that demonstrate the correct behavior of the change. Tests
|
||||
should fail without the change.
|
||||
- Add or update relevant docs, in the docs folder and in code.
|
||||
- Add an entry in CHANGES.rst summarizing the change and linking to the issue.
|
||||
- Add `.. versionchanged::` entries in any relevant code docs.
|
||||
-->
|
||||
26
.github/workflows/lock.yaml
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
name: Lock inactive closed issues
|
||||
# Lock closed issues that have not received any further activity for two weeks.
|
||||
# This does not close open issues, only humans may do that. It is easier to
|
||||
# respond to new issues with fresh examples rather than continuing discussions
|
||||
# on old issues.
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
permissions: {}
|
||||
concurrency:
|
||||
group: lock
|
||||
cancel-in-progress: true
|
||||
jobs:
|
||||
lock:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
discussions: write
|
||||
steps:
|
||||
- uses: dessant/lock-threads@7266a7ce5c1df01b1c6db85bf8cd86c737dadbe7 # v6.0.0
|
||||
with:
|
||||
issue-inactive-days: 14
|
||||
pr-inactive-days: 14
|
||||
discussion-inactive-days: 14
|
||||
29
.github/workflows/pre-commit.yaml
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
name: pre-commit
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches: [main, stable]
|
||||
permissions: {}
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
jobs:
|
||||
main:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
|
||||
with:
|
||||
enable-cache: true
|
||||
prune-cache: false
|
||||
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||
id: setup-python
|
||||
with:
|
||||
python-version-file: pyproject.toml
|
||||
- uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
|
||||
with:
|
||||
path: ~/.cache/pre-commit
|
||||
key: pre-commit|${{ hashFiles('pyproject.toml', '.pre-commit-config.yaml') }}
|
||||
- run: uv run --locked --no-default-groups --group pre-commit pre-commit run --show-diff-on-failure --color=always --all-files
|
||||
62
.github/workflows/publish.yaml
vendored
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
name: Publish
|
||||
on:
|
||||
push:
|
||||
tags: ['*']
|
||||
permissions: {}
|
||||
concurrency:
|
||||
group: publish-${{ github.event.push.ref }}
|
||||
cancel-in-progress: true
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
artifact-id: ${{ steps.upload-artifact.outputs.artifact-id }}
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
|
||||
with:
|
||||
enable-cache: false
|
||||
prune-cache: false
|
||||
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||
with:
|
||||
python-version-file: pyproject.toml
|
||||
- run: echo "SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct)" >> $GITHUB_ENV
|
||||
- run: uv build
|
||||
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
id: upload-artifact
|
||||
with:
|
||||
name: dist
|
||||
path: dist/
|
||||
if-no-files-found: error
|
||||
create-release:
|
||||
needs: [build]
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
artifact-ids: ${{ needs.build.outputs.artifact-id }}
|
||||
path: dist/
|
||||
- name: create release
|
||||
run: gh release create --draft --repo ${GITHUB_REPOSITORY} ${GITHUB_REF_NAME} dist/*
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
publish-pypi:
|
||||
needs: [build]
|
||||
environment:
|
||||
name: publish
|
||||
url: https://pypi.org/project/Flask/${{ github.ref_name }}
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
id-token: write
|
||||
steps:
|
||||
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
artifact-ids: ${{ needs.build.outputs.artifact-id }}
|
||||
path: dist/
|
||||
- uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
|
||||
with:
|
||||
packages-dir: "dist/"
|
||||
63
.github/workflows/tests.yaml
vendored
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
name: Tests
|
||||
on:
|
||||
pull_request:
|
||||
paths-ignore: ['docs/**', 'README.md']
|
||||
push:
|
||||
branches: [main, stable]
|
||||
paths-ignore: ['docs/**', 'README.md']
|
||||
permissions: {}
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
jobs:
|
||||
tests:
|
||||
name: ${{ matrix.name || matrix.python }}
|
||||
runs-on: ${{ matrix.os || 'ubuntu-latest' }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- {python: '3.14'}
|
||||
- {python: '3.14t'}
|
||||
- {name: Windows, python: '3.14', os: windows-latest}
|
||||
- {name: Mac, python: '3.14', os: macos-latest}
|
||||
- {python: '3.13'}
|
||||
- {python: '3.12'}
|
||||
- {python: '3.11'}
|
||||
- {python: '3.10'}
|
||||
- {name: PyPy, python: 'pypy-3.11', tox: pypy3.11}
|
||||
- {name: Minimum Versions, python: '3.14', tox: tests-min}
|
||||
- {name: Development Versions, python: '3.10', tox: tests-dev}
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
|
||||
with:
|
||||
enable-cache: true
|
||||
prune-cache: false
|
||||
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||
with:
|
||||
python-version: ${{ matrix.python }}
|
||||
- run: uv run --locked --no-default-groups --group dev tox run
|
||||
env:
|
||||
TOX_ENV: ${{ matrix.tox || format('py{0}', matrix.python) }}
|
||||
typing:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: astral-sh/setup-uv@cec208311dfd045dd5311c1add060b2062131d57 # v8.0.0
|
||||
with:
|
||||
enable-cache: true
|
||||
prune-cache: false
|
||||
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||
with:
|
||||
python-version-file: pyproject.toml
|
||||
- name: cache mypy
|
||||
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
|
||||
with:
|
||||
path: ./.mypy_cache
|
||||
key: mypy|${{ hashFiles('pyproject.toml') }}
|
||||
- run: uv run --locked --no-default-groups --group dev tox run -e typing
|
||||
22
.github/workflows/zizmor.yaml
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
name: GitHub Actions security analysis with zizmor
|
||||
on:
|
||||
pull_request:
|
||||
paths: ["**/*.yaml?"]
|
||||
push:
|
||||
branches: [main, stable]
|
||||
paths: ["**/*.yaml?"]
|
||||
permissions: {}
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
jobs:
|
||||
zizmor:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: zizmorcore/zizmor-action@71321a20a9ded102f6e9ce5718a2fcec2c4f70d8 # v0.5.2
|
||||
with:
|
||||
advanced-security: false
|
||||
annotations: true
|
||||
28
.gitignore
vendored
|
|
@ -1,24 +1,8 @@
|
|||
.DS_Store
|
||||
.env
|
||||
.flaskenv
|
||||
*.pyc
|
||||
*.pyo
|
||||
env/
|
||||
env*
|
||||
dist/
|
||||
build/
|
||||
*.egg
|
||||
*.egg-info/
|
||||
_mailinglist
|
||||
.tox/
|
||||
.cache/
|
||||
.pytest_cache/
|
||||
.idea/
|
||||
docs/_build/
|
||||
.vscode
|
||||
|
||||
# Coverage reports
|
||||
.vscode/
|
||||
__pycache__/
|
||||
dist/
|
||||
.coverage*
|
||||
htmlcov/
|
||||
.coverage
|
||||
.coverage.*
|
||||
*,cover
|
||||
.tox/
|
||||
docs/_build/
|
||||
|
|
|
|||
|
|
@ -1,23 +1,23 @@
|
|||
repos:
|
||||
- repo: https://github.com/asottile/reorder_python_imports
|
||||
rev: v1.5.0
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: 5e2fb545eba1ea9dc051f6f962d52fe8f76a9794 # frozen: v0.15.13
|
||||
hooks:
|
||||
- id: reorder-python-imports
|
||||
name: Reorder Python imports (src, tests)
|
||||
files: "^(?!examples/)"
|
||||
args: ["--application-directories", ".:src"]
|
||||
- repo: https://github.com/python/black
|
||||
rev: 19.3b0
|
||||
- id: ruff-check
|
||||
- id: ruff-format
|
||||
- repo: https://github.com/astral-sh/uv-pre-commit
|
||||
rev: fa60a193803535a9e2accdb3ca4b1b584b1150cb # frozen: 0.11.15
|
||||
hooks:
|
||||
- id: black
|
||||
- repo: https://gitlab.com/pycqa/flake8
|
||||
rev: 3.7.7
|
||||
- id: uv-lock
|
||||
- repo: https://github.com/codespell-project/codespell
|
||||
rev: 2ccb47ff45ad361a21071a7eedda4c37e6ae8c5a # frozen: v2.4.2
|
||||
hooks:
|
||||
- id: flake8
|
||||
additional_dependencies: [flake8-bugbear]
|
||||
- id: codespell
|
||||
args: ['--write-changes']
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v2.2.3
|
||||
rev: 3e8a8703264a2f4a69428a0aa4dcb512790b2c8c # frozen: v6.0.0
|
||||
hooks:
|
||||
- id: check-byte-order-marker
|
||||
- id: check-merge-conflict
|
||||
- id: debug-statements
|
||||
- id: fix-byte-order-marker
|
||||
- id: trailing-whitespace
|
||||
- id: end-of-file-fixer
|
||||
|
|
|
|||
10
.readthedocs.yaml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
version: 2
|
||||
build:
|
||||
os: ubuntu-24.04
|
||||
tools:
|
||||
python: '3.13'
|
||||
commands:
|
||||
- asdf plugin add uv
|
||||
- asdf install uv latest
|
||||
- asdf global uv latest
|
||||
- uv run --group docs sphinx-build -W -b dirhtml docs $READTHEDOCS_OUTPUT/html
|
||||
1076
CHANGES.rst
|
|
@ -1,76 +0,0 @@
|
|||
# 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, sex characteristics, gender identity and expression,
|
||||
level of experience, education, socio-economic status, 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 report@palletsprojects.com. 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 https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see
|
||||
https://www.contributor-covenant.org/faq
|
||||
196
CONTRIBUTING.rst
|
|
@ -1,196 +0,0 @@
|
|||
How to contribute to Flask
|
||||
==========================
|
||||
|
||||
Thank you for considering contributing to Flask!
|
||||
|
||||
Support questions
|
||||
-----------------
|
||||
|
||||
Please, don't use the issue tracker for this. Use one of the following
|
||||
resources for questions about your own code:
|
||||
|
||||
* The ``#get-help`` channel on our Discord chat: https://discordapp.com/invite/t6rrQZH
|
||||
|
||||
* The IRC channel ``#pocoo`` on FreeNode is linked to Discord, but
|
||||
Discord is preferred.
|
||||
|
||||
* The mailing list flask@python.org for long term discussion or larger issues.
|
||||
* Ask on `Stack Overflow`_. Search with Google first using:
|
||||
``site:stackoverflow.com flask {search term, exception message, etc.}``
|
||||
|
||||
.. _Stack Overflow: https://stackoverflow.com/questions/tagged/flask?sort=linked
|
||||
|
||||
Reporting issues
|
||||
----------------
|
||||
|
||||
- Describe what you expected to happen.
|
||||
- If possible, include a `minimal reproducible example`_ to help us
|
||||
identify the issue. This also helps check that the issue is not with
|
||||
your own code.
|
||||
- Describe what actually happened. Include the full traceback if there was an
|
||||
exception.
|
||||
- List your Python, Flask, and Werkzeug versions. If possible, check if this
|
||||
issue is already fixed in the repository.
|
||||
|
||||
.. _minimal reproducible example: https://stackoverflow.com/help/minimal-reproducible-example
|
||||
|
||||
Submitting patches
|
||||
------------------
|
||||
|
||||
- Use `Black`_ to autoformat your code. This should be done for you as a
|
||||
git `pre-commit`_ hook, which gets installed when you run ``pip install -e .[dev]``.
|
||||
You may also wish to use Black's `Editor integration`_.
|
||||
- Include tests if your patch is supposed to solve a bug, and explain
|
||||
clearly under which circumstances the bug happens. Make sure the test fails
|
||||
without your patch.
|
||||
- Include a string like "Fixes #123" in your commit message
|
||||
(where 123 is the issue you fixed).
|
||||
See `Closing issues using keywords
|
||||
<https://help.github.com/articles/creating-a-pull-request/>`__.
|
||||
|
||||
First time setup
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
- Download and install the `latest version of git`_.
|
||||
- Configure git with your `username`_ and `email`_::
|
||||
|
||||
git config --global user.name 'your name'
|
||||
git config --global user.email 'your email'
|
||||
|
||||
- Make sure you have a `GitHub account`_.
|
||||
- Fork Flask to your GitHub account by clicking the `Fork`_ button.
|
||||
- `Clone`_ your GitHub fork locally::
|
||||
|
||||
git clone https://github.com/{username}/flask
|
||||
cd flask
|
||||
|
||||
- Add the main repository as a remote to update later::
|
||||
|
||||
git remote add pallets https://github.com/pallets/flask
|
||||
git fetch pallets
|
||||
|
||||
- Create a virtualenv::
|
||||
|
||||
python3 -m venv env
|
||||
. env/bin/activate
|
||||
# or "env\Scripts\activate" on Windows
|
||||
|
||||
- Install Flask in editable mode with development dependencies::
|
||||
|
||||
pip install -e ".[dev]"
|
||||
|
||||
- Install the `pre-commit framework`_.
|
||||
- Install the pre-commit hooks::
|
||||
|
||||
pre-commit install --install-hooks
|
||||
|
||||
.. _GitHub account: https://github.com/join
|
||||
.. _latest version of git: https://git-scm.com/downloads
|
||||
.. _username: https://help.github.com/en/articles/setting-your-username-in-git
|
||||
.. _email: https://help.github.com/en/articles/setting-your-commit-email-address-in-git
|
||||
.. _Fork: https://github.com/pallets/flask/fork
|
||||
.. _Clone: https://help.github.com/en/articles/fork-a-repo#step-2-create-a-local-clone-of-your-fork
|
||||
.. _pre-commit framework: https://pre-commit.com/#install
|
||||
|
||||
Start coding
|
||||
~~~~~~~~~~~~
|
||||
|
||||
- Create a branch to identify the issue you would like to work on. If
|
||||
you're submitting a bug or documentation fix, branch off of the
|
||||
latest ".x" branch::
|
||||
|
||||
git checkout -b your-branch-name origin/1.0.x
|
||||
|
||||
If you're submitting a feature addition or change, branch off of the
|
||||
"master" branch::
|
||||
|
||||
git checkout -b your-branch-name origin/master
|
||||
|
||||
- Using your favorite editor, make your changes, `committing as you go`_.
|
||||
- Include tests that cover any code changes you make. Make sure the test fails
|
||||
without your patch. `Run the tests. <contributing-testsuite_>`_.
|
||||
- Push your commits to GitHub and `create a pull request`_ by using::
|
||||
|
||||
git push --set-upstream origin your-branch-name
|
||||
|
||||
- Celebrate 🎉
|
||||
|
||||
.. _committing as you go: https://dont-be-afraid-to-commit.readthedocs.io/en/latest/git/commandlinegit.html#commit-your-changes
|
||||
.. _Black: https://black.readthedocs.io
|
||||
.. _Editor integration: https://black.readthedocs.io/en/stable/editor_integration.html
|
||||
.. _pre-commit: https://pre-commit.com
|
||||
.. _create a pull request: https://help.github.com/en/articles/creating-a-pull-request
|
||||
|
||||
.. _contributing-testsuite:
|
||||
|
||||
Running the tests
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Run the basic test suite with::
|
||||
|
||||
pytest
|
||||
|
||||
This only runs the tests for the current environment. Whether this is relevant
|
||||
depends on which part of Flask you're working on. Travis-CI will run the full
|
||||
suite when you submit your pull request.
|
||||
|
||||
The full test suite takes a long time to run because it tests multiple
|
||||
combinations of Python and dependencies. You need to have Python 2.7, 3.4,
|
||||
3.5 3.6, and PyPy 2.7 installed to run all of the environments. Then run::
|
||||
|
||||
tox
|
||||
|
||||
Running test coverage
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Generating a report of lines that do not have test coverage can indicate
|
||||
where to start contributing. Run ``pytest`` using ``coverage`` and generate a
|
||||
report on the terminal and as an interactive HTML document::
|
||||
|
||||
coverage run -m pytest
|
||||
coverage report
|
||||
coverage html
|
||||
# then open htmlcov/index.html
|
||||
|
||||
Read more about `coverage <https://coverage.readthedocs.io>`_.
|
||||
|
||||
Running the full test suite with ``tox`` will combine the coverage reports
|
||||
from all runs.
|
||||
|
||||
|
||||
Building the docs
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Build the docs in the ``docs`` directory using Sphinx::
|
||||
|
||||
cd docs
|
||||
pip install -r requirements.txt
|
||||
make html
|
||||
|
||||
Open ``_build/html/index.html`` in your browser to view the docs.
|
||||
|
||||
Read more about `Sphinx <https://www.sphinx-doc.org/en/master/>`_.
|
||||
|
||||
|
||||
Caution: zero-padded file modes
|
||||
-------------------------------
|
||||
|
||||
This repository contains several zero-padded file modes that may cause issues
|
||||
when pushing this repository to git hosts other than GitHub. Fixing this is
|
||||
destructive to the commit history, so we suggest ignoring these warnings. If it
|
||||
fails to push and you're using a self-hosted git service like GitLab, you can
|
||||
turn off repository checks in the admin panel.
|
||||
|
||||
These files can also cause issues while cloning. If you have ::
|
||||
|
||||
[fetch]
|
||||
fsckobjects = true
|
||||
|
||||
or ::
|
||||
|
||||
[receive]
|
||||
fsckObjects = true
|
||||
|
||||
set in your git configuration file, cloning this repository will fail. The only
|
||||
solution is to set both of the above settings to false while cloning, and then
|
||||
setting them back to true after the cloning is finished.
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
include CHANGES.rst
|
||||
include CONTRIBUTING.rst
|
||||
include tox.ini
|
||||
graft artwork
|
||||
graft docs
|
||||
prune docs/_build
|
||||
graft examples
|
||||
graft tests
|
||||
global-exclude *.pyc
|
||||
53
README.md
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
<div align="center"><img src="https://raw.githubusercontent.com/pallets/flask/refs/heads/stable/docs/_static/flask-name.svg" alt="" height="150"></div>
|
||||
|
||||
# Flask
|
||||
|
||||
Flask is a lightweight [WSGI] web application framework. It is designed
|
||||
to make getting started quick and easy, with the ability to scale up to
|
||||
complex applications. It began as a simple wrapper around [Werkzeug]
|
||||
and [Jinja], and has become one of the most popular Python web
|
||||
application frameworks.
|
||||
|
||||
Flask offers suggestions, but doesn't enforce any dependencies or
|
||||
project layout. It is up to the developer to choose the tools and
|
||||
libraries they want to use. There are many extensions provided by the
|
||||
community that make adding new functionality easy.
|
||||
|
||||
[WSGI]: https://wsgi.readthedocs.io/
|
||||
[Werkzeug]: https://werkzeug.palletsprojects.com/
|
||||
[Jinja]: https://jinja.palletsprojects.com/
|
||||
|
||||
## A Simple Example
|
||||
|
||||
```python
|
||||
# save this as app.py
|
||||
from flask import Flask
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
@app.route("/")
|
||||
def hello():
|
||||
return "Hello, World!"
|
||||
```
|
||||
|
||||
```
|
||||
$ flask run
|
||||
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
|
||||
```
|
||||
|
||||
## Donate
|
||||
|
||||
The Pallets organization develops and supports Flask and the libraries
|
||||
it uses. In order to grow the community of contributors and users, and
|
||||
allow the maintainers to devote more time to the projects, [please
|
||||
donate today].
|
||||
|
||||
[please donate today]: https://palletsprojects.com/donate
|
||||
|
||||
## Contributing
|
||||
|
||||
See our [detailed contributing documentation][contrib] for many ways to
|
||||
contribute, including reporting issues, requesting features, asking or answering
|
||||
questions, and making PRs.
|
||||
|
||||
[contrib]: https://palletsprojects.com/contributing/
|
||||
80
README.rst
|
|
@ -1,80 +0,0 @@
|
|||
Flask
|
||||
=====
|
||||
|
||||
Flask is a lightweight `WSGI`_ web application framework. It is designed
|
||||
to make getting started quick and easy, with the ability to scale up to
|
||||
complex applications. It began as a simple wrapper around `Werkzeug`_
|
||||
and `Jinja`_ and has become one of the most popular Python web
|
||||
application frameworks.
|
||||
|
||||
Flask offers suggestions, but doesn't enforce any dependencies or
|
||||
project layout. It is up to the developer to choose the tools and
|
||||
libraries they want to use. There are many extensions provided by the
|
||||
community that make adding new functionality easy.
|
||||
|
||||
|
||||
Installing
|
||||
----------
|
||||
|
||||
Install and update using `pip`_:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
pip install -U Flask
|
||||
|
||||
|
||||
A Simple Example
|
||||
----------------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from flask import Flask
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
@app.route("/")
|
||||
def hello():
|
||||
return "Hello, World!"
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ env FLASK_APP=hello.py flask run
|
||||
* Serving Flask app "hello"
|
||||
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
|
||||
|
||||
|
||||
Contributing
|
||||
------------
|
||||
|
||||
For guidance on setting up a development environment and how to make a
|
||||
contribution to Flask, see the `contributing guidelines`_.
|
||||
|
||||
.. _contributing guidelines: https://github.com/pallets/flask/blob/master/CONTRIBUTING.rst
|
||||
|
||||
|
||||
Donate
|
||||
------
|
||||
|
||||
The Pallets organization develops and supports Flask and the libraries
|
||||
it uses. In order to grow the community of contributors and users, and
|
||||
allow the maintainers to devote more time to the projects, `please
|
||||
donate today`_.
|
||||
|
||||
.. _please donate today: https://psfmember.org/civicrm/contribute/transact?reset=1&id=20
|
||||
|
||||
|
||||
Links
|
||||
-----
|
||||
|
||||
* Website: https://palletsprojects.com/p/flask/
|
||||
* Documentation: https://flask.palletsprojects.com/
|
||||
* Releases: https://pypi.org/project/Flask/
|
||||
* Code: https://github.com/pallets/flask
|
||||
* Issue tracker: https://github.com/pallets/flask/issues
|
||||
* Test status: https://dev.azure.com/pallets/flask/_build
|
||||
* Official chat: https://discord.gg/t6rrQZH
|
||||
|
||||
.. _WSGI: https://wsgi.readthedocs.io
|
||||
.. _Werkzeug: https://www.palletsprojects.com/p/werkzeug/
|
||||
.. _Jinja: https://www.palletsprojects.com/p/jinja/
|
||||
.. _pip: https://pip.pypa.io/en/stable/quickstart/
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
Copyright 2010 Pallets
|
||||
|
||||
This logo or a modified version may be used by anyone to refer to the
|
||||
Flask project, but does not indicate endorsement by the project.
|
||||
|
||||
Redistribution and use in source (SVG) and binary (renders in PNG, etc.)
|
||||
forms, with or without modification, are permitted provided that the
|
||||
following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice and this list of conditions.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
We would appreciate that you make the image a link to
|
||||
https://palletsprojects.com/p/flask/ if you use it in a medium that
|
||||
supports links.
|
||||
|
Before Width: | Height: | Size: 77 KiB |
|
|
@ -1,165 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="211.15901"
|
||||
height="190.52811"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.5 r10040"
|
||||
sodipodi:docname="logo-lineart.svg">
|
||||
<defs
|
||||
id="defs4">
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 526.18109 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="744.09448 : 526.18109 : 1"
|
||||
inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
|
||||
id="perspective10" />
|
||||
<inkscape:perspective
|
||||
id="perspective2824"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective2840"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective2878"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective2894"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective2910"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective2926"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective2976"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3020"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3036"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3052"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3866"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="2.1723341"
|
||||
inkscape:cx="242.05817"
|
||||
inkscape:cy="92.686293"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1676"
|
||||
inkscape:window-height="1005"
|
||||
inkscape:window-x="4"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
fit-margin-top="10"
|
||||
fit-margin-left="10"
|
||||
fit-margin-right="10"
|
||||
fit-margin-bottom="10" />
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-29.820801,-20.186869)">
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
d="M 9.7525867,55.788422 C 40.421293,45.982204 33.821969,46.567748 69.327984,40.346648 c 8.493721,2.411576 22.910914,5.687215 22.240236,12.296506 -6.241933,2.320572 -15.351869,-6.455434 -20.254712,-1.539882 0.01014,18.421641 5.965221,38.200493 13.480678,55.747588 7.515457,17.5471 18.880714,32.86245 34.290034,42.35708 20.42595,12.66826 41.92048,14.9356 63.64846,15.65546 6.66858,0.23786 17.30912,-1.47838 20.01846,0 -4.9124,8.703 -19.28006,12.8118 -34.21844,14.71154 -14.93837,1.89974 -30.44747,1.59043 -37.64272,1.45723 -15.88921,-0.50065 -29.5942,-2.65111 -42.06658,-7.29048 C 56.640409,160.78176 38.428746,134.71246 24.668078,106.25832 16.765019,89.693325 11.290118,72.259923 9.7525867,55.788422 z"
|
||||
id="path3826"
|
||||
inkscape:connector-curvature="0"
|
||||
transform="translate(29.820801,20.186869)"
|
||||
sodipodi:nodetypes="ccccscccscccc" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
d="m 54.066806,32.351647 c -0.427165,0.87404 -0.384822,1.998232 -0.02834,2.90275 0.781834,1.983761 2.799883,3.252081 4.397491,4.681241 0.728446,0.651642 2.26934,0.803097 2.364296,1.769134 0.215279,2.190161 -2.700769,3.566537 -4.456242,4.921486 -1.316317,1.015991 -3.845581,0.776849 -4.451985,2.314219 -0.417515,1.058499 0.837317,2.10047 1.1679,3.188615 0.465799,1.533243 1.642442,3.150334 1.145997,4.674061 -0.597449,1.833868 -2.700081,2.84663 -4.420626,3.754433 -1.893115,0.998854 -4.450538,0.497797 -6.207667,1.715064 -1.674125,1.159765 -3.485979,2.907099 -3.554321,4.925579 -0.03097,0.915115 -0.384582,2.676814 -0.233936,3.114037 12.863193,-4.155671 20.195138,-6.507915 28.694286,-8.598094 8.499136,-2.090222 16.108852,-3.399531 29.579722,-5.689662 -0.06867,-0.457321 -1.197061,-1.855664 -1.647827,-2.652661 -0.994254,-1.75795 -3.408869,-2.469029 -5.429591,-2.722885 -2.120906,-0.26644 -4.15652,1.360749 -6.296964,1.350851 -1.945327,-0.009 -4.277958,0.06569 -5.655921,-1.283841 -1.144955,-1.121286 -0.849755,-3.099246 -1.145997,-4.674061 -0.210243,-1.117649 0.420309,-2.621884 -0.439473,-3.367212 -1.248754,-1.082519 -3.380554,0.299434 -5.017542,0.0075 -2.183125,-0.389274 -5.405114,-0.260713 -6.227327,-2.302063 -0.362663,-0.900401 0.93342,-1.747432 1.277831,-2.662119 0.75535,-2.006065 1.957858,-4.064009 1.733419,-6.184432 -0.102333,-0.966833 -0.5848,-1.983113 -1.367813,-2.56044 -1.68203,-1.191313 -4.366912,-1.323763 -7.531636,-0.525881 -3.164723,0.797885 -5.342193,2.137743 -6.247739,3.90434 z"
|
||||
id="path3832"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="csssssscssscccssscssssssccc" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
d="m 71.836704,50.617573 c 0,0 -24.55635,5.277975 -35.918352,8.551988 C 27.8621,61.491007 12.143824,67.37947 12.143824,67.37947"
|
||||
id="path3847"
|
||||
inkscape:connector-curvature="0"
|
||||
transform="translate(29.820801,20.186869)"
|
||||
sodipodi:nodetypes="csc" />
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 7.9 KiB |
BIN
docs/_static/flask-icon.png
vendored
|
Before Width: | Height: | Size: 6.8 KiB |
15
docs/_static/flask-icon.svg
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="100%" height="100%" viewBox="0 0 500 500" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||
<rect id="Icon" x="0" y="0" width="500" height="500" style="fill:none;"/>
|
||||
<clipPath id="_clip1">
|
||||
<rect x="0" y="0" width="500" height="500"/>
|
||||
</clipPath>
|
||||
<g clip-path="url(#_clip1)">
|
||||
<g>
|
||||
<path d="M224.446,59.975c-0.056,-4.151 -0.483,-5.543 -2.7,-6.823c-2.104,-1.393 -5.288,-1.421 -8.329,-0.085l-204.674,87.64c-3.042,1.336 -5.913,4.008 -7.448,6.908c-1.535,2.899 -1.705,5.97 -0.511,8.158l17.084,31.384l0.228,0.369c1.847,2.928 6.026,3.696 10.29,1.82l1.251,-0.54c5.344,22.4 14.1,50.429 25.783,70.413l178.294,-79.794c-2.559,-23.14 -9.552,-89.602 -9.268,-119.479l0,0.029Z" style="fill:#3babc3;fill-rule:nonzero;"/>
|
||||
<path d="M238.603,205.776l-171.698,76.838c10.091,19.132 22.542,39.428 37.722,58.986c50.429,-25.698 100.887,-51.396 151.316,-77.094c-3.269,-8.471 -6.452,-17.653 -17.34,-58.73Z" style="fill:#3babc3;fill-rule:nonzero;"/>
|
||||
<path d="M497.601,388.846l-12.139,-18.535c-1.819,-2.018 -4.633,-2.786 -7.106,-1.791l-15.578,5.999c-1.848,-2.047 -4.52,-2.815 -7.135,-1.791c-5.089,1.99 -10.206,4.008 -15.294,5.998c-1.649,0.625 -2.104,1.847 -1.791,3.439l0.995,4.861c-28.711,3.099 -77.236,1.564 -120.701,-32.577c-19.216,-15.066 -37.239,-36.386 -52.277,-66.206l-144.75,73.768c26.466,29.08 59.697,54.864 100.973,70.385c57.422,21.633 130.593,23.679 222.838,-13.475l0.512,2.616c0.455,2.928 3.98,6.026 8.755,4.15l15.323,-5.97c5.258,-1.99 5.287,-6.026 4.519,-8.641l19.729,-7.704c2.217,-0.853 9.096,-6.169 3.183,-14.526l-0.056,-0Z" style="fill:#3babc3;fill-rule:nonzero;"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2 KiB |
BIN
docs/_static/flask-logo.png
vendored
|
Before Width: | Height: | Size: 14 KiB |
17
docs/_static/flask-logo.svg
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="100%" height="100%" viewBox="0 0 500 500" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||
<rect id="Logo" x="0" y="0" width="500" height="500" style="fill:none;"/>
|
||||
<g>
|
||||
<path id="Box" d="M500,50l0,400c0,27.596 -22.404,50 -50,50l-400,0c-27.596,0 -50,-22.404 -50,-50l0,-400c0,-27.596 22.404,-50 50,-50l400,0c27.596,0 50,22.404 50,50Z" style="fill:url(#_Linear1);"/>
|
||||
<path id="Shadow" d="M500,398.646l0,51.354c0,27.596 -22.404,50 -50,50l-94.111,0l-170.452,-170.451c13.541,12.279 29.511,22.845 48.242,29.888c34.453,12.98 78.356,14.208 133.703,-8.084l0.307,1.569c0.273,1.757 2.388,3.616 5.253,2.49l9.193,-3.582c3.156,-1.194 3.173,-3.616 2.712,-5.185l11.837,-4.622c1.331,-0.512 5.458,-3.701 1.91,-8.716l-0.034,0l-7.283,-11.12c-1.091,-1.211 -2.78,-1.672 -4.264,-1.075l-9.346,3.599c-1.109,-1.228 -2.712,-1.688 -4.281,-1.074c-3.054,1.194 -6.124,2.404 -9.177,3.598c-0.989,0.376 -1.262,1.109 -1.074,2.064l0.597,2.917c-17.227,1.859 -46.342,0.938 -72.421,-19.547c-11.53,-9.039 -22.343,-21.831 -31.366,-39.723l-83.923,42.769l-13.246,-10.755c30.258,-15.419 60.532,-30.837 90.79,-46.256c-1.961,-5.083 -3.872,-10.592 -10.404,-35.238l-98.082,43.893l-11.828,-11.828l106.976,-47.876c-1.534,-13.88 -5.728,-53.736 -5.56,-71.67l-0,-0.017c-0.027,-1.894 -0.184,-2.827 -0.85,-3.504l266.182,266.182Zm-388.562,-185.436c1.272,1.164 3.414,1.356 5.594,0.397l0.75,-0.324c0.645,2.703 1.373,5.543 2.181,8.452l-8.525,-8.525Z" style="fill:#3b808b;"/>
|
||||
<g id="Icon">
|
||||
<path d="M234.668,135.985c-0.034,-2.49 -0.29,-3.326 -1.62,-4.094c-1.263,-0.835 -3.173,-0.852 -4.998,-0.051l-122.804,52.584c-1.825,0.802 -3.548,2.405 -4.469,4.145c-0.921,1.74 -1.023,3.582 -0.307,4.895l10.251,18.83l0.136,0.222c1.109,1.757 3.616,2.217 6.175,1.091l0.75,-0.324c3.207,13.441 8.46,30.258 15.47,42.248l106.976,-47.876c-1.535,-13.884 -5.731,-53.761 -5.56,-71.687l-0,0.017Z" style="fill:#fff;fill-rule:nonzero;"/>
|
||||
<path d="M243.162,223.466l-103.019,46.103c6.055,11.478 13.525,23.656 22.633,35.391c30.258,-15.419 60.532,-30.837 90.79,-46.256c-1.961,-5.083 -3.872,-10.592 -10.404,-35.238Z" style="fill:#fff;fill-rule:nonzero;"/>
|
||||
<path d="M398.56,333.307l-7.283,-11.12c-1.091,-1.211 -2.78,-1.672 -4.264,-1.075l-9.346,3.599c-1.109,-1.228 -2.712,-1.688 -4.281,-1.074c-3.054,1.194 -6.124,2.404 -9.177,3.598c-0.989,0.376 -1.262,1.109 -1.074,2.064l0.597,2.917c-17.227,1.859 -46.342,0.938 -72.421,-19.547c-11.53,-9.039 -22.343,-21.831 -31.366,-39.723l-86.85,44.26c15.879,17.449 35.818,32.919 60.584,42.231c34.453,12.98 78.356,14.208 133.703,-8.084l0.307,1.569c0.273,1.757 2.388,3.616 5.253,2.49l9.193,-3.582c3.156,-1.194 3.173,-3.616 2.712,-5.185l11.837,-4.622c1.331,-0.512 5.458,-3.701 1.91,-8.716l-0.034,0Z" style="fill:#fff;fill-rule:nonzero;"/>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="_Linear1" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(3.06162e-14,500,-500,3.06162e-14,267.59,0)"><stop offset="0" style="stop-color:#bdddeb;stop-opacity:1"/><stop offset="1" style="stop-color:#53a9d1;stop-opacity:1"/></linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.4 KiB |
23
docs/_static/flask-name.svg
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="100%" height="100%" viewBox="0 0 706 300" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||
<g>
|
||||
<path id="Name" d="M420.35,117.6l-37.65,-0c-4.4,-0 -7.475,0.85 -9.225,2.55c-1.75,1.7 -2.625,4.65 -2.625,8.85l-0,14.85l39.15,-0l-1.5,18.6l-37.65,-0l-0,43.35l-20.85,-0l-0,-85.05c-0,-7.9 1.725,-13.5 5.175,-16.8c3.45,-3.3 9.375,-4.95 17.775,-4.95l47.4,-0l0,18.6Z" style="fill-rule:nonzero;"/>
|
||||
<path d="M455.75,205.8l-19.5,0.15l-0,-112.95l19.5,-0l-0,112.8Z" style="fill-rule:nonzero;"/>
|
||||
<path d="M535.7,205.8l-13.05,-0l-6,-10.35l-20.85,11.25c-11.6,-0 -19.4,-4.2 -23.4,-12.6c-2,-4.1 -3.375,-8.525 -4.125,-13.275c-0.75,-4.75 -1.125,-9.7 -1.125,-14.85c-0,-5.15 0.05,-8.95 0.15,-11.4c0.1,-2.45 0.35,-5.3 0.75,-8.55c0.4,-3.25 0.975,-5.975 1.725,-8.175c0.75,-2.2 1.825,-4.475 3.225,-6.825c1.4,-2.35 3.1,-4.225 5.1,-5.625c4.5,-3.1 10.35,-4.65 17.55,-4.65l20.55,-0l19.5,-1.2l-0,86.25Zm-19.5,-27.3l-0,-40.2l-14.85,-0c-5.5,-0 -9.325,2.1 -11.475,6.3c-2.15,4.2 -3.225,10.475 -3.225,18.825c-0,8.35 1.025,14.225 3.075,17.625c2.05,3.4 5.925,5.1 11.625,5.1l14.85,-7.65Z" style="fill-rule:nonzero;"/>
|
||||
<path d="M615.65,182.1l0,2.25c-0.6,7.5 -3.775,13.15 -9.525,16.95c-5.75,3.8 -12.925,5.7 -21.525,5.7c-12.7,-0 -21.6,-2.3 -26.7,-6.9c-4.7,-4.2 -7.05,-10.4 -7.05,-18.6l0,-1.8l17.7,0c0,4.6 1.2,7.75 3.6,9.45c2.4,1.7 6.55,2.55 12.45,2.55c8,0 12,-2.9 12,-8.7c0,-4.8 -1.4,-8 -4.2,-9.6c-1.3,-0.8 -2.95,-1.4 -4.95,-1.8l-15.15,-2.55c-13.2,-2.1 -19.8,-10.35 -19.8,-24.75c0,-8 2.925,-14.225 8.775,-18.675c5.85,-4.45 13.275,-6.675 22.275,-6.675c20.5,0 30.75,8.85 30.75,26.55l0,1.95l-16.95,0c-0.2,-4.7 -1.45,-7.9 -3.75,-9.6c-2.3,-1.7 -5.525,-2.55 -9.675,-2.55c-4.15,0 -7.275,0.825 -9.375,2.475c-2.1,1.65 -3.15,3.475 -3.15,5.475c0,5.7 2.3,8.95 6.9,9.75l18.15,3.3c12.8,2.4 19.2,11 19.2,25.8Z" style="fill-rule:nonzero;"/>
|
||||
<path d="M705.65,205.8l-23.4,-0l-22.5,-30.3l-8.55,12.15l0,18.15l-19.5,-0l0,-112.8l19.5,-0l0,71.25l27.3,-40.65l22.05,-0l-28.05,38.4l33.15,43.8Z" style="fill-rule:nonzero;"/>
|
||||
<g id="Logo">
|
||||
<path id="Box" d="M300,30l0,240c0,16.557 -13.443,30 -30,30l-240,-0c-16.557,-0 -30,-13.443 -30,-30l0,-240c0,-16.557 13.443,-30 30,-30l240,0c16.557,0 30,13.443 30,30Z" style="fill:url(#_Linear1);"/>
|
||||
<path id="Shadow" d="M300,239.188l0,30.812c0,16.557 -13.443,30 -30,30l-56.467,-0l-102.271,-102.271c8.125,7.368 17.707,13.707 28.945,17.933c20.672,7.788 47.014,8.525 80.222,-4.85l0.184,0.941c0.164,1.054 1.433,2.17 3.152,1.494l5.516,-2.149c1.893,-0.716 1.904,-2.169 1.627,-3.111l7.103,-2.773c0.798,-0.307 3.274,-2.221 1.146,-5.23l-0.021,0l-4.37,-6.672c-0.655,-0.727 -1.668,-1.003 -2.558,-0.645l-5.608,2.16c-0.665,-0.737 -1.627,-1.013 -2.569,-0.645c-1.832,0.716 -3.674,1.443 -5.505,2.159c-0.594,0.225 -0.758,0.665 -0.645,1.239l0.358,1.749c-10.336,1.116 -27.805,0.563 -43.452,-11.727c-6.918,-5.424 -13.406,-13.099 -18.82,-23.835l-50.354,25.662l-7.947,-6.453c18.154,-9.251 36.319,-18.502 54.474,-27.754c-1.177,-3.049 -2.323,-6.355 -6.243,-21.143l-58.849,26.336l-7.097,-7.096l64.186,-28.726c-0.921,-8.328 -3.437,-32.242 -3.336,-43.002l-0,-0.01c-0.016,-1.137 -0.111,-1.697 -0.51,-2.103l159.709,159.71Zm-233.137,-111.262c0.763,0.699 2.048,0.814 3.356,0.238l0.45,-0.194c0.387,1.622 0.824,3.325 1.309,5.071l-5.115,-5.115Z" style="fill:#3b808b;"/>
|
||||
<g id="Icon">
|
||||
<path d="M140.801,81.591c-0.021,-1.494 -0.174,-1.996 -0.972,-2.456c-0.758,-0.502 -1.904,-0.512 -2.999,-0.031l-73.683,31.551c-1.095,0.481 -2.128,1.443 -2.681,2.486c-0.552,1.044 -0.614,2.149 -0.184,2.937l6.151,11.298l0.081,0.133c0.666,1.055 2.17,1.331 3.705,0.655l0.45,-0.194c1.924,8.064 5.076,18.155 9.282,25.349l64.186,-28.726c-0.921,-8.33 -3.439,-32.257 -3.336,-43.012l-0,0.01Z" style="fill:#fff;fill-rule:nonzero;"/>
|
||||
<path d="M145.897,134.079l-61.811,27.662c3.633,6.887 8.115,14.194 13.58,21.235c18.154,-9.251 36.319,-18.502 54.474,-27.754c-1.177,-3.049 -2.323,-6.355 -6.243,-21.143Z" style="fill:#fff;fill-rule:nonzero;"/>
|
||||
<path d="M239.136,199.984l-4.37,-6.672c-0.655,-0.727 -1.668,-1.003 -2.558,-0.645l-5.608,2.16c-0.665,-0.737 -1.627,-1.013 -2.569,-0.645c-1.832,0.716 -3.674,1.443 -5.505,2.159c-0.594,0.225 -0.758,0.665 -0.645,1.239l0.358,1.749c-10.336,1.116 -27.805,0.563 -43.452,-11.727c-6.918,-5.424 -13.406,-13.099 -18.82,-23.835l-52.11,26.557c9.528,10.469 21.491,19.751 36.35,25.338c20.672,7.788 47.014,8.525 80.222,-4.85l0.184,0.941c0.164,1.054 1.433,2.17 3.152,1.494l5.516,-2.149c1.893,-0.716 1.904,-2.169 1.627,-3.111l7.103,-2.773c0.798,-0.307 3.274,-2.221 1.146,-5.23l-0.021,0Z" style="fill:#fff;fill-rule:nonzero;"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="_Linear1" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(1.83697e-14,300,-300,1.83697e-14,160.554,0)"><stop offset="0" style="stop-color:#bdddeb;stop-opacity:1"/><stop offset="1" style="stop-color:#53a9d1;stop-opacity:1"/></linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.2 KiB |
BIN
docs/_static/no.png
vendored
|
Before Width: | Height: | Size: 259 B |
BIN
docs/_static/pycharm-run-config.png
vendored
Normal file
|
After Width: | Height: | Size: 97 KiB |
BIN
docs/_static/pycharm-runconfig.png
vendored
|
Before Width: | Height: | Size: 17 KiB |
BIN
docs/_static/yes.png
vendored
|
Before Width: | Height: | Size: 241 B |
|
|
@ -1,48 +0,0 @@
|
|||
.. _advanced_foreword:
|
||||
|
||||
Foreword for Experienced Programmers
|
||||
====================================
|
||||
|
||||
Thread-Locals in Flask
|
||||
----------------------
|
||||
|
||||
One of the design decisions in Flask was that simple tasks should be simple;
|
||||
they should not take a lot of code and yet they should not limit you. Because
|
||||
of that, Flask has a few design choices that some people might find
|
||||
surprising or unorthodox. For example, Flask uses thread-local objects
|
||||
internally so that you don’t have to pass objects around from
|
||||
function to function within a request in order to stay threadsafe.
|
||||
This approach is convenient, but requires a valid
|
||||
request context for dependency injection or when attempting to reuse code which
|
||||
uses a value pegged to the request. The Flask project is honest about
|
||||
thread-locals, does not hide them, and calls out in the code and documentation
|
||||
where they are used.
|
||||
|
||||
Develop for the Web with Caution
|
||||
--------------------------------
|
||||
|
||||
Always keep security in mind when building web applications.
|
||||
|
||||
If you write a web application, you are probably allowing users to register
|
||||
and leave their data on your server. The users are entrusting you with data.
|
||||
And even if you are the only user that might leave data in your application,
|
||||
you still want that data to be stored securely.
|
||||
|
||||
Unfortunately, there are many ways the security of a web application can be
|
||||
compromised. Flask protects you against one of the most common security
|
||||
problems of modern web applications: cross-site scripting (XSS). Unless you
|
||||
deliberately mark insecure HTML as secure, Flask and the underlying Jinja2
|
||||
template engine have you covered. But there are many more ways to cause
|
||||
security problems.
|
||||
|
||||
The documentation will warn you about aspects of web development that require
|
||||
attention to security. Some of these security concerns are far more complex
|
||||
than one might think, and we all sometimes underestimate the likelihood that a
|
||||
vulnerability will be exploited - until a clever attacker figures out a way to
|
||||
exploit our applications. And don't think that your application is not
|
||||
important enough to attract an attacker. Depending on the kind of attack,
|
||||
chances are that automated bots are probing for ways to fill your database with
|
||||
spam, links to malicious software, and the like.
|
||||
|
||||
Flask is no different from any other framework in that you the developer must
|
||||
build with caution, watching for exploits when building to your requirements.
|
||||
431
docs/api.rst
|
|
@ -1,11 +1,9 @@
|
|||
.. _api:
|
||||
|
||||
API
|
||||
===
|
||||
|
||||
.. module:: flask
|
||||
|
||||
This part of the documentation covers all the interfaces of Flask. For
|
||||
This part of the documentation covers all the interfaces of Flask. For
|
||||
parts where Flask depends on external libraries, we document the most
|
||||
important right here and provide links to the canonical documentation.
|
||||
|
||||
|
|
@ -29,76 +27,28 @@ Incoming Request Data
|
|||
---------------------
|
||||
|
||||
.. autoclass:: Request
|
||||
:members:
|
||||
:inherited-members:
|
||||
:members:
|
||||
:inherited-members:
|
||||
:exclude-members: json_module
|
||||
|
||||
.. attribute:: environ
|
||||
.. data:: request
|
||||
|
||||
The underlying WSGI environment.
|
||||
A proxy to the request data for the current request, an instance of
|
||||
:class:`.Request`.
|
||||
|
||||
.. attribute:: path
|
||||
.. attribute:: full_path
|
||||
.. attribute:: script_root
|
||||
.. attribute:: url
|
||||
.. attribute:: base_url
|
||||
.. attribute:: url_root
|
||||
This is only available when a :doc:`request context </appcontext>` is
|
||||
active.
|
||||
|
||||
Provides different ways to look at the current :rfc:`3987`.
|
||||
Imagine your application is listening on the following application
|
||||
root::
|
||||
|
||||
http://www.example.com/myapplication
|
||||
|
||||
And a user requests the following URI::
|
||||
|
||||
http://www.example.com/myapplication/%CF%80/page.html?x=y
|
||||
|
||||
In this case the values of the above mentioned attributes would be
|
||||
the following:
|
||||
|
||||
============= ======================================================
|
||||
`path` ``u'/π/page.html'``
|
||||
`full_path` ``u'/π/page.html?x=y'``
|
||||
`script_root` ``u'/myapplication'``
|
||||
`base_url` ``u'http://www.example.com/myapplication/π/page.html'``
|
||||
`url` ``u'http://www.example.com/myapplication/π/page.html?x=y'``
|
||||
`url_root` ``u'http://www.example.com/myapplication/'``
|
||||
============= ======================================================
|
||||
|
||||
|
||||
.. attribute:: request
|
||||
|
||||
To access incoming request data, you can use the global `request`
|
||||
object. Flask parses incoming request data for you and gives you
|
||||
access to it through that global object. Internally Flask makes
|
||||
sure that you always get the correct data for the active thread if you
|
||||
are in a multithreaded environment.
|
||||
|
||||
This is a proxy. See :ref:`notes-on-proxies` for more information.
|
||||
|
||||
The request object is an instance of a :class:`~werkzeug.wrappers.Request`
|
||||
subclass and provides all of the attributes Werkzeug defines. This
|
||||
just shows a quick overview of the most important ones.
|
||||
This is a proxy. See :ref:`context-visibility` for more information.
|
||||
|
||||
|
||||
Response Objects
|
||||
----------------
|
||||
|
||||
.. autoclass:: flask.Response
|
||||
:members: set_cookie, max_cookie_size, data, mimetype, is_json, get_json
|
||||
|
||||
.. attribute:: headers
|
||||
|
||||
A :class:`~werkzeug.datastructures.Headers` object representing the response headers.
|
||||
|
||||
.. attribute:: status
|
||||
|
||||
A string with a response status.
|
||||
|
||||
.. attribute:: status_code
|
||||
|
||||
The response status as integer.
|
||||
|
||||
:members:
|
||||
:inherited-members:
|
||||
:exclude-members: json_module
|
||||
|
||||
Sessions
|
||||
--------
|
||||
|
|
@ -110,40 +60,33 @@ does this is by using a signed cookie. The user can look at the session
|
|||
contents, but can't modify it unless they know the secret key, so make sure to
|
||||
set that to something complex and unguessable.
|
||||
|
||||
To access the current session you can use the :class:`session` object:
|
||||
To access the current session you can use the :data:`.session` proxy.
|
||||
|
||||
.. class:: session
|
||||
.. data:: session
|
||||
|
||||
The session object works pretty much like an ordinary dict, with the
|
||||
difference that it keeps track of modifications.
|
||||
A proxy to the session data for the current request, an instance of
|
||||
:class:`.SessionMixin`.
|
||||
|
||||
This is a proxy. See :ref:`notes-on-proxies` for more information.
|
||||
This is only available when a :doc:`request context </appcontext>` is
|
||||
active.
|
||||
|
||||
The following attributes are interesting:
|
||||
This is a proxy. See :ref:`context-visibility` for more information.
|
||||
|
||||
.. attribute:: new
|
||||
The session object works like a dict but tracks assignment and access to its
|
||||
keys. It cannot track modifications to mutable values, you need to set
|
||||
:attr:`~.SessionMixin.modified` manually when modifying a list, dict, etc.
|
||||
|
||||
``True`` if the session is new, ``False`` otherwise.
|
||||
.. code-block:: python
|
||||
|
||||
.. attribute:: modified
|
||||
|
||||
``True`` if the session object detected a modification. Be advised
|
||||
that modifications on mutable structures are not picked up
|
||||
automatically, in that situation you have to explicitly set the
|
||||
attribute to ``True`` yourself. Here an example::
|
||||
|
||||
# this change is not picked up because a mutable object (here
|
||||
# a list) is changed.
|
||||
session['objects'].append(42)
|
||||
# appending to a list is not detected
|
||||
session["numbers"].append(42)
|
||||
# so mark it as modified yourself
|
||||
session.modified = True
|
||||
|
||||
.. attribute:: permanent
|
||||
|
||||
If set to ``True`` the session lives for
|
||||
:attr:`~flask.Flask.permanent_session_lifetime` seconds. The
|
||||
default is 31 days. If set to ``False`` (which is the default) the
|
||||
session will be deleted when the user closes the browser.
|
||||
The session is persisted across requests using a cookie. By default the
|
||||
users's browser will clear the cookie when it is closed. Set
|
||||
:attr:`~.SessionMixin.permanent` to ``True`` to persist the cookie for
|
||||
:data:`PERMANENT_SESSION_LIFETIME`.
|
||||
|
||||
|
||||
Session Interface
|
||||
|
|
@ -173,10 +116,9 @@ implementation that Flask is using.
|
|||
|
||||
.. admonition:: Notice
|
||||
|
||||
The ``PERMANENT_SESSION_LIFETIME`` config key can also be an integer
|
||||
starting with Flask 0.8. Either catch this down yourself or use
|
||||
the :attr:`~flask.Flask.permanent_session_lifetime` attribute on the
|
||||
app which converts the result to an integer automatically.
|
||||
The :data:`PERMANENT_SESSION_LIFETIME` config can be an integer or ``timedelta``.
|
||||
The :attr:`~flask.Flask.permanent_session_lifetime` attribute is always a
|
||||
``timedelta``.
|
||||
|
||||
|
||||
Test Client
|
||||
|
|
@ -204,23 +146,24 @@ Application Globals
|
|||
|
||||
To share data that is valid for one request only from one function to
|
||||
another, a global variable is not good enough because it would break in
|
||||
threaded environments. Flask provides you with a special object that
|
||||
threaded environments. Flask provides you with a special object that
|
||||
ensures it is only valid for the active request and that will return
|
||||
different values for each request. In a nutshell: it does the right
|
||||
thing, like it does for :class:`request` and :class:`session`.
|
||||
different values for each request. In a nutshell: it does the right
|
||||
thing, like it does for :data:`.request` and :data:`.session`.
|
||||
|
||||
.. data:: g
|
||||
|
||||
A namespace object that can store data during an
|
||||
:doc:`application context </appcontext>`. This is an instance of
|
||||
:attr:`Flask.app_ctx_globals_class`, which defaults to
|
||||
:class:`ctx._AppCtxGlobals`.
|
||||
A proxy to a namespace object used to store data during a single request or
|
||||
app context. An instance of :attr:`.Flask.app_ctx_globals_class`, which
|
||||
defaults to :class:`._AppCtxGlobals`.
|
||||
|
||||
This is a good place to store resources during a request. During
|
||||
testing, you can use the :ref:`faking-resources` pattern to
|
||||
pre-configure such resources.
|
||||
This is a good place to store resources during a request. For example, a
|
||||
:meth:`~.Flask.before_request` function could load a user object from a
|
||||
session id, then set ``g.user`` to be used in the view function.
|
||||
|
||||
This is a proxy. See :ref:`notes-on-proxies` for more information.
|
||||
This is only available when an :doc:`app context </appcontext>` is active.
|
||||
|
||||
This is a proxy. See :ref:`context-visibility` for more information.
|
||||
|
||||
.. versionchanged:: 0.10
|
||||
Bound to the application context instead of the request context.
|
||||
|
|
@ -234,17 +177,16 @@ Useful Functions and Classes
|
|||
|
||||
.. data:: current_app
|
||||
|
||||
A proxy to the application handling the current request. This is
|
||||
useful to access the application without needing to import it, or if
|
||||
it can't be imported, such as when using the application factory
|
||||
pattern or in blueprints and extensions.
|
||||
A proxy to the :class:`.Flask` application handling the current request or
|
||||
other activity.
|
||||
|
||||
This is only available when an
|
||||
:doc:`application context </appcontext>` is pushed. This happens
|
||||
automatically during requests and CLI commands. It can be controlled
|
||||
manually with :meth:`~flask.Flask.app_context`.
|
||||
This is useful to access the application without needing to import it, or if
|
||||
it can't be imported, such as when using the application factory pattern or
|
||||
in blueprints and extensions.
|
||||
|
||||
This is a proxy. See :ref:`notes-on-proxies` for more information.
|
||||
This is only available when an :doc:`app context </appcontext>` is active.
|
||||
|
||||
This is a proxy. See :ref:`context-visibility` for more information.
|
||||
|
||||
.. autofunction:: has_request_context
|
||||
|
||||
|
|
@ -266,12 +208,6 @@ Useful Functions and Classes
|
|||
|
||||
.. autofunction:: send_from_directory
|
||||
|
||||
.. autofunction:: safe_join
|
||||
|
||||
.. autofunction:: escape
|
||||
|
||||
.. autoclass:: Markup
|
||||
:members: escape, unescape, striptags
|
||||
|
||||
Message Flashing
|
||||
----------------
|
||||
|
|
@ -280,58 +216,29 @@ Message Flashing
|
|||
|
||||
.. autofunction:: get_flashed_messages
|
||||
|
||||
|
||||
JSON Support
|
||||
------------
|
||||
|
||||
.. module:: flask.json
|
||||
|
||||
Flask uses ``simplejson`` for the JSON implementation. Since simplejson
|
||||
is provided by both the standard library as well as extension, Flask will
|
||||
try simplejson first and then fall back to the stdlib json module. On top
|
||||
of that it will delegate access to the current application's JSON encoders
|
||||
and decoders for easier customization.
|
||||
Flask uses Python's built-in :mod:`json` module for handling JSON by
|
||||
default. The JSON implementation can be changed by assigning a different
|
||||
provider to :attr:`flask.Flask.json_provider_class` or
|
||||
:attr:`flask.Flask.json`. The functions provided by ``flask.json`` will
|
||||
use methods on ``app.json`` if an app context is active.
|
||||
|
||||
So for starters instead of doing::
|
||||
|
||||
try:
|
||||
import simplejson as json
|
||||
except ImportError:
|
||||
import json
|
||||
|
||||
You can instead just do this::
|
||||
|
||||
from flask import json
|
||||
|
||||
For usage examples, read the :mod:`json` documentation in the standard
|
||||
library. The following extensions are by default applied to the stdlib's
|
||||
JSON module:
|
||||
|
||||
1. ``datetime`` objects are serialized as :rfc:`822` strings.
|
||||
2. Any object with an ``__html__`` method (like :class:`~flask.Markup`)
|
||||
will have that method called and then the return value is serialized
|
||||
as string.
|
||||
|
||||
The :func:`~htmlsafe_dumps` function of this json module is also available
|
||||
as a filter called ``|tojson`` in Jinja2. Note that in versions of Flask prior
|
||||
to Flask 0.10, you must disable escaping with ``|safe`` if you intend to use
|
||||
``|tojson`` output inside ``script`` tags. In Flask 0.10 and above, this
|
||||
happens automatically (but it's harmless to include ``|safe`` anyway).
|
||||
Jinja's ``|tojson`` filter is configured to use the app's JSON provider.
|
||||
The filter marks the output with ``|safe``. Use it to render data inside
|
||||
HTML ``<script>`` tags.
|
||||
|
||||
.. sourcecode:: html+jinja
|
||||
|
||||
<script type=text/javascript>
|
||||
doSomethingWith({{ user.username|tojson|safe }});
|
||||
<script>
|
||||
const names = {{ names|tojson }};
|
||||
renderChart(names, {{ axis_data|tojson }});
|
||||
</script>
|
||||
|
||||
.. admonition:: Auto-Sort JSON Keys
|
||||
|
||||
The configuration variable ``JSON_SORT_KEYS`` (:ref:`config`) can be
|
||||
set to false to stop Flask from auto-sorting keys. By default sorting
|
||||
is enabled and outside of the app context sorting is turned on.
|
||||
|
||||
Notice that disabling key sorting can cause issues when using content
|
||||
based HTTP caches and Python's hash randomization feature.
|
||||
|
||||
.. autofunction:: jsonify
|
||||
|
||||
.. autofunction:: dumps
|
||||
|
|
@ -342,14 +249,17 @@ happens automatically (but it's harmless to include ``|safe`` anyway).
|
|||
|
||||
.. autofunction:: load
|
||||
|
||||
.. autoclass:: JSONEncoder
|
||||
:members:
|
||||
.. autoclass:: flask.json.provider.JSONProvider
|
||||
:members:
|
||||
:member-order: bysource
|
||||
|
||||
.. autoclass:: JSONDecoder
|
||||
:members:
|
||||
.. autoclass:: flask.json.provider.DefaultJSONProvider
|
||||
:members:
|
||||
:member-order: bysource
|
||||
|
||||
.. automodule:: flask.json.tag
|
||||
|
||||
|
||||
Template Rendering
|
||||
------------------
|
||||
|
||||
|
|
@ -359,6 +269,10 @@ Template Rendering
|
|||
|
||||
.. autofunction:: render_template_string
|
||||
|
||||
.. autofunction:: stream_template
|
||||
|
||||
.. autofunction:: stream_template_string
|
||||
|
||||
.. autofunction:: get_template_attribute
|
||||
|
||||
Configuration
|
||||
|
|
@ -376,59 +290,31 @@ Stream Helpers
|
|||
Useful Internals
|
||||
----------------
|
||||
|
||||
.. autoclass:: flask.ctx.RequestContext
|
||||
:members:
|
||||
|
||||
.. data:: _request_ctx_stack
|
||||
|
||||
The internal :class:`~werkzeug.local.LocalStack` that holds
|
||||
:class:`~flask.ctx.RequestContext` instances. Typically, the
|
||||
:data:`request` and :data:`session` proxies should be accessed
|
||||
instead of the stack. It may be useful to access the stack in
|
||||
extension code.
|
||||
|
||||
The following attributes are always present on each layer of the
|
||||
stack:
|
||||
|
||||
`app`
|
||||
the active Flask application.
|
||||
|
||||
`url_adapter`
|
||||
the URL adapter that was used to match the request.
|
||||
|
||||
`request`
|
||||
the current request object.
|
||||
|
||||
`session`
|
||||
the active session object.
|
||||
|
||||
`g`
|
||||
an object with all the attributes of the :data:`flask.g` object.
|
||||
|
||||
`flashes`
|
||||
an internal cache for the flashed messages.
|
||||
|
||||
Example usage::
|
||||
|
||||
from flask import _request_ctx_stack
|
||||
|
||||
def get_session():
|
||||
ctx = _request_ctx_stack.top
|
||||
if ctx is not None:
|
||||
return ctx.session
|
||||
|
||||
.. autoclass:: flask.ctx.AppContext
|
||||
:members:
|
||||
|
||||
.. data:: _app_ctx_stack
|
||||
.. data:: flask.globals.app_ctx
|
||||
|
||||
The internal :class:`~werkzeug.local.LocalStack` that holds
|
||||
:class:`~flask.ctx.AppContext` instances. Typically, the
|
||||
:data:`current_app` and :data:`g` proxies should be accessed instead
|
||||
of the stack. Extensions can access the contexts on the stack as a
|
||||
namespace to store data.
|
||||
A proxy to the active :class:`.AppContext`.
|
||||
|
||||
.. versionadded:: 0.9
|
||||
This is an internal object that is essential to how Flask handles requests.
|
||||
Accessing this should not be needed in most cases. Most likely you want
|
||||
:data:`.current_app`, :data:`.g`, :data:`.request`, and :data:`.session` instead.
|
||||
|
||||
This is only available when a :doc:`request context </appcontext>` is
|
||||
active.
|
||||
|
||||
This is a proxy. See :ref:`context-visibility` for more information.
|
||||
|
||||
.. class:: flask.ctx.RequestContext
|
||||
|
||||
.. deprecated:: 3.2
|
||||
Merged with :class:`AppContext`. This alias will be removed in Flask 4.0.
|
||||
|
||||
.. data:: flask.globals.request_ctx
|
||||
|
||||
.. deprecated:: 3.2
|
||||
Merged with :data:`.app_ctx`. This alias will be removed in Flask 4.0.
|
||||
|
||||
.. autoclass:: flask.blueprints.BlueprintSetupState
|
||||
:members:
|
||||
|
|
@ -438,18 +324,13 @@ Useful Internals
|
|||
Signals
|
||||
-------
|
||||
|
||||
.. versionadded:: 0.6
|
||||
Signals are provided by the `Blinker`_ library. See :doc:`signals` for an introduction.
|
||||
|
||||
.. data:: signals.signals_available
|
||||
|
||||
``True`` if the signaling system is available. This is the case
|
||||
when `blinker`_ is installed.
|
||||
|
||||
The following signals exist in Flask:
|
||||
.. _blinker: https://blinker.readthedocs.io/
|
||||
|
||||
.. data:: template_rendered
|
||||
|
||||
This signal is sent when a template was successfully rendered. The
|
||||
This signal is sent when a template was successfully rendered. The
|
||||
signal is invoked with the instance of the template as `template`
|
||||
and the context as dictionary (named `context`).
|
||||
|
||||
|
|
@ -483,7 +364,7 @@ The following signals exist in Flask:
|
|||
.. data:: request_started
|
||||
|
||||
This signal is sent when the request context is set up, before
|
||||
any request processing happens. Because the request context is already
|
||||
any request processing happens. Because the request context is already
|
||||
bound, the subscriber can access the request with the standard global
|
||||
proxies such as :class:`~flask.request`.
|
||||
|
||||
|
|
@ -503,7 +384,7 @@ The following signals exist in Flask:
|
|||
Example subscriber::
|
||||
|
||||
def log_response(sender, response, **extra):
|
||||
sender.logger.debug('Request context is about to close down. '
|
||||
sender.logger.debug('Request context is about to close down. '
|
||||
'Response: %s', response)
|
||||
|
||||
from flask import request_finished
|
||||
|
|
@ -511,23 +392,37 @@ The following signals exist in Flask:
|
|||
|
||||
.. data:: got_request_exception
|
||||
|
||||
This signal is sent when an exception happens during request processing.
|
||||
It is sent *before* the standard exception handling kicks in and even
|
||||
in debug mode, where no exception handling happens. The exception
|
||||
itself is passed to the subscriber as `exception`.
|
||||
This signal is sent when an unhandled exception happens during
|
||||
request processing, including when debugging. The exception is
|
||||
passed to the subscriber as ``exception``.
|
||||
|
||||
Example subscriber::
|
||||
This signal is not sent for
|
||||
:exc:`~werkzeug.exceptions.HTTPException`, or other exceptions that
|
||||
have error handlers registered, unless the exception was raised from
|
||||
an error handler.
|
||||
|
||||
def log_exception(sender, exception, **extra):
|
||||
sender.logger.debug('Got exception during processing: %s', exception)
|
||||
This example shows how to do some extra logging if a theoretical
|
||||
``SecurityException`` was raised:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from flask import got_request_exception
|
||||
got_request_exception.connect(log_exception, app)
|
||||
|
||||
def log_security_exception(sender, exception, **extra):
|
||||
if not isinstance(exception, SecurityException):
|
||||
return
|
||||
|
||||
security_logger.exception(
|
||||
f"SecurityException at {request.url!r}",
|
||||
exc_info=exception,
|
||||
)
|
||||
|
||||
got_request_exception.connect(log_security_exception, app)
|
||||
|
||||
.. data:: request_tearing_down
|
||||
|
||||
This signal is sent when the request is tearing down. This is always
|
||||
called, even if an exception is caused. Currently functions listening
|
||||
This signal is sent when the request is tearing down. This is always
|
||||
called, even if an exception is caused. Currently functions listening
|
||||
to this signal are called after the regular teardown handlers, but this
|
||||
is not something you can rely on.
|
||||
|
||||
|
|
@ -545,8 +440,8 @@ The following signals exist in Flask:
|
|||
|
||||
.. data:: appcontext_tearing_down
|
||||
|
||||
This signal is sent when the app context is tearing down. This is always
|
||||
called, even if an exception is caused. Currently functions listening
|
||||
This signal is sent when the app context is tearing down. This is always
|
||||
called, even if an exception is caused. Currently functions listening
|
||||
to this signal are called after the regular teardown handlers, but this
|
||||
is not something you can rely on.
|
||||
|
||||
|
|
@ -563,9 +458,9 @@ The following signals exist in Flask:
|
|||
|
||||
.. data:: appcontext_pushed
|
||||
|
||||
This signal is sent when an application context is pushed. The sender
|
||||
is the application. This is usually useful for unittests in order to
|
||||
temporarily hook in information. For instance it can be used to
|
||||
This signal is sent when an application context is pushed. The sender
|
||||
is the application. This is usually useful for unittests in order to
|
||||
temporarily hook in information. For instance it can be used to
|
||||
set a resource early onto the `g` object.
|
||||
|
||||
Example usage::
|
||||
|
|
@ -592,16 +487,15 @@ The following signals exist in Flask:
|
|||
|
||||
.. data:: appcontext_popped
|
||||
|
||||
This signal is sent when an application context is popped. The sender
|
||||
is the application. This usually falls in line with the
|
||||
This signal is sent when an application context is popped. The sender
|
||||
is the application. This usually falls in line with the
|
||||
:data:`appcontext_tearing_down` signal.
|
||||
|
||||
.. versionadded:: 0.10
|
||||
|
||||
|
||||
.. data:: message_flashed
|
||||
|
||||
This signal is sent when the application is flashing a message. The
|
||||
This signal is sent when the application is flashing a message. The
|
||||
messages is sent as `message` keyword argument and the category as
|
||||
`category`.
|
||||
|
||||
|
|
@ -616,24 +510,6 @@ The following signals exist in Flask:
|
|||
|
||||
.. versionadded:: 0.10
|
||||
|
||||
.. class:: signals.Namespace
|
||||
|
||||
An alias for :class:`blinker.base.Namespace` if blinker is available,
|
||||
otherwise a dummy class that creates fake signals. This class is
|
||||
available for Flask extensions that want to provide the same fallback
|
||||
system as Flask itself.
|
||||
|
||||
.. method:: signal(name, doc=None)
|
||||
|
||||
Creates a new signal for this namespace if blinker is available,
|
||||
otherwise returns a fake signal that has a send method that will
|
||||
do nothing but will fail with a :exc:`RuntimeError` for all other
|
||||
operations, including connecting.
|
||||
|
||||
|
||||
.. _blinker: https://pypi.org/project/blinker/
|
||||
|
||||
.. _class-based-views:
|
||||
|
||||
Class-Based Views
|
||||
-----------------
|
||||
|
|
@ -661,7 +537,7 @@ Generally there are three ways to define rules for the routing system:
|
|||
which is exposed as :attr:`flask.Flask.url_map`.
|
||||
|
||||
Variable parts in the route can be specified with angular brackets
|
||||
(``/user/<username>``). By default a variable part in the URL accepts any
|
||||
(``/user/<username>``). By default a variable part in the URL accepts any
|
||||
string without a slash however a different converter can be specified as
|
||||
well by using ``<converter:name>``.
|
||||
|
||||
|
|
@ -695,7 +571,7 @@ Here are some examples::
|
|||
pass
|
||||
|
||||
An important detail to keep in mind is how Flask deals with trailing
|
||||
slashes. The idea is to keep each URL unique so the following rules
|
||||
slashes. The idea is to keep each URL unique so the following rules
|
||||
apply:
|
||||
|
||||
1. If a rule ends with a slash and is requested without a slash by the
|
||||
|
|
@ -704,11 +580,11 @@ apply:
|
|||
2. If a rule does not end with a trailing slash and the user requests the
|
||||
page with a trailing slash, a 404 not found is raised.
|
||||
|
||||
This is consistent with how web servers deal with static files. This
|
||||
This is consistent with how web servers deal with static files. This
|
||||
also makes it possible to use relative link targets safely.
|
||||
|
||||
You can also define multiple rules for the same function. They have to be
|
||||
unique however. Defaults can also be specified. Here for example is a
|
||||
You can also define multiple rules for the same function. They have to be
|
||||
unique however. Defaults can also be specified. Here for example is a
|
||||
definition for a URL that accepts an optional page::
|
||||
|
||||
@app.route('/users/', defaults={'page': 1})
|
||||
|
|
@ -720,7 +596,7 @@ This specifies that ``/users/`` will be the URL for page one and
|
|||
``/users/page/N`` will be the URL for page ``N``.
|
||||
|
||||
If a URL contains a default value, it will be redirected to its simpler
|
||||
form with a 301 redirect. In the above example, ``/users/page/1`` will
|
||||
form with a 308 redirect. In the above example, ``/users/page/1`` will
|
||||
be redirected to ``/users/``. If your route handles ``GET`` and ``POST``
|
||||
requests, make sure the default route only handles ``GET``, as redirects
|
||||
can't preserve form data. ::
|
||||
|
|
@ -731,36 +607,35 @@ can't preserve form data. ::
|
|||
pass
|
||||
|
||||
Here are the parameters that :meth:`~flask.Flask.route` and
|
||||
:meth:`~flask.Flask.add_url_rule` accept. The only difference is that
|
||||
:meth:`~flask.Flask.add_url_rule` accept. The only difference is that
|
||||
with the route parameter the view function is defined with the decorator
|
||||
instead of the `view_func` parameter.
|
||||
|
||||
=============== ==========================================================
|
||||
`rule` the URL rule as string
|
||||
`endpoint` the endpoint for the registered URL rule. Flask itself
|
||||
`endpoint` the endpoint for the registered URL rule. Flask itself
|
||||
assumes that the name of the view function is the name
|
||||
of the endpoint if not explicitly stated.
|
||||
`view_func` the function to call when serving a request to the
|
||||
provided endpoint. If this is not provided one can
|
||||
provided endpoint. If this is not provided one can
|
||||
specify the function later by storing it in the
|
||||
:attr:`~flask.Flask.view_functions` dictionary with the
|
||||
endpoint as key.
|
||||
`defaults` A dictionary with defaults for this rule. See the
|
||||
`defaults` A dictionary with defaults for this rule. See the
|
||||
example above for how defaults work.
|
||||
`subdomain` specifies the rule for the subdomain in case subdomain
|
||||
matching is in use. If not specified the default
|
||||
matching is in use. If not specified the default
|
||||
subdomain is assumed.
|
||||
`**options` the options to be forwarded to the underlying
|
||||
:class:`~werkzeug.routing.Rule` object. A change to
|
||||
Werkzeug is handling of method options. methods is a list
|
||||
:class:`~werkzeug.routing.Rule` object. A change to
|
||||
Werkzeug is handling of method options. methods is a list
|
||||
of methods this rule should be limited to (``GET``, ``POST``
|
||||
etc.). By default a rule just listens for ``GET`` (and
|
||||
implicitly ``HEAD``). Starting with Flask 0.6, ``OPTIONS`` is
|
||||
etc.). By default a rule just listens for ``GET`` (and
|
||||
implicitly ``HEAD``). Starting with Flask 0.6, ``OPTIONS`` is
|
||||
implicitly added and handled by the standard request
|
||||
handling. They have to be specified as keyword arguments.
|
||||
handling. They have to be specified as keyword arguments.
|
||||
=============== ==========================================================
|
||||
|
||||
.. _view-func-options:
|
||||
|
||||
View Function Options
|
||||
---------------------
|
||||
|
|
@ -770,19 +645,19 @@ customize behavior the view function would normally not have control over.
|
|||
The following attributes can be provided optionally to either override
|
||||
some defaults to :meth:`~flask.Flask.add_url_rule` or general behavior:
|
||||
|
||||
- `__name__`: The name of a function is by default used as endpoint. If
|
||||
endpoint is provided explicitly this value is used. Additionally this
|
||||
- `__name__`: The name of a function is by default used as endpoint. If
|
||||
endpoint is provided explicitly this value is used. Additionally this
|
||||
will be prefixed with the name of the blueprint by default which
|
||||
cannot be customized from the function itself.
|
||||
|
||||
- `methods`: If methods are not provided when the URL rule is added,
|
||||
Flask will look on the view function object itself if a `methods`
|
||||
attribute exists. If it does, it will pull the information for the
|
||||
attribute exists. If it does, it will pull the information for the
|
||||
methods from there.
|
||||
|
||||
- `provide_automatic_options`: if this attribute is set Flask will
|
||||
either force enable or disable the automatic implementation of the
|
||||
HTTP ``OPTIONS`` response. This can be useful when working with
|
||||
HTTP ``OPTIONS`` response. This can be useful when working with
|
||||
decorators that want to customize the ``OPTIONS`` response on a per-view
|
||||
basis.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,76 +1,63 @@
|
|||
.. currentmodule:: flask
|
||||
The App and Request Context
|
||||
===========================
|
||||
|
||||
.. _app-context:
|
||||
The context keeps track of data and objects during a request, CLI command, or
|
||||
other activity. Rather than passing this data around to every function, the
|
||||
:data:`.current_app`, :data:`.g`, :data:`.request`, and :data:`.session` proxies
|
||||
are accessed instead.
|
||||
|
||||
The Application Context
|
||||
=======================
|
||||
When handling a request, the context is referred to as the "request context"
|
||||
because it contains request data in addition to application data. Otherwise,
|
||||
such as during a CLI command, it is referred to as the "app context". During an
|
||||
app context, :data:`.current_app` and :data:`.g` are available, while during a
|
||||
request context :data:`.request` and :data:`.session` are also available.
|
||||
|
||||
The application context keeps track of the application-level data during
|
||||
a request, CLI command, or other activity. Rather than passing the
|
||||
application around to each function, the :data:`current_app` and
|
||||
:data:`g` proxies are accessed instead.
|
||||
|
||||
This is similar to the :doc:`/reqcontext`, which keeps track of
|
||||
request-level data during a request. A corresponding application context
|
||||
is pushed when a request context is pushed.
|
||||
|
||||
Purpose of the Context
|
||||
----------------------
|
||||
|
||||
The :class:`Flask` application object has attributes, such as
|
||||
:attr:`~Flask.config`, that are useful to access within views and
|
||||
:doc:`CLI commands </cli>`. However, importing the ``app`` instance
|
||||
within the modules in your project is prone to circular import issues.
|
||||
When using the :doc:`app factory pattern </patterns/appfactories>` or
|
||||
writing reusable :doc:`blueprints </blueprints>` or
|
||||
:doc:`extensions </extensions>` there won't be an ``app`` instance to
|
||||
import at all.
|
||||
The context and proxies help solve two development issues: circular imports, and
|
||||
passing around global data during a request.
|
||||
|
||||
Flask solves this issue with the *application context*. Rather than
|
||||
referring to an ``app`` directly, you use the :data:`current_app`
|
||||
proxy, which points to the application handling the current activity.
|
||||
The :class:`.Flask` application object has attributes, such as
|
||||
:attr:`~.Flask.config`, that are useful to access within views and other
|
||||
functions. However, importing the ``app`` instance within the modules in your
|
||||
project is prone to circular import issues. When using the
|
||||
:doc:`app factory pattern </patterns/appfactories>` or writing reusable
|
||||
:doc:`blueprints </blueprints>` or :doc:`extensions </extensions>` there won't
|
||||
be an ``app`` instance to import at all.
|
||||
|
||||
Flask automatically *pushes* an application context when handling a
|
||||
request. View functions, error handlers, and other functions that run
|
||||
during a request will have access to :data:`current_app`.
|
||||
When the application handles a request, it creates a :class:`.Request` object.
|
||||
Because a *worker* handles only one request at a time, the request data can be
|
||||
considered global to that worker during that request. Passing it as an argument
|
||||
through every function during the request becomes verbose and redundant.
|
||||
|
||||
Flask will also automatically push an app context when running CLI
|
||||
commands registered with :attr:`Flask.cli` using ``@app.cli.command()``.
|
||||
Flask solves these issues with the *active context* pattern. Rather than
|
||||
importing an ``app`` directly, or having to pass it and the request through to
|
||||
every single function, you import and access the proxies, which point to the
|
||||
currently active application and request data. This is sometimes referred to
|
||||
as "context local" data.
|
||||
|
||||
|
||||
Lifetime of the Context
|
||||
-----------------------
|
||||
Context During Setup
|
||||
--------------------
|
||||
|
||||
The application context is created and destroyed as necessary. When a
|
||||
Flask application begins handling a request, it pushes an application
|
||||
context and a :doc:`request context </reqcontext>`. When the request
|
||||
ends it pops the request context then the application context.
|
||||
Typically, an application context will have the same lifetime as a
|
||||
request.
|
||||
|
||||
See :doc:`/reqcontext` for more information about how the contexts work
|
||||
and the full life cycle of a request.
|
||||
|
||||
|
||||
Manually Push a Context
|
||||
-----------------------
|
||||
|
||||
If you try to access :data:`current_app`, or anything that uses it,
|
||||
outside an application context, you'll get this error message:
|
||||
If you try to access :data:`.current_app`, :data:`.g`, or anything that uses it,
|
||||
outside an app context, you'll get this error message:
|
||||
|
||||
.. code-block:: pytb
|
||||
|
||||
RuntimeError: Working outside of application context.
|
||||
|
||||
This typically means that you attempted to use functionality that
|
||||
needed to interface with the current application object in some way.
|
||||
To solve this, set up an application context with app.app_context().
|
||||
Attempted to use functionality that expected a current application to be
|
||||
set. To solve this, set up an app context using 'with app.app_context()'.
|
||||
See the documentation on app context for more information.
|
||||
|
||||
If you see that error while configuring your application, such as when
|
||||
initializing an extension, you can push a context manually since you
|
||||
have direct access to the ``app``. Use :meth:`~Flask.app_context` in a
|
||||
``with`` block, and everything that runs in the block will have access
|
||||
to :data:`current_app`. ::
|
||||
initializing an extension, you can push a context manually since you have direct
|
||||
access to the ``app``. Use :meth:`.Flask.app_context` in a ``with`` block.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def create_app():
|
||||
app = Flask(__name__)
|
||||
|
|
@ -80,80 +67,121 @@ to :data:`current_app`. ::
|
|||
|
||||
return app
|
||||
|
||||
If you see that error somewhere else in your code not related to
|
||||
configuring the application, it most likely indicates that you should
|
||||
move that code into a view function or CLI command.
|
||||
If you see that error somewhere else in your code not related to setting up the
|
||||
application, it most likely indicates that you should move that code into a view
|
||||
function or CLI command.
|
||||
|
||||
|
||||
Storing Data
|
||||
------------
|
||||
Context During Testing
|
||||
----------------------
|
||||
|
||||
The application context is a good place to store common data during a
|
||||
request or CLI command. Flask provides the :data:`g object <g>` for this
|
||||
purpose. It is a simple namespace object that has the same lifetime as
|
||||
an application context.
|
||||
See :doc:`/testing` for detailed information about managing the context during
|
||||
tests.
|
||||
|
||||
.. note::
|
||||
The ``g`` name stands for "global", but that is referring to the
|
||||
data being global *within a context*. The data on ``g`` is lost
|
||||
after the context ends, and it is not an appropriate place to store
|
||||
data between requests. Use the :data:`session` or a database to
|
||||
store data across requests.
|
||||
If you try to access :data:`.request`, :data:`.session`, or anything that uses
|
||||
it, outside a request context, you'll get this error message:
|
||||
|
||||
A common use for :data:`g` is to manage resources during a request.
|
||||
.. code-block:: pytb
|
||||
|
||||
1. ``get_X()`` creates resource ``X`` if it does not exist, caching it
|
||||
as ``g.X``.
|
||||
2. ``teardown_X()`` closes or otherwise deallocates the resource if it
|
||||
exists. It is registered as a :meth:`~Flask.teardown_appcontext`
|
||||
handler.
|
||||
RuntimeError: Working outside of request context.
|
||||
|
||||
For example, you can manage a database connection using this pattern::
|
||||
Attempted to use functionality that expected an active HTTP request. See the
|
||||
documentation on request context for more information.
|
||||
|
||||
from flask import g
|
||||
This will probably only happen during tests. If you see that error somewhere
|
||||
else in your code not related to testing, it most likely indicates that you
|
||||
should move that code into a view function.
|
||||
|
||||
def get_db():
|
||||
if 'db' not in g:
|
||||
g.db = connect_to_database()
|
||||
The primary way to solve this is to use :meth:`.Flask.test_client` to simulate
|
||||
a full request.
|
||||
|
||||
return g.db
|
||||
If you only want to unit test one function, rather than a full request, use
|
||||
:meth:`.Flask.test_request_context` in a ``with`` block.
|
||||
|
||||
@app.teardown_appcontext
|
||||
def teardown_db(exception):
|
||||
db = g.pop('db', None)
|
||||
.. code-block:: python
|
||||
|
||||
if db is not None:
|
||||
db.close()
|
||||
def generate_report(year):
|
||||
format = request.args.get("format")
|
||||
...
|
||||
|
||||
During a request, every call to ``get_db()`` will return the same
|
||||
connection, and it will be closed automatically at the end of the
|
||||
request.
|
||||
|
||||
You can use :class:`~werkzeug.local.LocalProxy` to make a new context
|
||||
local from ``get_db()``::
|
||||
|
||||
from werkzeug.local import LocalProxy
|
||||
db = LocalProxy(get_db)
|
||||
|
||||
Accessing ``db`` will call ``get_db`` internally, in the same way that
|
||||
:data:`current_app` works.
|
||||
|
||||
----
|
||||
|
||||
If you're writing an extension, :data:`g` should be reserved for user
|
||||
code. You may store internal data on the context itself, but be sure to
|
||||
use a sufficiently unique name. The current context is accessed with
|
||||
:data:`_app_ctx_stack.top <_app_ctx_stack>`. For more information see
|
||||
:doc:`/extensiondev`.
|
||||
with app.test_request_context(
|
||||
"/make_report/2017", query_string={"format": "short"}
|
||||
):
|
||||
generate_report()
|
||||
|
||||
|
||||
Events and Signals
|
||||
------------------
|
||||
.. _context-visibility:
|
||||
|
||||
The application will call functions registered with
|
||||
:meth:`~Flask.teardown_appcontext` when the application context is
|
||||
popped.
|
||||
Visibility of the Context
|
||||
-------------------------
|
||||
|
||||
If :data:`~signals.signals_available` is true, the following signals are
|
||||
sent: :data:`appcontext_pushed`, :data:`appcontext_tearing_down`, and
|
||||
:data:`appcontext_popped`.
|
||||
The context will have the same lifetime as an activity, such as a request, CLI
|
||||
command, or ``with`` block. Various callbacks and signals registered with the
|
||||
app will be run during the context.
|
||||
|
||||
When a Flask application handles a request, it pushes a request context
|
||||
to set the active application and request data. When it handles a CLI command,
|
||||
it pushes an app context to set the active application. When the activity ends,
|
||||
it pops that context. Proxy objects like :data:`.request`, :data:`.session`,
|
||||
:data:`.g`, and :data:`.current_app`, are accessible while the context is pushed
|
||||
and active, and are not accessible after the context is popped.
|
||||
|
||||
The context is unique to each thread (or other worker type). The proxies cannot
|
||||
be passed to another worker, which has a different context space and will not
|
||||
know about the active context in the parent's space.
|
||||
|
||||
Besides being scoped to each worker, the proxy object has a separate type and
|
||||
identity than the proxied real object. In some cases you'll need access to the
|
||||
real object, rather than the proxy. Use the
|
||||
:meth:`~.LocalProxy._get_current_object` method in those cases.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
app = current_app._get_current_object()
|
||||
my_signal.send(app)
|
||||
|
||||
|
||||
Lifecycle of the Context
|
||||
------------------------
|
||||
|
||||
Flask dispatches a request in multiple stages which can affect the request,
|
||||
response, and how errors are handled. See :doc:`/lifecycle` for a list of all
|
||||
the steps, callbacks, and signals during each request. The following are the
|
||||
steps directly related to the context.
|
||||
|
||||
- The app context is pushed, the proxies are available.
|
||||
- The :data:`.appcontext_pushed` signal is sent.
|
||||
- The request is dispatched.
|
||||
- Any :meth:`.Flask.teardown_request` decorated functions are called.
|
||||
- The :data:`.request_tearing_down` signal is sent.
|
||||
- Any :meth:`.Flask.teardown_appcontext` decorated functions are called.
|
||||
- The :data:`.appcontext_tearing_down` signal is sent.
|
||||
- The app context is popped, the proxies are no longer available.
|
||||
- The :data:`.appcontext_popped` signal is sent.
|
||||
|
||||
The teardown callbacks are called by the context when it is popped. They are
|
||||
called even if there is an unhandled exception during dispatch. They may be
|
||||
called multiple times in some test scenarios. This means there is no guarantee
|
||||
that any other parts of the request dispatch have run. Be sure to write these
|
||||
functions in a way that does not depend on other callbacks. All callbacks are
|
||||
called even if any raise an error.
|
||||
|
||||
|
||||
How the Context Works
|
||||
---------------------
|
||||
|
||||
Context locals are implemented using Python's :mod:`contextvars` and Werkzeug's
|
||||
:class:`~werkzeug.local.LocalProxy`. Python's contextvars are a low level
|
||||
structure to manage data local to a thread or coroutine. ``LocalProxy`` wraps
|
||||
the contextvar so that access to any attributes and methods is forwarded to the
|
||||
object stored in the contextvar.
|
||||
|
||||
The context is tracked like a stack, with the active context at the top of the
|
||||
stack. Flask manages pushing and popping contexts during requests, CLI commands,
|
||||
testing, ``with`` blocks, etc. The proxies access attributes on the active
|
||||
context.
|
||||
|
||||
Because it is a stack, other contexts may be pushed to change the proxies during
|
||||
an already active context. This is not a common pattern, but can be used in
|
||||
advanced use cases. For example, a Flask application can be used as WSGI
|
||||
middleware, calling another wrapped Flask app from a view.
|
||||
|
|
|
|||
119
docs/async-await.rst
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
.. _async_await:
|
||||
|
||||
Using ``async`` and ``await``
|
||||
=============================
|
||||
|
||||
.. versionadded:: 2.0
|
||||
|
||||
Routes, error handlers, before request, after request, and teardown
|
||||
functions can all be coroutine functions if Flask is installed with the
|
||||
``async`` extra (``pip install flask[async]``). This allows views to be
|
||||
defined with ``async def`` and use ``await``.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@app.route("/get-data")
|
||||
async def get_data():
|
||||
data = await async_db_query(...)
|
||||
return jsonify(data)
|
||||
|
||||
Pluggable class-based views also support handlers that are implemented as
|
||||
coroutines. This applies to the :meth:`~flask.views.View.dispatch_request`
|
||||
method in views that inherit from the :class:`flask.views.View` class, as
|
||||
well as all the HTTP method handlers in views that inherit from the
|
||||
:class:`flask.views.MethodView` class.
|
||||
|
||||
|
||||
Performance
|
||||
-----------
|
||||
|
||||
Async functions require an event loop to run. Flask, as a WSGI
|
||||
application, uses one worker to handle one request/response cycle.
|
||||
When a request comes in to an async view, Flask will start an event loop
|
||||
in a thread, run the view function there, then return the result.
|
||||
|
||||
Each request still ties up one worker, even for async views. The upside
|
||||
is that you can run async code within a view, for example to make
|
||||
multiple concurrent database queries, HTTP requests to an external API,
|
||||
etc. However, the number of requests your application can handle at one
|
||||
time will remain the same.
|
||||
|
||||
**Async is not inherently faster than sync code.** Async is beneficial
|
||||
when performing concurrent IO-bound tasks, but will probably not improve
|
||||
CPU-bound tasks. Traditional Flask views will still be appropriate for
|
||||
most use cases, but Flask's async support enables writing and using
|
||||
code that wasn't possible natively before.
|
||||
|
||||
|
||||
Background tasks
|
||||
----------------
|
||||
|
||||
Async functions will run in an event loop until they complete, at
|
||||
which stage the event loop will stop. This means any additional
|
||||
spawned tasks that haven't completed when the async function completes
|
||||
will be cancelled. Therefore you cannot spawn background tasks, for
|
||||
example via ``asyncio.create_task``.
|
||||
|
||||
If you wish to use background tasks it is best to use a task queue to
|
||||
trigger background work, rather than spawn tasks in a view
|
||||
function. With that in mind you can spawn asyncio tasks by serving
|
||||
Flask with an ASGI server and utilising the asgiref WsgiToAsgi adapter
|
||||
as described in :doc:`deploying/asgi`. This works as the adapter creates
|
||||
an event loop that runs continually.
|
||||
|
||||
|
||||
When to use Quart instead
|
||||
-------------------------
|
||||
|
||||
Flask's async support is less performant than async-first frameworks due
|
||||
to the way it is implemented. If you have a mainly async codebase it
|
||||
would make sense to consider `Quart`_. Quart is a reimplementation of
|
||||
Flask based on the `ASGI`_ standard instead of WSGI. This allows it to
|
||||
handle many concurrent requests, long running requests, and websockets
|
||||
without requiring multiple worker processes or threads.
|
||||
|
||||
It has also already been possible to :doc:`run Flask with Gevent </gevent>` to
|
||||
get many of the benefits of async request handling. Gevent patches low-level
|
||||
Python functions to accomplish this, whereas ``async``/``await`` and ASGI use
|
||||
standard, modern Python capabilities. Deciding whether you should use gevent
|
||||
with Flask, or Quart, or something else is ultimately up to understanding the
|
||||
specific needs of your project.
|
||||
|
||||
.. _Quart: https://quart.palletsprojects.com
|
||||
.. _ASGI: https://asgi.readthedocs.io
|
||||
|
||||
|
||||
Extensions
|
||||
----------
|
||||
|
||||
Flask extensions predating Flask's async support do not expect async views.
|
||||
If they provide decorators to add functionality to views, those will probably
|
||||
not work with async views because they will not await the function or be
|
||||
awaitable. Other functions they provide will not be awaitable either and
|
||||
will probably be blocking if called within an async view.
|
||||
|
||||
Extension authors can support async functions by utilising the
|
||||
:meth:`flask.Flask.ensure_sync` method. For example, if the extension
|
||||
provides a view function decorator add ``ensure_sync`` before calling
|
||||
the decorated function,
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def extension(func):
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
... # Extension logic
|
||||
return current_app.ensure_sync(func)(*args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
Check the changelog of the extension you want to use to see if they've
|
||||
implemented async support, or make a feature request or PR to them.
|
||||
|
||||
|
||||
Other event loops
|
||||
-----------------
|
||||
|
||||
At the moment Flask only supports :mod:`asyncio`. It's possible to override
|
||||
:meth:`flask.Flask.ensure_sync` to change how async functions are wrapped to use
|
||||
a different library. See :ref:`gevent-asyncio` for an example.
|
||||
|
|
@ -1,102 +0,0 @@
|
|||
.. _becomingbig:
|
||||
|
||||
Becoming Big
|
||||
============
|
||||
|
||||
Here are your options when growing your codebase or scaling your application.
|
||||
|
||||
Read the Source.
|
||||
----------------
|
||||
|
||||
Flask started in part to demonstrate how to build your own framework on top of
|
||||
existing well-used tools Werkzeug (WSGI) and Jinja (templating), and as it
|
||||
developed, it became useful to a wide audience. As you grow your codebase,
|
||||
don't just use Flask -- understand it. Read the source. Flask's code is
|
||||
written to be read; its documentation is published so you can use its internal
|
||||
APIs. Flask sticks to documented APIs in upstream libraries, and documents its
|
||||
internal utilities so that you can find the hook points needed for your
|
||||
project.
|
||||
|
||||
Hook. Extend.
|
||||
-------------
|
||||
|
||||
The :ref:`api` docs are full of available overrides, hook points, and
|
||||
:ref:`signals`. You can provide custom classes for things like the
|
||||
request and response objects. Dig deeper on the APIs you use, and look
|
||||
for the customizations which are available out of the box in a Flask
|
||||
release. Look for ways in which your project can be refactored into a
|
||||
collection of utilities and Flask extensions. Explore the many
|
||||
:doc:`/extensions` in the community, and look for patterns to build your
|
||||
own extensions if you do not find the tools you need.
|
||||
|
||||
Subclass.
|
||||
---------
|
||||
|
||||
The :class:`~flask.Flask` class has many methods designed for subclassing. You
|
||||
can quickly add or customize behavior by subclassing :class:`~flask.Flask` (see
|
||||
the linked method docs) and using that subclass wherever you instantiate an
|
||||
application class. This works well with :ref:`app-factories`.
|
||||
See :doc:`/patterns/subclassing` for an example.
|
||||
|
||||
Wrap with middleware.
|
||||
---------------------
|
||||
|
||||
The :ref:`app-dispatch` chapter shows in detail how to apply middleware. You
|
||||
can introduce WSGI middleware to wrap your Flask instances and introduce fixes
|
||||
and changes at the layer between your Flask application and your HTTP
|
||||
server. Werkzeug includes several `middlewares
|
||||
<https://werkzeug.palletsprojects.com/middleware/>`_.
|
||||
|
||||
Fork.
|
||||
-----
|
||||
|
||||
If none of the above options work, fork Flask. The majority of code of Flask
|
||||
is within Werkzeug and Jinja2. These libraries do the majority of the work.
|
||||
Flask is just the paste that glues those together. For every project there is
|
||||
the point where the underlying framework gets in the way (due to assumptions
|
||||
the original developers had). This is natural because if this would not be the
|
||||
case, the framework would be a very complex system to begin with which causes a
|
||||
steep learning curve and a lot of user frustration.
|
||||
|
||||
This is not unique to Flask. Many people use patched and modified
|
||||
versions of their framework to counter shortcomings. This idea is also
|
||||
reflected in the license of Flask. You don't have to contribute any
|
||||
changes back if you decide to modify the framework.
|
||||
|
||||
The downside of forking is of course that Flask extensions will most
|
||||
likely break because the new framework has a different import name.
|
||||
Furthermore integrating upstream changes can be a complex process,
|
||||
depending on the number of changes. Because of that, forking should be
|
||||
the very last resort.
|
||||
|
||||
Scale like a pro.
|
||||
-----------------
|
||||
|
||||
For many web applications the complexity of the code is less an issue than
|
||||
the scaling for the number of users or data entries expected. Flask by
|
||||
itself is only limited in terms of scaling by your application code, the
|
||||
data store you want to use and the Python implementation and webserver you
|
||||
are running on.
|
||||
|
||||
Scaling well means for example that if you double the amount of servers
|
||||
you get about twice the performance. Scaling bad means that if you add a
|
||||
new server the application won't perform any better or would not even
|
||||
support a second server.
|
||||
|
||||
There is only one limiting factor regarding scaling in Flask which are
|
||||
the context local proxies. They depend on context which in Flask is
|
||||
defined as being either a thread, process or greenlet. If your server
|
||||
uses some kind of concurrency that is not based on threads or greenlets,
|
||||
Flask will no longer be able to support these global proxies. However the
|
||||
majority of servers are using either threads, greenlets or separate
|
||||
processes to achieve concurrency which are all methods well supported by
|
||||
the underlying Werkzeug library.
|
||||
|
||||
Discuss with the community.
|
||||
---------------------------
|
||||
|
||||
The Flask developers keep the framework accessible to users with codebases big
|
||||
and small. If you find an obstacle in your way, caused by Flask, don't hesitate
|
||||
to contact the developers on the mailing list or Discord server. The best way for
|
||||
the Flask and Flask extension developers to improve the tools for larger
|
||||
applications is getting feedback from users.
|
||||
|
|
@ -1,5 +1,3 @@
|
|||
.. _blueprints:
|
||||
|
||||
Modular Applications with Blueprints
|
||||
====================================
|
||||
|
||||
|
|
@ -37,8 +35,9 @@ Blueprints in Flask are intended for these cases:
|
|||
A blueprint in Flask is not a pluggable app because it is not actually an
|
||||
application -- it's a set of operations which can be registered on an
|
||||
application, even multiple times. Why not have multiple application
|
||||
objects? You can do that (see :ref:`app-dispatch`), but your applications
|
||||
will have separate configs and will be managed at the WSGI layer.
|
||||
objects? You can do that (see :doc:`/patterns/appdispatch`), but your
|
||||
applications will have separate configs and will be managed at the WSGI
|
||||
layer.
|
||||
|
||||
Blueprints instead provide separation at the Flask level, share
|
||||
application config, and can change an application object as necessary with
|
||||
|
|
@ -70,7 +69,7 @@ implement a blueprint that does simple rendering of static templates::
|
|||
@simple_page.route('/<page>')
|
||||
def show(page):
|
||||
try:
|
||||
return render_template('pages/%s.html' % page)
|
||||
return render_template(f'pages/{page}.html')
|
||||
except TemplateNotFound:
|
||||
abort(404)
|
||||
|
||||
|
|
@ -121,6 +120,44 @@ On top of that you can register blueprints multiple times though not every
|
|||
blueprint might respond properly to that. In fact it depends on how the
|
||||
blueprint is implemented if it can be mounted more than once.
|
||||
|
||||
Nesting Blueprints
|
||||
------------------
|
||||
|
||||
It is possible to register a blueprint on another blueprint.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
parent = Blueprint('parent', __name__, url_prefix='/parent')
|
||||
child = Blueprint('child', __name__, url_prefix='/child')
|
||||
parent.register_blueprint(child)
|
||||
app.register_blueprint(parent)
|
||||
|
||||
The child blueprint will gain the parent's name as a prefix to its
|
||||
name, and child URLs will be prefixed with the parent's URL prefix.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
url_for('parent.child.create')
|
||||
/parent/child/create
|
||||
|
||||
In addition a child blueprint's will gain their parent's subdomain,
|
||||
with their subdomain as prefix if present i.e.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
parent = Blueprint('parent', __name__, subdomain='parent')
|
||||
child = Blueprint('child', __name__, subdomain='child')
|
||||
parent.register_blueprint(child)
|
||||
app.register_blueprint(parent)
|
||||
|
||||
url_for('parent.child.create', _external=True)
|
||||
"child.parent.domain.tld"
|
||||
|
||||
Blueprint-specific before request functions, etc. registered with the
|
||||
parent will trigger for the child. If a child does not have an error
|
||||
handler that can handle a given exception, the parent's will be tried.
|
||||
|
||||
|
||||
Blueprint Resources
|
||||
-------------------
|
||||
|
||||
|
|
@ -243,8 +280,9 @@ you can use relative redirects by prefixing the endpoint with a dot only::
|
|||
This will link to ``admin.index`` for instance in case the current request
|
||||
was dispatched to any other admin blueprint endpoint.
|
||||
|
||||
Error Handlers
|
||||
--------------
|
||||
|
||||
Blueprint Error Handlers
|
||||
------------------------
|
||||
|
||||
Blueprints support the ``errorhandler`` decorator just like the :class:`Flask`
|
||||
application object, so it is easy to make Blueprint-specific custom error
|
||||
|
|
@ -270,8 +308,8 @@ at the application level using the ``request`` proxy object::
|
|||
@app.errorhandler(405)
|
||||
def _handle_api_error(ex):
|
||||
if request.path.startswith('/api/'):
|
||||
return jsonify_error(ex)
|
||||
return jsonify(error=str(ex)), ex.code
|
||||
else:
|
||||
return ex
|
||||
|
||||
More information on error handling see :ref:`errorpages`.
|
||||
See :doc:`/errorhandling`.
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
Changelog
|
||||
=========
|
||||
Changes
|
||||
=======
|
||||
|
||||
.. include:: ../CHANGES.rst
|
||||
403
docs/cli.rst
|
|
@ -1,7 +1,5 @@
|
|||
.. currentmodule:: flask
|
||||
|
||||
.. _cli:
|
||||
|
||||
Command Line Interface
|
||||
======================
|
||||
|
||||
|
|
@ -17,58 +15,43 @@ Application Discovery
|
|||
---------------------
|
||||
|
||||
The ``flask`` command is installed by Flask, not your application; it must be
|
||||
told where to find your application in order to use it. The ``FLASK_APP``
|
||||
environment variable is used to specify how to load the application.
|
||||
told where to find your application in order to use it. The ``--app``
|
||||
option is used to specify how to load the application.
|
||||
|
||||
Unix Bash (Linux, Mac, etc.)::
|
||||
|
||||
$ export FLASK_APP=hello
|
||||
$ flask run
|
||||
|
||||
Windows CMD::
|
||||
|
||||
> set FLASK_APP=hello
|
||||
> flask run
|
||||
|
||||
Windows PowerShell::
|
||||
|
||||
> $env:FLASK_APP = "hello"
|
||||
> flask run
|
||||
|
||||
While ``FLASK_APP`` supports a variety of options for specifying your
|
||||
While ``--app`` supports a variety of options for specifying your
|
||||
application, most use cases should be simple. Here are the typical values:
|
||||
|
||||
(nothing)
|
||||
The file :file:`wsgi.py` is imported, automatically detecting an app
|
||||
(``app``). This provides an easy way to create an app from a factory with
|
||||
extra arguments.
|
||||
The name "app" or "wsgi" is imported (as a ".py" file, or package),
|
||||
automatically detecting an app (``app`` or ``application``) or
|
||||
factory (``create_app`` or ``make_app``).
|
||||
|
||||
``FLASK_APP=hello``
|
||||
The name is imported, automatically detecting an app (``app``) or factory
|
||||
(``create_app``).
|
||||
``--app hello``
|
||||
The given name is imported, automatically detecting an app (``app``
|
||||
or ``application``) or factory (``create_app`` or ``make_app``).
|
||||
|
||||
----
|
||||
|
||||
``FLASK_APP`` has three parts: an optional path that sets the current working
|
||||
``--app`` has three parts: an optional path that sets the current working
|
||||
directory, a Python file or dotted import path, and an optional variable
|
||||
name of the instance or factory. If the name is a factory, it can optionally
|
||||
be followed by arguments in parentheses. The following values demonstrate these
|
||||
parts:
|
||||
|
||||
``FLASK_APP=src/hello``
|
||||
``--app src/hello``
|
||||
Sets the current working directory to ``src`` then imports ``hello``.
|
||||
|
||||
``FLASK_APP=hello.web``
|
||||
``--app hello.web``
|
||||
Imports the path ``hello.web``.
|
||||
|
||||
``FLASK_APP=hello:app2``
|
||||
``--app hello:app2``
|
||||
Uses the ``app2`` Flask instance in ``hello``.
|
||||
|
||||
``FLASK_APP="hello:create_app('dev')"``
|
||||
``--app 'hello:create_app("dev")'``
|
||||
The ``create_app`` factory in ``hello`` is called with the string ``'dev'``
|
||||
as the argument.
|
||||
|
||||
If ``FLASK_APP`` is not set, the command will try to import "app" or
|
||||
If ``--app`` is not set, the command will try to import "app" or
|
||||
"wsgi" (as a ".py" file, or package) and try to detect an application
|
||||
instance or factory.
|
||||
|
||||
|
|
@ -77,12 +60,8 @@ Within the given import, the command looks for an application instance named
|
|||
found, the command looks for a factory function named ``create_app`` or
|
||||
``make_app`` that returns an instance.
|
||||
|
||||
When calling an application factory, if the factory takes an argument named
|
||||
``script_info``, then the :class:`~cli.ScriptInfo` instance is passed as a
|
||||
keyword argument. If the application factory takes only one argument and no
|
||||
parentheses follow the factory name, the :class:`~cli.ScriptInfo` instance
|
||||
is passed as a positional argument. If parentheses follow the factory name,
|
||||
their contents are parsed as Python literals and passes as arguments to the
|
||||
If parentheses follow the factory name, their contents are parsed as
|
||||
Python literals and passed as arguments and keyword arguments to the
|
||||
function. This means that strings must still be in quotes.
|
||||
|
||||
|
||||
|
|
@ -92,14 +71,63 @@ Run the Development Server
|
|||
The :func:`run <cli.run_command>` command will start the development server. It
|
||||
replaces the :meth:`Flask.run` method in most cases. ::
|
||||
|
||||
$ flask run
|
||||
$ flask --app hello run
|
||||
* Serving Flask app "hello"
|
||||
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
|
||||
|
||||
.. warning:: Do not use this command to run your application in production.
|
||||
Only use the development server during development. The development server
|
||||
is provided for convenience, but is not designed to be particularly secure,
|
||||
stable, or efficient. See :ref:`deployment` for how to run in production.
|
||||
stable, or efficient. See :doc:`/deploying/index` for how to run in production.
|
||||
|
||||
If another program is already using port 5000, you'll see
|
||||
``OSError: [Errno 98]`` or ``OSError: [WinError 10013]`` when the
|
||||
server tries to start. See :ref:`address-already-in-use` for how to
|
||||
handle that.
|
||||
|
||||
|
||||
Debug Mode
|
||||
~~~~~~~~~~
|
||||
|
||||
In debug mode, the ``flask run`` command will enable the interactive debugger and the
|
||||
reloader by default, and make errors easier to see and debug. To enable debug mode, use
|
||||
the ``--debug`` option.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ flask --app hello run --debug
|
||||
* Serving Flask app "hello"
|
||||
* Debug mode: on
|
||||
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
|
||||
* Restarting with inotify reloader
|
||||
* Debugger is active!
|
||||
* Debugger PIN: 223-456-919
|
||||
|
||||
The ``--debug`` option can also be passed to the top level ``flask`` command to enable
|
||||
debug mode for any command. The following two ``run`` calls are equivalent.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ flask --app hello --debug run
|
||||
$ flask --app hello run --debug
|
||||
|
||||
|
||||
Watch and Ignore Files with the Reloader
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
When using debug mode, the reloader will trigger whenever your Python code or imported
|
||||
modules change. The reloader can watch additional files with the ``--extra-files``
|
||||
option. Multiple paths are separated with ``:``, or ``;`` on Windows.
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ flask run --extra-files file1:dirA/file2:dirB/
|
||||
* Running on http://127.0.0.1:8000/
|
||||
* Detected change in '/path/to/file1', reloading
|
||||
|
||||
The reloader can also ignore files using :mod:`fnmatch` patterns with the
|
||||
``--exclude-patterns`` option. Multiple patterns are separated with ``:``, or ``;`` on
|
||||
Windows.
|
||||
|
||||
|
||||
Open a Shell
|
||||
|
|
@ -110,82 +138,34 @@ shell with the :func:`shell <cli.shell_command>` command. An application
|
|||
context will be active, and the app instance will be imported. ::
|
||||
|
||||
$ flask shell
|
||||
Python 3.6.2 (default, Jul 20 2017, 03:52:27)
|
||||
[GCC 7.1.1 20170630] on linux
|
||||
App: example
|
||||
Instance: /home/user/Projects/hello/instance
|
||||
Python 3.10.0 (default, Oct 27 2021, 06:59:51) [GCC 11.1.0] on linux
|
||||
App: example [production]
|
||||
Instance: /home/david/Projects/pallets/flask/instance
|
||||
>>>
|
||||
|
||||
Use :meth:`~Flask.shell_context_processor` to add other automatic imports.
|
||||
|
||||
|
||||
Environments
|
||||
------------
|
||||
|
||||
.. versionadded:: 1.0
|
||||
|
||||
The environment in which the Flask app runs is set by the
|
||||
:envvar:`FLASK_ENV` environment variable. If not set it defaults to
|
||||
``production``. The other recognized environment is ``development``.
|
||||
Flask and extensions may choose to enable behaviors based on the
|
||||
environment.
|
||||
|
||||
If the env is set to ``development``, the ``flask`` command will enable
|
||||
debug mode and ``flask run`` will enable the interactive debugger and
|
||||
reloader.
|
||||
|
||||
::
|
||||
|
||||
$ FLASK_ENV=development flask run
|
||||
* Serving Flask app "hello"
|
||||
* Environment: development
|
||||
* Debug mode: on
|
||||
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
|
||||
* Restarting with inotify reloader
|
||||
* Debugger is active!
|
||||
* Debugger PIN: 223-456-919
|
||||
|
||||
|
||||
Watch Extra Files with the Reloader
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
When using development mode, the reloader will trigger whenever your
|
||||
Python code or imported modules change. The reloader can watch
|
||||
additional files with the ``--extra-files`` option, or the
|
||||
``FLASK_RUN_EXTRA_FILES`` environment variable. Multiple paths are
|
||||
separated with ``:``, or ``;`` on Windows.
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
$ flask run --extra-files file1:dirA/file2:dirB/
|
||||
# or
|
||||
$ export FLASK_RUN_EXTRA_FILES=file1:dirA/file2:dirB/
|
||||
$ flask run
|
||||
* Running on http://127.0.0.1:8000/
|
||||
* Detected change in '/path/to/file1', reloading
|
||||
|
||||
|
||||
Debug Mode
|
||||
----------
|
||||
|
||||
Debug mode will be enabled when :envvar:`FLASK_ENV` is ``development``,
|
||||
as described above. If you want to control debug mode separately, use
|
||||
:envvar:`FLASK_DEBUG`. The value ``1`` enables it, ``0`` disables it.
|
||||
|
||||
|
||||
.. _dotenv:
|
||||
|
||||
Environment Variables From dotenv
|
||||
---------------------------------
|
||||
|
||||
Rather than setting ``FLASK_APP`` each time you open a new terminal, you can
|
||||
use Flask's dotenv support to set environment variables automatically.
|
||||
The ``flask`` command supports setting any option for any command with
|
||||
environment variables. The variables are named like ``FLASK_OPTION`` or
|
||||
``FLASK_COMMAND_OPTION``, for example ``FLASK_APP`` or
|
||||
``FLASK_RUN_PORT``.
|
||||
|
||||
Rather than passing options every time you run a command, or environment
|
||||
variables every time you open a new terminal, you can use Flask's dotenv
|
||||
support to set environment variables automatically.
|
||||
|
||||
If `python-dotenv`_ is installed, running the ``flask`` command will set
|
||||
environment variables defined in the files :file:`.env` and :file:`.flaskenv`.
|
||||
This can be used to avoid having to set ``FLASK_APP`` manually every time you
|
||||
open a new terminal, and to set configuration using environment variables
|
||||
similar to how some deployment services work.
|
||||
environment variables defined in the files ``.env`` and ``.flaskenv``.
|
||||
You can also specify an extra file to load with the ``--env-file``
|
||||
option. Dotenv files can be used to avoid having to set ``--app`` or
|
||||
``FLASK_APP`` manually, and to set configuration using environment
|
||||
variables similar to how some deployment services work.
|
||||
|
||||
Variables set on the command line are used over those set in :file:`.env`,
|
||||
which are used over those set in :file:`.flaskenv`. :file:`.flaskenv` should be
|
||||
|
|
@ -193,9 +173,7 @@ used for public variables, such as ``FLASK_APP``, while :file:`.env` should not
|
|||
be committed to your repository so that it can set private variables.
|
||||
|
||||
Directories are scanned upwards from the directory you call ``flask``
|
||||
from to locate the files. The current working directory will be set to the
|
||||
location of the file, with the assumption that that is the top level project
|
||||
directory.
|
||||
from to locate the files.
|
||||
|
||||
The files are only loaded by the ``flask`` command or calling
|
||||
:meth:`~Flask.run`. If you would like to load these files when running in
|
||||
|
|
@ -212,11 +190,39 @@ environment variables. The variables use the pattern
|
|||
``FLASK_COMMAND_OPTION``. For example, to set the port for the run
|
||||
command, instead of ``flask run --port 8000``:
|
||||
|
||||
.. code-block:: bash
|
||||
.. tabs::
|
||||
|
||||
$ export FLASK_RUN_PORT=8000
|
||||
$ flask run
|
||||
* Running on http://127.0.0.1:8000/
|
||||
.. group-tab:: Bash
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ export FLASK_RUN_PORT=8000
|
||||
$ flask run
|
||||
* Running on http://127.0.0.1:8000/
|
||||
|
||||
.. group-tab:: Fish
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ set -x FLASK_RUN_PORT 8000
|
||||
$ flask run
|
||||
* Running on http://127.0.0.1:8000/
|
||||
|
||||
.. group-tab:: CMD
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
> set FLASK_RUN_PORT=8000
|
||||
> flask run
|
||||
* Running on http://127.0.0.1:8000/
|
||||
|
||||
.. group-tab:: Powershell
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
> $env:FLASK_RUN_PORT = 8000
|
||||
> flask run
|
||||
* Running on http://127.0.0.1:8000/
|
||||
|
||||
These can be added to the ``.flaskenv`` file just like ``FLASK_APP`` to
|
||||
control default command options.
|
||||
|
|
@ -240,10 +246,35 @@ a project runner that loads them already. Keep in mind that the
|
|||
environment variables must be set before the app loads or it won't
|
||||
configure as expected.
|
||||
|
||||
.. code-block:: bash
|
||||
.. tabs::
|
||||
|
||||
$ export FLASK_SKIP_DOTENV=1
|
||||
$ flask run
|
||||
.. group-tab:: Bash
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ export FLASK_SKIP_DOTENV=1
|
||||
$ flask run
|
||||
|
||||
.. group-tab:: Fish
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ set -x FLASK_SKIP_DOTENV 1
|
||||
$ flask run
|
||||
|
||||
.. group-tab:: CMD
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
> set FLASK_SKIP_DOTENV=1
|
||||
> flask run
|
||||
|
||||
.. group-tab:: Powershell
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
> $env:FLASK_SKIP_DOTENV = 1
|
||||
> flask run
|
||||
|
||||
|
||||
Environment Variables From virtualenv
|
||||
|
|
@ -253,13 +284,31 @@ If you do not want to install dotenv support, you can still set environment
|
|||
variables by adding them to the end of the virtualenv's :file:`activate`
|
||||
script. Activating the virtualenv will set the variables.
|
||||
|
||||
Unix Bash, :file:`venv/bin/activate`::
|
||||
.. tabs::
|
||||
|
||||
$ export FLASK_APP=hello
|
||||
.. group-tab:: Bash
|
||||
|
||||
Windows CMD, :file:`venv\\Scripts\\activate.bat`::
|
||||
Unix Bash, :file:`.venv/bin/activate`::
|
||||
|
||||
> set FLASK_APP=hello
|
||||
$ export FLASK_APP=hello
|
||||
|
||||
.. group-tab:: Fish
|
||||
|
||||
Fish, :file:`.venv/bin/activate.fish`::
|
||||
|
||||
$ set -x FLASK_APP hello
|
||||
|
||||
.. group-tab:: CMD
|
||||
|
||||
Windows CMD, :file:`.venv\\Scripts\\activate.bat`::
|
||||
|
||||
> set FLASK_APP=hello
|
||||
|
||||
.. group-tab:: Powershell
|
||||
|
||||
Windows Powershell, :file:`.venv\\Scripts\\activate.ps1`::
|
||||
|
||||
> $env:FLASK_APP = "hello"
|
||||
|
||||
It is preferred to use dotenv support over this, since :file:`.flaskenv` can be
|
||||
committed to the repository so that it works automatically wherever the project
|
||||
|
|
@ -372,12 +421,14 @@ commands directly to the application's level:
|
|||
Application Context
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Commands added using the Flask app's :attr:`~Flask.cli`
|
||||
:meth:`~cli.AppGroup.command` decorator will be executed with an application
|
||||
context pushed, so your command and extensions have access to the app and its
|
||||
configuration. If you create a command using the Click :func:`~click.command`
|
||||
decorator instead of the Flask decorator, you can use
|
||||
:func:`~cli.with_appcontext` to get the same behavior. ::
|
||||
Commands added using the Flask app's :attr:`~Flask.cli` or
|
||||
:class:`~flask.cli.FlaskGroup` :meth:`~cli.AppGroup.command` decorator
|
||||
will be executed with an application context pushed, so your custom
|
||||
commands and parameters have access to the app and its configuration. The
|
||||
:func:`~cli.with_appcontext` decorator can be used to get the same
|
||||
behavior, but is not needed in most cases.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import click
|
||||
from flask.cli import with_appcontext
|
||||
|
|
@ -389,36 +440,22 @@ decorator instead of the Flask decorator, you can use
|
|||
|
||||
app.cli.add_command(do_work)
|
||||
|
||||
If you're sure a command doesn't need the context, you can disable it::
|
||||
|
||||
@app.cli.command(with_appcontext=False)
|
||||
def do_work():
|
||||
...
|
||||
|
||||
|
||||
Plugins
|
||||
-------
|
||||
|
||||
Flask will automatically load commands specified in the ``flask.commands``
|
||||
`entry point`_. This is useful for extensions that want to add commands when
|
||||
they are installed. Entry points are specified in :file:`setup.py` ::
|
||||
they are installed. Entry points are specified in :file:`pyproject.toml`:
|
||||
|
||||
from setuptools import setup
|
||||
|
||||
setup(
|
||||
name='flask-my-extension',
|
||||
...,
|
||||
entry_points={
|
||||
'flask.commands': [
|
||||
'my-command=flask_my_extension.commands:cli'
|
||||
],
|
||||
},
|
||||
)
|
||||
.. code-block:: toml
|
||||
|
||||
[project.entry-points."flask.commands"]
|
||||
my-command = "my_extension.commands:cli"
|
||||
|
||||
.. _entry point: https://packaging.python.org/tutorials/packaging-projects/#entry-points
|
||||
|
||||
Inside :file:`flask_my_extension/commands.py` you can then export a Click
|
||||
Inside :file:`my_extension/commands.py` you can then export a Click
|
||||
object::
|
||||
|
||||
import click
|
||||
|
|
@ -437,7 +474,7 @@ Custom Scripts
|
|||
--------------
|
||||
|
||||
When you are using the app factory pattern, it may be more convenient to define
|
||||
your own Click script. Instead of using ``FLASK_APP`` and letting Flask load
|
||||
your own Click script. Instead of using ``--app`` and letting Flask load
|
||||
your application, you can create your own Click object and export it as a
|
||||
`console script`_ entry point.
|
||||
|
||||
|
|
@ -456,22 +493,15 @@ Create an instance of :class:`~cli.FlaskGroup` and pass it the factory::
|
|||
def cli():
|
||||
"""Management script for the Wiki application."""
|
||||
|
||||
Define the entry point in :file:`setup.py`::
|
||||
Define the entry point in :file:`pyproject.toml`:
|
||||
|
||||
from setuptools import setup
|
||||
.. code-block:: toml
|
||||
|
||||
setup(
|
||||
name='flask-my-extension',
|
||||
...,
|
||||
entry_points={
|
||||
'console_scripts': [
|
||||
'wiki=wiki:cli'
|
||||
],
|
||||
},
|
||||
)
|
||||
[project.scripts]
|
||||
wiki = "wiki:cli"
|
||||
|
||||
Install the application in the virtualenv in editable mode and the custom
|
||||
script is available. Note that you don't need to set ``FLASK_APP``. ::
|
||||
script is available. Note that you don't need to set ``--app``. ::
|
||||
|
||||
$ pip install -e .
|
||||
$ wiki run
|
||||
|
|
@ -491,53 +521,36 @@ script is available. Note that you don't need to set ``FLASK_APP``. ::
|
|||
PyCharm Integration
|
||||
-------------------
|
||||
|
||||
PyCharm Professional provides a special Flask run configuration. For
|
||||
the Community Edition, we need to configure it to call the ``flask run``
|
||||
CLI command with the correct environment variables. These instructions
|
||||
should be similar for any other IDE you might want to use.
|
||||
PyCharm Professional provides a special Flask run configuration to run the development
|
||||
server. For the Community Edition, and for other commands besides ``run``, you need to
|
||||
create a custom run configuration. These instructions should be similar for any other
|
||||
IDE you use.
|
||||
|
||||
In PyCharm, with your project open, click on *Run* from the menu bar and
|
||||
go to *Edit Configurations*. You'll be greeted by a screen similar to
|
||||
this:
|
||||
In PyCharm, with your project open, click on *Run* from the menu bar and go to *Edit
|
||||
Configurations*. You'll see a screen similar to this:
|
||||
|
||||
.. image:: _static/pycharm-runconfig.png
|
||||
.. image:: _static/pycharm-run-config.png
|
||||
:align: center
|
||||
:class: screenshot
|
||||
:alt: Screenshot of PyCharms's run configuration settings.
|
||||
:alt: Screenshot of PyCharm run configuration.
|
||||
|
||||
There's quite a few options to change, but once we've done it for one
|
||||
command, we can easily copy the entire configuration and make a single
|
||||
tweak to give us access to other commands, including any custom ones you
|
||||
may implement yourself.
|
||||
Once you create a configuration for the ``flask run``, you can copy and change it to
|
||||
call any other command.
|
||||
|
||||
Click the + (*Add New Configuration*) button and select *Python*. Give
|
||||
the configuration a name such as "flask run". For the ``flask run``
|
||||
command, check "Single instance only" since you can't run the server
|
||||
more than once at the same time.
|
||||
Click the *+ (Add New Configuration)* button and select *Python*. Give the configuration
|
||||
a name such as "flask run".
|
||||
|
||||
Select *Module name* from the dropdown (**A**) then input ``flask``.
|
||||
Click the *Script path* dropdown and change it to *Module name*, then input ``flask``.
|
||||
|
||||
The *Parameters* field (**B**) is set to the CLI command to execute
|
||||
(with any arguments). In this example we use ``run``, which will run
|
||||
the development server.
|
||||
The *Parameters* field is set to the CLI command to execute along with any arguments.
|
||||
This example uses ``--app hello run --debug``, which will run the development server in
|
||||
debug mode. ``--app hello`` should be the import or file with your Flask app.
|
||||
|
||||
You can skip this next step if you're using :ref:`dotenv`. We need to
|
||||
add an environment variable (**C**) to identify our application. Click
|
||||
on the browse button and add an entry with ``FLASK_APP`` on the left and
|
||||
the Python import or file on the right (``hello`` for example). Add an
|
||||
entry with ``FLASK_ENV`` and set it to ``development``.
|
||||
If you installed your project as a package in your virtualenv, you may uncheck the
|
||||
*PYTHONPATH* options. This will more accurately match how you deploy later.
|
||||
|
||||
Next we need to set the working directory (**D**) to be the folder where
|
||||
our application resides.
|
||||
Click *OK* to save and close the configuration. Select the configuration in the main
|
||||
PyCharm window and click the play button next to it to run the server.
|
||||
|
||||
If you have installed your project as a package in your virtualenv, you
|
||||
may untick the *PYTHONPATH* options (**E**). This will more accurately
|
||||
match how you deploy the app later.
|
||||
|
||||
Click *Apply* to save the configuration, or *OK* to save and close the
|
||||
window. Select the configuration in the main PyCharm window and click
|
||||
the play button next to it to run the server.
|
||||
|
||||
Now that we have a configuration which runs ``flask run`` from within
|
||||
PyCharm, we can copy that configuration and alter the *Script* argument
|
||||
to run a different CLI command, e.g. ``flask shell``.
|
||||
Now that you have a configuration for ``flask run``, you can copy that configuration and
|
||||
change the *Parameters* argument to run a different CLI command.
|
||||
|
|
|
|||
49
docs/conf.py
|
|
@ -11,25 +11,33 @@ release, version = get_version("Flask")
|
|||
|
||||
# General --------------------------------------------------------------
|
||||
|
||||
master_doc = "index"
|
||||
default_role = "code"
|
||||
extensions = [
|
||||
"sphinx.ext.autodoc",
|
||||
"sphinx.ext.extlinks",
|
||||
"sphinx.ext.intersphinx",
|
||||
"sphinxcontrib.log_cabinet",
|
||||
"sphinx_tabs.tabs",
|
||||
"pallets_sphinx_themes",
|
||||
"sphinx_issues",
|
||||
]
|
||||
autodoc_member_order = "bysource"
|
||||
autodoc_typehints = "description"
|
||||
autodoc_preserve_defaults = True
|
||||
extlinks = {
|
||||
"issue": ("https://github.com/pallets/flask/issues/%s", "#%s"),
|
||||
"pr": ("https://github.com/pallets/flask/pull/%s", "#%s"),
|
||||
"ghsa": ("https://github.com/pallets/flask/security/advisories/GHSA-%s", "GHSA-%s"),
|
||||
}
|
||||
intersphinx_mapping = {
|
||||
"python": ("https://docs.python.org/3/", None),
|
||||
"werkzeug": ("https://werkzeug.palletsprojects.com/", None),
|
||||
"click": ("https://click.palletsprojects.com/", None),
|
||||
"jinja": ("http://jinja.pocoo.org/docs/", None),
|
||||
"jinja": ("https://jinja.palletsprojects.com/", None),
|
||||
"itsdangerous": ("https://itsdangerous.palletsprojects.com/", None),
|
||||
"sqlalchemy": ("https://docs.sqlalchemy.org/", None),
|
||||
"wtforms": ("https://wtforms.readthedocs.io/en/stable/", None),
|
||||
"blinker": ("https://pythonhosted.org/blinker/", None),
|
||||
"wtforms": ("https://wtforms.readthedocs.io/", None),
|
||||
"blinker": ("https://blinker.readthedocs.io/", None),
|
||||
}
|
||||
issues_github_path = "pallets/flask"
|
||||
|
||||
# HTML -----------------------------------------------------------------
|
||||
|
||||
|
|
@ -37,29 +45,26 @@ html_theme = "flask"
|
|||
html_theme_options = {"index_sidebar_logo": False}
|
||||
html_context = {
|
||||
"project_links": [
|
||||
ProjectLink("Donate to Pallets", "https://palletsprojects.com/donate"),
|
||||
ProjectLink("Flask Website", "https://palletsprojects.com/p/flask/"),
|
||||
ProjectLink("PyPI releases", "https://pypi.org/project/Flask/"),
|
||||
ProjectLink("Donate", "https://palletsprojects.com/donate"),
|
||||
ProjectLink("PyPI Releases", "https://pypi.org/project/Flask/"),
|
||||
ProjectLink("Source Code", "https://github.com/pallets/flask/"),
|
||||
ProjectLink("Issue Tracker", "https://github.com/pallets/flask/issues/"),
|
||||
ProjectLink("Chat", "https://discord.gg/pallets"),
|
||||
]
|
||||
}
|
||||
html_sidebars = {
|
||||
"index": ["project.html", "localtoc.html", "searchbox.html"],
|
||||
"**": ["localtoc.html", "relations.html", "searchbox.html"],
|
||||
"index": ["project.html", "localtoc.html", "searchbox.html", "ethicalads.html"],
|
||||
"**": ["localtoc.html", "relations.html", "searchbox.html", "ethicalads.html"],
|
||||
}
|
||||
singlehtml_sidebars = {"index": ["project.html", "localtoc.html"]}
|
||||
singlehtml_sidebars = {"index": ["project.html", "localtoc.html", "ethicalads.html"]}
|
||||
html_static_path = ["_static"]
|
||||
html_favicon = "_static/flask-icon.png"
|
||||
html_logo = "_static/flask-icon.png"
|
||||
html_title = "Flask Documentation ({})".format(version)
|
||||
html_favicon = "_static/flask-icon.svg"
|
||||
html_logo = "_static/flask-logo.svg"
|
||||
html_title = f"Flask Documentation ({version})"
|
||||
html_show_sourcelink = False
|
||||
|
||||
# LaTeX ----------------------------------------------------------------
|
||||
|
||||
latex_documents = [
|
||||
(master_doc, "Flask-{}.tex".format(version), html_title, author, "manual")
|
||||
]
|
||||
gettext_uuid = True
|
||||
gettext_compact = False
|
||||
|
||||
# Local Extensions -----------------------------------------------------
|
||||
|
||||
|
|
@ -76,9 +81,9 @@ def github_link(name, rawtext, text, lineno, inliner, options=None, content=None
|
|||
words = None
|
||||
|
||||
if packaging.version.parse(release).is_devrelease:
|
||||
url = "{0}master/{1}".format(base_url, text)
|
||||
url = f"{base_url}main/{text}"
|
||||
else:
|
||||
url = "{0}{1}/{2}".format(base_url, release, text)
|
||||
url = f"{base_url}{release}/{text}"
|
||||
|
||||
if words is None:
|
||||
words = url
|
||||
|
|
|
|||
484
docs/config.rst
|
|
@ -1,5 +1,3 @@
|
|||
.. _config:
|
||||
|
||||
Configuration Handling
|
||||
======================
|
||||
|
||||
|
|
@ -40,45 +38,26 @@ method::
|
|||
|
||||
app.config.update(
|
||||
TESTING=True,
|
||||
SECRET_KEY=b'_5#y2L"F4Q8z\n\xec]/'
|
||||
SECRET_KEY='192b9bdd22ab9ed4d12e236c78afcb9a393ec15f71bbf5dc987d54727823bcbf'
|
||||
)
|
||||
|
||||
|
||||
Environment and Debug Features
|
||||
------------------------------
|
||||
Debug Mode
|
||||
----------
|
||||
|
||||
The :data:`ENV` and :data:`DEBUG` config values are special because they
|
||||
may behave inconsistently if changed after the app has begun setting up.
|
||||
In order to set the environment and debug mode reliably, Flask uses
|
||||
environment variables.
|
||||
The :data:`DEBUG` config value is special because it may behave inconsistently if
|
||||
changed after the app has begun setting up. In order to set debug mode reliably, use the
|
||||
``--debug`` option on the ``flask`` or ``flask run`` command. ``flask run`` will use the
|
||||
interactive debugger and reloader by default in debug mode.
|
||||
|
||||
The environment is used to indicate to Flask, extensions, and other
|
||||
programs, like Sentry, what context Flask is running in. It is
|
||||
controlled with the :envvar:`FLASK_ENV` environment variable and
|
||||
defaults to ``production``.
|
||||
.. code-block:: text
|
||||
|
||||
Setting :envvar:`FLASK_ENV` to ``development`` will enable debug mode.
|
||||
``flask run`` will use the interactive debugger and reloader by default
|
||||
in debug mode. To control this separately from the environment, use the
|
||||
:envvar:`FLASK_DEBUG` flag.
|
||||
$ flask --app hello run --debug
|
||||
|
||||
.. versionchanged:: 1.0
|
||||
Added :envvar:`FLASK_ENV` to control the environment separately
|
||||
from debug mode. The development environment enables debug mode.
|
||||
|
||||
To switch Flask to the development environment and enable debug mode,
|
||||
set :envvar:`FLASK_ENV`::
|
||||
|
||||
$ export FLASK_ENV=development
|
||||
$ flask run
|
||||
|
||||
(On Windows, use ``set`` instead of ``export``.)
|
||||
|
||||
Using the environment variables as described above is recommended. While
|
||||
it is possible to set :data:`ENV` and :data:`DEBUG` in your config or
|
||||
code, this is strongly discouraged. They can't be read early by the
|
||||
``flask`` command, and some systems or extensions may have already
|
||||
configured themselves based on a previous value.
|
||||
Using the option is recommended. While it is possible to set :data:`DEBUG` in your
|
||||
config or code, this is strongly discouraged. It can't be read early by the
|
||||
``flask run`` command, and some systems or extensions may have already configured
|
||||
themselves based on a previous value.
|
||||
|
||||
|
||||
Builtin Configuration Values
|
||||
|
|
@ -86,38 +65,21 @@ Builtin Configuration Values
|
|||
|
||||
The following configuration values are used internally by Flask:
|
||||
|
||||
.. py:data:: ENV
|
||||
|
||||
What environment the app is running in. Flask and extensions may
|
||||
enable behaviors based on the environment, such as enabling debug
|
||||
mode. The :attr:`~flask.Flask.env` attribute maps to this config
|
||||
key. This is set by the :envvar:`FLASK_ENV` environment variable and
|
||||
may not behave as expected if set in code.
|
||||
|
||||
**Do not enable development when deploying in production.**
|
||||
|
||||
Default: ``'production'``
|
||||
|
||||
.. versionadded:: 1.0
|
||||
|
||||
.. py:data:: DEBUG
|
||||
|
||||
Whether debug mode is enabled. When using ``flask run`` to start the
|
||||
development server, an interactive debugger will be shown for
|
||||
unhandled exceptions, and the server will be reloaded when code
|
||||
changes. The :attr:`~flask.Flask.debug` attribute maps to this
|
||||
config key. This is enabled when :data:`ENV` is ``'development'``
|
||||
and is overridden by the ``FLASK_DEBUG`` environment variable. It
|
||||
may not behave as expected if set in code.
|
||||
Whether debug mode is enabled. When using ``flask run`` to start the development
|
||||
server, an interactive debugger will be shown for unhandled exceptions, and the
|
||||
server will be reloaded when code changes. The :attr:`~flask.Flask.debug` attribute
|
||||
maps to this config key. This is set with the ``FLASK_DEBUG`` environment variable.
|
||||
It may not behave as expected if set in code.
|
||||
|
||||
**Do not enable debug mode when deploying in production.**
|
||||
|
||||
Default: ``True`` if :data:`ENV` is ``'development'``, or ``False``
|
||||
otherwise.
|
||||
Default: ``False``
|
||||
|
||||
.. py:data:: TESTING
|
||||
|
||||
Enable testing mode. Exceptions are propagated rather than handled by the
|
||||
Enable testing mode. Exceptions are propagated rather than handled by
|
||||
the app's error handlers. Extensions may also change their behavior to
|
||||
facilitate easier testing. You should enable this in your own tests.
|
||||
|
||||
|
|
@ -131,14 +93,6 @@ The following configuration values are used internally by Flask:
|
|||
|
||||
Default: ``None``
|
||||
|
||||
.. py:data:: PRESERVE_CONTEXT_ON_EXCEPTION
|
||||
|
||||
Don't pop the request context when an exception occurs. If not set, this
|
||||
is true if ``DEBUG`` is true. This allows debuggers to introspect the
|
||||
request data on errors, and should normally not need to be set directly.
|
||||
|
||||
Default: ``None``
|
||||
|
||||
.. py:data:: TRAP_HTTP_EXCEPTIONS
|
||||
|
||||
If there is no handler for an ``HTTPException``-type exception, re-raise it
|
||||
|
|
@ -161,16 +115,35 @@ The following configuration values are used internally by Flask:
|
|||
|
||||
A secret key that will be used for securely signing the session cookie
|
||||
and can be used for any other security related needs by extensions or your
|
||||
application. It should be a long random string of bytes, although unicode
|
||||
is accepted too. For example, copy the output of this to your config::
|
||||
application. It should be a long random ``bytes`` or ``str``. For
|
||||
example, copy the output of this to your config::
|
||||
|
||||
$ python -c 'import os; print(os.urandom(16))'
|
||||
b'_5#y2L"F4Q8z\n\xec]/'
|
||||
$ python -c 'import secrets; print(secrets.token_hex())'
|
||||
'192b9bdd22ab9ed4d12e236c78afcb9a393ec15f71bbf5dc987d54727823bcbf'
|
||||
|
||||
**Do not reveal the secret key when posting questions or committing code.**
|
||||
|
||||
Default: ``None``
|
||||
|
||||
.. py:data:: SECRET_KEY_FALLBACKS
|
||||
|
||||
A list of old secret keys that can still be used for unsigning. This allows
|
||||
a project to implement key rotation without invalidating active sessions or
|
||||
other recently-signed secrets.
|
||||
|
||||
Keys should be removed after an appropriate period of time, as checking each
|
||||
additional key adds some overhead.
|
||||
|
||||
Order should not matter, but the default implementation will test the last
|
||||
key in the list first, so it might make sense to order oldest to newest.
|
||||
|
||||
Flask's built-in secure cookie session supports this. Extensions that use
|
||||
:data:`SECRET_KEY` may not support this yet.
|
||||
|
||||
Default: ``None``
|
||||
|
||||
.. versionadded:: 3.1
|
||||
|
||||
.. py:data:: SESSION_COOKIE_NAME
|
||||
|
||||
The name of the session cookie. Can be changed in case you already have a
|
||||
|
|
@ -180,12 +153,23 @@ The following configuration values are used internally by Flask:
|
|||
|
||||
.. py:data:: SESSION_COOKIE_DOMAIN
|
||||
|
||||
The domain match rule that the session cookie will be valid for. If not
|
||||
set, the cookie will be valid for all subdomains of :data:`SERVER_NAME`.
|
||||
If ``False``, the cookie's domain will not be set.
|
||||
The value of the ``Domain`` parameter on the session cookie. If not set, browsers
|
||||
will only send the cookie to the exact domain it was set from. Otherwise, they
|
||||
will send it to any subdomain of the given value as well.
|
||||
|
||||
Not setting this value is more restricted and secure than setting it.
|
||||
|
||||
Default: ``None``
|
||||
|
||||
.. warning::
|
||||
If this is changed after the browser created a cookie is created with
|
||||
one setting, it may result in another being created. Browsers may send
|
||||
send both in an undefined order. In that case, you may want to change
|
||||
:data:`SESSION_COOKIE_NAME` as well or otherwise invalidate old sessions.
|
||||
|
||||
.. versionchanged:: 2.3
|
||||
Not set by default, does not fall back to ``SERVER_NAME``.
|
||||
|
||||
.. py:data:: SESSION_COOKIE_PATH
|
||||
|
||||
The path that the session cookie will be valid for. If not set, the cookie
|
||||
|
|
@ -208,6 +192,23 @@ The following configuration values are used internally by Flask:
|
|||
|
||||
Default: ``False``
|
||||
|
||||
.. py:data:: SESSION_COOKIE_PARTITIONED
|
||||
|
||||
Browsers will send cookies based on the top-level document's domain, rather
|
||||
than only the domain of the document setting the cookie. This prevents third
|
||||
party cookies set in iframes from "leaking" between separate sites.
|
||||
|
||||
Browsers are beginning to disallow non-partitioned third party cookies, so
|
||||
you need to mark your cookies partitioned if you expect them to work in such
|
||||
embedded situations.
|
||||
|
||||
Enabling this implicitly enables :data:`SESSION_COOKIE_SECURE` as well, as
|
||||
it is only valid when served over HTTPS.
|
||||
|
||||
Default: ``False``
|
||||
|
||||
.. versionadded:: 3.1
|
||||
|
||||
.. py:data:: SESSION_COOKIE_SAMESITE
|
||||
|
||||
Restrict how cookies are sent with requests from external sites. Can
|
||||
|
|
@ -249,36 +250,60 @@ The following configuration values are used internally by Flask:
|
|||
.. py:data:: SEND_FILE_MAX_AGE_DEFAULT
|
||||
|
||||
When serving files, set the cache control max age to this number of
|
||||
seconds. Can either be a :class:`datetime.timedelta` or an ``int``.
|
||||
seconds. Can be a :class:`datetime.timedelta` or an ``int``.
|
||||
Override this value on a per-file basis using
|
||||
:meth:`~flask.Flask.get_send_file_max_age` on the application or blueprint.
|
||||
:meth:`~flask.Flask.get_send_file_max_age` on the application or
|
||||
blueprint.
|
||||
|
||||
Default: ``timedelta(hours=12)`` (``43200`` seconds)
|
||||
If ``None``, ``send_file`` tells the browser to use conditional
|
||||
requests will be used instead of a timed cache, which is usually
|
||||
preferable.
|
||||
|
||||
Default: ``None``
|
||||
|
||||
.. py:data:: TRUSTED_HOSTS
|
||||
|
||||
Validate :attr:`.Request.host` and other attributes that use it against
|
||||
these trusted values. Raise a :exc:`~werkzeug.exceptions.SecurityError` if
|
||||
the host is invalid, which results in a 400 error. If it is ``None``, all
|
||||
hosts are valid. Each value is either an exact match, or can start with
|
||||
a dot ``.`` to match any subdomain.
|
||||
|
||||
Validation is done during routing against this value. ``before_request`` and
|
||||
``after_request`` callbacks will still be called.
|
||||
|
||||
Default: ``None``
|
||||
|
||||
.. versionadded:: 3.1
|
||||
|
||||
.. py:data:: SERVER_NAME
|
||||
|
||||
Inform the application what host and port it is bound to. Required
|
||||
for subdomain route matching support.
|
||||
Inform the application what host and port it is bound to.
|
||||
|
||||
If set, will be used for the session cookie domain if
|
||||
:data:`SESSION_COOKIE_DOMAIN` is not set. Modern web browsers will
|
||||
not allow setting cookies for domains without a dot. To use a domain
|
||||
locally, add any names that should route to the app to your
|
||||
``hosts`` file. ::
|
||||
Must be set if ``subdomain_matching`` is enabled, to be able to extract the
|
||||
subdomain from the request.
|
||||
|
||||
127.0.0.1 localhost.dev
|
||||
|
||||
If set, ``url_for`` can generate external URLs with only an application
|
||||
context instead of a request context.
|
||||
Must be set for ``url_for`` to generate external URLs outside of a
|
||||
request context.
|
||||
|
||||
Default: ``None``
|
||||
|
||||
.. versionchanged:: 3.1
|
||||
Does not restrict requests to only this domain, for both
|
||||
``subdomain_matching`` and ``host_matching``.
|
||||
|
||||
.. versionchanged:: 1.0
|
||||
Does not implicitly enable ``subdomain_matching``.
|
||||
|
||||
.. versionchanged:: 2.3
|
||||
Does not affect ``SESSION_COOKIE_DOMAIN``.
|
||||
|
||||
.. py:data:: APPLICATION_ROOT
|
||||
|
||||
Inform the application what path it is mounted under by the application /
|
||||
web server. This is used for generating URLs outside the context of a
|
||||
request (inside a request, the dispatcher is responsible for setting
|
||||
``SCRIPT_NAME`` instead; see :ref:`Application Dispatching <app-dispatch>`
|
||||
``SCRIPT_NAME`` instead; see :doc:`/patterns/appdispatch`
|
||||
for examples of dispatch configuration).
|
||||
|
||||
Will be used for the session cookie path if ``SESSION_COOKIE_PATH`` is not
|
||||
|
|
@ -294,42 +319,53 @@ The following configuration values are used internally by Flask:
|
|||
|
||||
.. py:data:: MAX_CONTENT_LENGTH
|
||||
|
||||
Don't read more than this many bytes from the incoming request data. If not
|
||||
set and the request does not specify a ``CONTENT_LENGTH``, no data will be
|
||||
read for security.
|
||||
The maximum number of bytes that will be read during this request. If
|
||||
this limit is exceeded, a 413 :exc:`~werkzeug.exceptions.RequestEntityTooLarge`
|
||||
error is raised. If it is set to ``None``, no limit is enforced at the
|
||||
Flask application level. However, if it is ``None`` and the request has no
|
||||
``Content-Length`` header and the WSGI server does not indicate that it
|
||||
terminates the stream, then no data is read to avoid an infinite stream.
|
||||
|
||||
Each request defaults to this config. It can be set on a specific
|
||||
:attr:`.Request.max_content_length` to apply the limit to that specific
|
||||
view. This should be set appropriately based on an application's or view's
|
||||
specific needs.
|
||||
|
||||
Default: ``None``
|
||||
|
||||
.. py:data:: JSON_AS_ASCII
|
||||
.. versionadded:: 0.6
|
||||
|
||||
Serialize objects to ASCII-encoded JSON. If this is disabled, the JSON
|
||||
will be returned as a Unicode string, or encoded as ``UTF-8`` by
|
||||
``jsonify``. This has security implications when rendering the JSON into
|
||||
JavaScript in templates, and should typically remain enabled.
|
||||
.. py:data:: MAX_FORM_MEMORY_SIZE
|
||||
|
||||
Default: ``True``
|
||||
The maximum size in bytes any non-file form field may be in a
|
||||
``multipart/form-data`` body. If this limit is exceeded, a 413
|
||||
:exc:`~werkzeug.exceptions.RequestEntityTooLarge` error is raised. If it is
|
||||
set to ``None``, no limit is enforced at the Flask application level.
|
||||
|
||||
.. py:data:: JSON_SORT_KEYS
|
||||
Each request defaults to this config. It can be set on a specific
|
||||
:attr:`.Request.max_form_memory_parts` to apply the limit to that specific
|
||||
view. This should be set appropriately based on an application's or view's
|
||||
specific needs.
|
||||
|
||||
Sort the keys of JSON objects alphabetically. This is useful for caching
|
||||
because it ensures the data is serialized the same way no matter what
|
||||
Python's hash seed is. While not recommended, you can disable this for a
|
||||
possible performance improvement at the cost of caching.
|
||||
Default: ``500_000``
|
||||
|
||||
Default: ``True``
|
||||
.. versionadded:: 3.1
|
||||
|
||||
.. py:data:: JSONIFY_PRETTYPRINT_REGULAR
|
||||
.. py:data:: MAX_FORM_PARTS
|
||||
|
||||
``jsonify`` responses will be output with newlines, spaces, and indentation
|
||||
for easier reading by humans. Always enabled in debug mode.
|
||||
The maximum number of fields that may be present in a
|
||||
``multipart/form-data`` body. If this limit is exceeded, a 413
|
||||
:exc:`~werkzeug.exceptions.RequestEntityTooLarge` error is raised. If it
|
||||
is set to ``None``, no limit is enforced at the Flask application level.
|
||||
|
||||
Default: ``False``
|
||||
Each request defaults to this config. It can be set on a specific
|
||||
:attr:`.Request.max_form_parts` to apply the limit to that specific view.
|
||||
This should be set appropriately based on an application's or view's
|
||||
specific needs.
|
||||
|
||||
.. py:data:: JSONIFY_MIMETYPE
|
||||
Default: ``1_000``
|
||||
|
||||
The mimetype of ``jsonify`` responses.
|
||||
|
||||
Default: ``'application/json'``
|
||||
.. versionadded:: 3.1
|
||||
|
||||
.. py:data:: TEMPLATES_AUTO_RELOAD
|
||||
|
||||
|
|
@ -352,6 +388,12 @@ The following configuration values are used internally by Flask:
|
|||
``4093``. Larger cookies may be silently ignored by browsers. Set to
|
||||
``0`` to disable the warning.
|
||||
|
||||
.. py:data:: PROVIDE_AUTOMATIC_OPTIONS
|
||||
|
||||
Set to ``False`` to disable the automatic addition of OPTIONS
|
||||
responses. This can be overridden per route by altering the
|
||||
``provide_automatic_options`` attribute.
|
||||
|
||||
.. versionadded:: 0.4
|
||||
``LOGGER_NAME``
|
||||
|
||||
|
|
@ -392,17 +434,30 @@ The following configuration values are used internally by Flask:
|
|||
|
||||
Added :data:`MAX_COOKIE_SIZE` to control a warning from Werkzeug.
|
||||
|
||||
.. versionchanged:: 2.2
|
||||
Removed ``PRESERVE_CONTEXT_ON_EXCEPTION``.
|
||||
|
||||
Configuring from Files
|
||||
----------------------
|
||||
.. versionchanged:: 2.3
|
||||
``JSON_AS_ASCII``, ``JSON_SORT_KEYS``, ``JSONIFY_MIMETYPE``, and
|
||||
``JSONIFY_PRETTYPRINT_REGULAR`` were removed. The default ``app.json`` provider has
|
||||
equivalent attributes instead.
|
||||
|
||||
Configuration becomes more useful if you can store it in a separate file,
|
||||
ideally located outside the actual application package. This makes
|
||||
packaging and distributing your application possible via various package
|
||||
handling tools (:ref:`distribute-deployment`) and finally modifying the
|
||||
configuration file afterwards.
|
||||
.. versionchanged:: 2.3
|
||||
``ENV`` was removed.
|
||||
|
||||
So a common pattern is this::
|
||||
.. versionadded:: 3.1
|
||||
Added :data:`PROVIDE_AUTOMATIC_OPTIONS` to control the default
|
||||
addition of autogenerated OPTIONS responses.
|
||||
|
||||
|
||||
Configuring from Python Files
|
||||
-----------------------------
|
||||
|
||||
Configuration becomes more useful if you can store it in a separate file, ideally
|
||||
located outside the actual application package. You can deploy your application, then
|
||||
separately configure it for the specific deployment.
|
||||
|
||||
A common pattern is this::
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config.from_object('yourapplication.default_settings')
|
||||
|
|
@ -411,18 +466,42 @@ So a common pattern is this::
|
|||
This first loads the configuration from the
|
||||
`yourapplication.default_settings` module and then overrides the values
|
||||
with the contents of the file the :envvar:`YOURAPPLICATION_SETTINGS`
|
||||
environment variable points to. This environment variable can be set on
|
||||
Linux or OS X with the export command in the shell before starting the
|
||||
server::
|
||||
environment variable points to. This environment variable can be set
|
||||
in the shell before starting the server:
|
||||
|
||||
$ export YOURAPPLICATION_SETTINGS=/path/to/settings.cfg
|
||||
$ python run-app.py
|
||||
* Running on http://127.0.0.1:5000/
|
||||
* Restarting with reloader...
|
||||
.. tabs::
|
||||
|
||||
On Windows systems use the `set` builtin instead::
|
||||
.. group-tab:: Bash
|
||||
|
||||
> set YOURAPPLICATION_SETTINGS=\path\to\settings.cfg
|
||||
.. code-block:: text
|
||||
|
||||
$ export YOURAPPLICATION_SETTINGS=/path/to/settings.cfg
|
||||
$ flask run
|
||||
* Running on http://127.0.0.1:5000/
|
||||
|
||||
.. group-tab:: Fish
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ set -x YOURAPPLICATION_SETTINGS /path/to/settings.cfg
|
||||
$ flask run
|
||||
* Running on http://127.0.0.1:5000/
|
||||
|
||||
.. group-tab:: CMD
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
> set YOURAPPLICATION_SETTINGS=\path\to\settings.cfg
|
||||
> flask run
|
||||
* Running on http://127.0.0.1:5000/
|
||||
|
||||
.. group-tab:: Powershell
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
> $env:YOURAPPLICATION_SETTINGS = "\path\to\settings.cfg"
|
||||
> flask run
|
||||
* Running on http://127.0.0.1:5000/
|
||||
|
||||
The configuration files themselves are actual Python files. Only values
|
||||
in uppercase are actually stored in the config object later on. So make
|
||||
|
|
@ -431,8 +510,7 @@ sure to use uppercase letters for your config keys.
|
|||
Here is an example of a configuration file::
|
||||
|
||||
# Example configuration
|
||||
DEBUG = False
|
||||
SECRET_KEY = b'_5#y2L"F4Q8z\n\xec]/'
|
||||
SECRET_KEY = '192b9bdd22ab9ed4d12e236c78afcb9a393ec15f71bbf5dc987d54727823bcbf'
|
||||
|
||||
Make sure to load the configuration very early on, so that extensions have
|
||||
the ability to access the configuration when starting up. There are other
|
||||
|
|
@ -441,50 +519,116 @@ complete reference, read the :class:`~flask.Config` object's
|
|||
documentation.
|
||||
|
||||
|
||||
Configuring from Data Files
|
||||
---------------------------
|
||||
|
||||
It is also possible to load configuration from a file in a format of
|
||||
your choice using :meth:`~flask.Config.from_file`. For example to load
|
||||
from a TOML file:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import tomllib
|
||||
app.config.from_file("config.toml", load=tomllib.load, text=False)
|
||||
|
||||
Or from a JSON file:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import json
|
||||
app.config.from_file("config.json", load=json.load)
|
||||
|
||||
|
||||
Configuring from Environment Variables
|
||||
--------------------------------------
|
||||
|
||||
In addition to pointing to configuration files using environment variables, you
|
||||
may find it useful (or necessary) to control your configuration values directly
|
||||
from the environment.
|
||||
In addition to pointing to configuration files using environment
|
||||
variables, you may find it useful (or necessary) to control your
|
||||
configuration values directly from the environment. Flask can be
|
||||
instructed to load all environment variables starting with a specific
|
||||
prefix into the config using :meth:`~flask.Config.from_prefixed_env`.
|
||||
|
||||
Environment variables can be set on Linux or OS X with the export command in
|
||||
the shell before starting the server::
|
||||
Environment variables can be set in the shell before starting the
|
||||
server:
|
||||
|
||||
$ export SECRET_KEY='5f352379324c22463451387a0aec5d2f'
|
||||
$ export MAIL_ENABLED=false
|
||||
$ python run-app.py
|
||||
* Running on http://127.0.0.1:5000/
|
||||
.. tabs::
|
||||
|
||||
On Windows systems use the ``set`` builtin instead::
|
||||
.. group-tab:: Bash
|
||||
|
||||
> set SECRET_KEY='5f352379324c22463451387a0aec5d2f'
|
||||
.. code-block:: text
|
||||
|
||||
While this approach is straightforward to use, it is important to remember that
|
||||
environment variables are strings -- they are not automatically deserialized
|
||||
into Python types.
|
||||
$ export FLASK_SECRET_KEY="5f352379324c22463451387a0aec5d2f"
|
||||
$ export FLASK_MAIL_ENABLED=false
|
||||
$ flask run
|
||||
* Running on http://127.0.0.1:5000/
|
||||
|
||||
Here is an example of a configuration file that uses environment variables::
|
||||
.. group-tab:: Fish
|
||||
|
||||
import os
|
||||
.. code-block:: text
|
||||
|
||||
_mail_enabled = os.environ.get("MAIL_ENABLED", default="true")
|
||||
MAIL_ENABLED = _mail_enabled.lower() in {"1", "t", "true"}
|
||||
$ set -x FLASK_SECRET_KEY "5f352379324c22463451387a0aec5d2f"
|
||||
$ set -x FLASK_MAIL_ENABLED false
|
||||
$ flask run
|
||||
* Running on http://127.0.0.1:5000/
|
||||
|
||||
SECRET_KEY = os.environ.get("SECRET_KEY")
|
||||
.. group-tab:: CMD
|
||||
|
||||
if not SECRET_KEY:
|
||||
raise ValueError("No SECRET_KEY set for Flask application")
|
||||
.. code-block:: text
|
||||
|
||||
> set FLASK_SECRET_KEY="5f352379324c22463451387a0aec5d2f"
|
||||
> set FLASK_MAIL_ENABLED=false
|
||||
> flask run
|
||||
* Running on http://127.0.0.1:5000/
|
||||
|
||||
Notice that any value besides an empty string will be interpreted as a boolean
|
||||
``True`` value in Python, which requires care if an environment explicitly sets
|
||||
values intended to be ``False``.
|
||||
.. group-tab:: Powershell
|
||||
|
||||
Make sure to load the configuration very early on, so that extensions have the
|
||||
ability to access the configuration when starting up. There are other methods
|
||||
on the config object as well to load from individual files. For a complete
|
||||
reference, read the :class:`~flask.Config` class documentation.
|
||||
.. code-block:: text
|
||||
|
||||
> $env:FLASK_SECRET_KEY = "5f352379324c22463451387a0aec5d2f"
|
||||
> $env:FLASK_MAIL_ENABLED = "false"
|
||||
> flask run
|
||||
* Running on http://127.0.0.1:5000/
|
||||
|
||||
The variables can then be loaded and accessed via the config with a key
|
||||
equal to the environment variable name without the prefix i.e.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
app.config.from_prefixed_env()
|
||||
app.config["SECRET_KEY"] # Is "5f352379324c22463451387a0aec5d2f"
|
||||
|
||||
The prefix is ``FLASK_`` by default. This is configurable via the
|
||||
``prefix`` argument of :meth:`~flask.Config.from_prefixed_env`.
|
||||
|
||||
Values will be parsed to attempt to convert them to a more specific type
|
||||
than strings. By default :func:`json.loads` is used, so any valid JSON
|
||||
value is possible, including lists and dicts. This is configurable via
|
||||
the ``loads`` argument of :meth:`~flask.Config.from_prefixed_env`.
|
||||
|
||||
When adding a boolean value with the default JSON parsing, only "true"
|
||||
and "false", lowercase, are valid values. Keep in mind that any
|
||||
non-empty string is considered ``True`` by Python.
|
||||
|
||||
It is possible to set keys in nested dictionaries by separating the
|
||||
keys with double underscore (``__``). Any intermediate keys that don't
|
||||
exist on the parent dict will be initialized to an empty dict.
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ export FLASK_MYAPI__credentials__username=user123
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
app.config["MYAPI"]["credentials"]["username"] # Is "user123"
|
||||
|
||||
On Windows, environment variable keys are always uppercase, therefore
|
||||
the above example would end up as ``MYAPI__CREDENTIALS__USERNAME``.
|
||||
|
||||
For even more config loading features, including merging and
|
||||
case-insensitive Windows support, try a dedicated library such as
|
||||
Dynaconf_, which includes integration with Flask.
|
||||
|
||||
.. _Dynaconf: https://www.dynaconf.com/
|
||||
|
||||
|
||||
Configuration Best Practices
|
||||
|
|
@ -504,6 +648,10 @@ that experience:
|
|||
limit yourself to request-only accesses to the configuration you can
|
||||
reconfigure the object later on as needed.
|
||||
|
||||
3. Make sure to load the configuration very early on, so that
|
||||
extensions can access the configuration when calling ``init_app``.
|
||||
|
||||
|
||||
.. _config-dev-prod:
|
||||
|
||||
Development / Production
|
||||
|
|
@ -536,17 +684,16 @@ An interesting pattern is also to use classes and inheritance for
|
|||
configuration::
|
||||
|
||||
class Config(object):
|
||||
DEBUG = False
|
||||
TESTING = False
|
||||
DATABASE_URI = 'sqlite:///:memory:'
|
||||
|
||||
class ProductionConfig(Config):
|
||||
DATABASE_URI = 'mysql://user@localhost/foo'
|
||||
|
||||
class DevelopmentConfig(Config):
|
||||
DEBUG = True
|
||||
DATABASE_URI = "sqlite:////tmp/foo.db"
|
||||
|
||||
class TestingConfig(Config):
|
||||
DATABASE_URI = 'sqlite:///:memory:'
|
||||
TESTING = True
|
||||
|
||||
To enable such a config you just have to call into
|
||||
|
|
@ -571,13 +718,12 @@ your configuration classes::
|
|||
|
||||
class Config(object):
|
||||
"""Base config, uses staging database server."""
|
||||
DEBUG = False
|
||||
TESTING = False
|
||||
DB_SERVER = '192.168.1.56'
|
||||
|
||||
@property
|
||||
def DATABASE_URI(self): # Note: all caps
|
||||
return 'mysql://user@{}/foo'.format(self.DB_SERVER)
|
||||
def DATABASE_URI(self): # Note: all caps
|
||||
return f"mysql://user@{self.DB_SERVER}/foo"
|
||||
|
||||
class ProductionConfig(Config):
|
||||
"""Uses production database server."""
|
||||
|
|
@ -585,11 +731,9 @@ your configuration classes::
|
|||
|
||||
class DevelopmentConfig(Config):
|
||||
DB_SERVER = 'localhost'
|
||||
DEBUG = True
|
||||
|
||||
class TestingConfig(Config):
|
||||
DB_SERVER = 'localhost'
|
||||
DEBUG = True
|
||||
DATABASE_URI = 'sqlite:///:memory:'
|
||||
|
||||
There are many different ways and it's up to you how you want to manage
|
||||
|
|
@ -605,10 +749,8 @@ your configuration files. However here a list of good recommendations:
|
|||
code at all. If you are working often on different projects you can
|
||||
even create your own script for sourcing that activates a virtualenv
|
||||
and exports the development configuration for you.
|
||||
- Use a tool like `fabric`_ in production to push code and
|
||||
configurations separately to the production server(s). For some
|
||||
details about how to do that, head over to the
|
||||
:ref:`fabric-deployment` pattern.
|
||||
- Use a tool like `fabric`_ to push code and configuration separately
|
||||
to the production server(s).
|
||||
|
||||
.. _fabric: https://www.fabfile.org/
|
||||
|
||||
|
|
@ -658,7 +800,7 @@ locations are used:
|
|||
|
||||
- Installed module or package::
|
||||
|
||||
$PREFIX/lib/python2.X/site-packages/myapp
|
||||
$PREFIX/lib/pythonX.Y/site-packages/myapp
|
||||
$PREFIX/var/myapp-instance
|
||||
|
||||
``$PREFIX`` is the prefix of your Python installation. This can be
|
||||
|
|
|
|||
|
|
@ -1 +1,8 @@
|
|||
.. include:: ../CONTRIBUTING.rst
|
||||
Contributing
|
||||
============
|
||||
|
||||
See the Pallets `detailed contributing documentation <contrib_>`_ for many ways
|
||||
to contribute, including reporting issues, requesting features, asking or
|
||||
answering questions, and making PRs.
|
||||
|
||||
.. _contrib: https://palletsprojects.com/contributing/
|
||||
|
|
|
|||
99
docs/debugging.rst
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
Debugging Application Errors
|
||||
============================
|
||||
|
||||
|
||||
In Production
|
||||
-------------
|
||||
|
||||
**Do not run the development server, or enable the built-in debugger, in
|
||||
a production environment.** The debugger allows executing arbitrary
|
||||
Python code from the browser. It's protected by a pin, but that should
|
||||
not be relied on for security.
|
||||
|
||||
Use an error logging tool, such as Sentry, as described in
|
||||
:ref:`error-logging-tools`, or enable logging and notifications as
|
||||
described in :doc:`/logging`.
|
||||
|
||||
If you have access to the server, you could add some code to start an
|
||||
external debugger if ``request.remote_addr`` matches your IP. Some IDE
|
||||
debuggers also have a remote mode so breakpoints on the server can be
|
||||
interacted with locally. Only enable a debugger temporarily.
|
||||
|
||||
|
||||
The Built-In Debugger
|
||||
---------------------
|
||||
|
||||
The built-in Werkzeug development server provides a debugger which shows
|
||||
an interactive traceback in the browser when an unhandled error occurs
|
||||
during a request. This debugger should only be used during development.
|
||||
|
||||
.. image:: _static/debugger.png
|
||||
:align: center
|
||||
:class: screenshot
|
||||
:alt: screenshot of debugger in action
|
||||
|
||||
.. warning::
|
||||
|
||||
The debugger allows executing arbitrary Python code from the
|
||||
browser. It is protected by a pin, but still represents a major
|
||||
security risk. Do not run the development server or debugger in a
|
||||
production environment.
|
||||
|
||||
The debugger is enabled by default when the development server is run in debug mode.
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ flask --app hello run --debug
|
||||
|
||||
When running from Python code, passing ``debug=True`` enables debug mode, which is
|
||||
mostly equivalent.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
app.run(debug=True)
|
||||
|
||||
:doc:`/server` and :doc:`/cli` have more information about running the debugger and
|
||||
debug mode. More information about the debugger can be found in the `Werkzeug
|
||||
documentation <https://werkzeug.palletsprojects.com/debug/>`__.
|
||||
|
||||
|
||||
External Debuggers
|
||||
------------------
|
||||
|
||||
External debuggers, such as those provided by IDEs, can offer a more
|
||||
powerful debugging experience than the built-in debugger. They can also
|
||||
be used to step through code during a request before an error is raised,
|
||||
or if no error is raised. Some even have a remote mode so you can debug
|
||||
code running on another machine.
|
||||
|
||||
When using an external debugger, the app should still be in debug mode, otherwise Flask
|
||||
turns unhandled errors into generic 500 error pages. However, the built-in debugger and
|
||||
reloader should be disabled so they don't interfere with the external debugger.
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ flask --app hello run --debug --no-debugger --no-reload
|
||||
|
||||
When running from Python:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
app.run(debug=True, use_debugger=False, use_reloader=False)
|
||||
|
||||
Disabling these isn't required, an external debugger will continue to work with the
|
||||
following caveats.
|
||||
|
||||
- If the built-in debugger is not disabled, it will catch unhandled exceptions before
|
||||
the external debugger can.
|
||||
- If the reloader is not disabled, it could cause an unexpected reload if code changes
|
||||
during a breakpoint.
|
||||
- The development server will still catch unhandled exceptions if the built-in
|
||||
debugger is disabled, otherwise it would crash on any error. If you want that (and
|
||||
usually you don't) pass ``passthrough_errors=True`` to ``app.run``.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
app.run(
|
||||
debug=True, passthrough_errors=True,
|
||||
use_debugger=False, use_reloader=False
|
||||
)
|
||||
66
docs/deploying/apache-httpd.rst
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
Apache httpd
|
||||
============
|
||||
|
||||
`Apache httpd`_ is a fast, production level HTTP server. When serving
|
||||
your application with one of the WSGI servers listed in :doc:`index`, it
|
||||
is often good or necessary to put a dedicated HTTP server in front of
|
||||
it. This "reverse proxy" can handle incoming requests, TLS, and other
|
||||
security and performance concerns better than the WSGI server.
|
||||
|
||||
httpd can be installed using your system package manager, or a pre-built
|
||||
executable for Windows. Installing and running httpd itself is outside
|
||||
the scope of this doc. This page outlines the basics of configuring
|
||||
httpd to proxy your application. Be sure to read its documentation to
|
||||
understand what features are available.
|
||||
|
||||
.. _Apache httpd: https://httpd.apache.org/
|
||||
|
||||
|
||||
Domain Name
|
||||
-----------
|
||||
|
||||
Acquiring and configuring a domain name is outside the scope of this
|
||||
doc. In general, you will buy a domain name from a registrar, pay for
|
||||
server space with a hosting provider, and then point your registrar
|
||||
at the hosting provider's name servers.
|
||||
|
||||
To simulate this, you can also edit your ``hosts`` file, located at
|
||||
``/etc/hosts`` on Linux. Add a line that associates a name with the
|
||||
local IP.
|
||||
|
||||
Modern Linux systems may be configured to treat any domain name that
|
||||
ends with ``.localhost`` like this without adding it to the ``hosts``
|
||||
file.
|
||||
|
||||
.. code-block:: python
|
||||
:caption: ``/etc/hosts``
|
||||
|
||||
127.0.0.1 hello.localhost
|
||||
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
The httpd configuration is located at ``/etc/httpd/conf/httpd.conf`` on
|
||||
Linux. It may be different depending on your operating system. Check the
|
||||
docs and look for ``httpd.conf``.
|
||||
|
||||
Remove or comment out any existing ``DocumentRoot`` directive. Add the
|
||||
config lines below. We'll assume the WSGI server is listening locally at
|
||||
``http://127.0.0.1:8000``.
|
||||
|
||||
.. code-block:: apache
|
||||
:caption: ``/etc/httpd/conf/httpd.conf``
|
||||
|
||||
LoadModule proxy_module modules/mod_proxy.so
|
||||
LoadModule proxy_http_module modules/mod_proxy_http.so
|
||||
ProxyPass / http://127.0.0.1:8000/
|
||||
RequestHeader set X-Forwarded-Proto http
|
||||
RequestHeader set X-Forwarded-Prefix /
|
||||
|
||||
The ``LoadModule`` lines might already exist. If so, make sure they are
|
||||
uncommented instead of adding them manually.
|
||||
|
||||
Then :doc:`proxy_fix` so that your application uses the ``X-Forwarded``
|
||||
headers. ``X-Forwarded-For`` and ``X-Forwarded-Host`` are automatically
|
||||
set by ``ProxyPass``.
|
||||
27
docs/deploying/asgi.rst
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
ASGI
|
||||
====
|
||||
|
||||
If you'd like to use an ASGI server you will need to utilise WSGI to
|
||||
ASGI middleware. The asgiref
|
||||
`WsgiToAsgi <https://github.com/django/asgiref#wsgi-to-asgi-adapter>`_
|
||||
adapter is recommended as it integrates with the event loop used for
|
||||
Flask's :ref:`async_await` support. You can use the adapter by
|
||||
wrapping the Flask app,
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from asgiref.wsgi import WsgiToAsgi
|
||||
from flask import Flask
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
...
|
||||
|
||||
asgi_app = WsgiToAsgi(app)
|
||||
|
||||
and then serving the ``asgi_app`` with the ASGI server, e.g. using
|
||||
`Hypercorn <https://github.com/pgjones/hypercorn>`_,
|
||||
|
||||
.. sourcecode:: text
|
||||
|
||||
$ hypercorn module:asgi_app
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
CGI
|
||||
===
|
||||
|
||||
If all other deployment methods do not work, CGI will work for sure.
|
||||
CGI is supported by all major servers but usually has a sub-optimal
|
||||
performance.
|
||||
|
||||
This is also the way you can use a Flask application on Google's `App
|
||||
Engine`_, where execution happens in a CGI-like environment.
|
||||
|
||||
.. admonition:: Watch Out
|
||||
|
||||
Please make sure in advance that any ``app.run()`` calls you might
|
||||
have in your application file are inside an ``if __name__ ==
|
||||
'__main__':`` block or moved to a separate file. Just make sure it's
|
||||
not called because this will always start a local WSGI server which
|
||||
we do not want if we deploy that application to CGI / app engine.
|
||||
|
||||
With CGI, you will also have to make sure that your code does not contain
|
||||
any ``print`` statements, or that ``sys.stdout`` is overridden by something
|
||||
that doesn't write into the HTTP response.
|
||||
|
||||
Creating a `.cgi` file
|
||||
----------------------
|
||||
|
||||
First you need to create the CGI application file. Let's call it
|
||||
:file:`yourapplication.cgi`::
|
||||
|
||||
#!/usr/bin/python
|
||||
from wsgiref.handlers import CGIHandler
|
||||
from yourapplication import app
|
||||
|
||||
CGIHandler().run(app)
|
||||
|
||||
Server Setup
|
||||
------------
|
||||
|
||||
Usually there are two ways to configure the server. Either just copy the
|
||||
``.cgi`` into a :file:`cgi-bin` (and use `mod_rewrite` or something similar to
|
||||
rewrite the URL) or let the server point to the file directly.
|
||||
|
||||
In Apache for example you can put something like this into the config:
|
||||
|
||||
.. sourcecode:: apache
|
||||
|
||||
ScriptAlias /app /path/to/the/application.cgi
|
||||
|
||||
On shared webhosting, though, you might not have access to your Apache config.
|
||||
In this case, a file called ``.htaccess``, sitting in the public directory
|
||||
you want your app to be available, works too but the ``ScriptAlias`` directive
|
||||
won't work in that case:
|
||||
|
||||
.. sourcecode:: apache
|
||||
|
||||
RewriteEngine On
|
||||
RewriteCond %{REQUEST_FILENAME} !-f # Don't interfere with static files
|
||||
RewriteRule ^(.*)$ /path/to/the/application.cgi/$1 [L]
|
||||
|
||||
For more information consult the documentation of your webserver.
|
||||
|
||||
.. _App Engine: https://cloud.google.com/appengine/docs/
|
||||
8
docs/deploying/eventlet.rst
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
:orphan:
|
||||
|
||||
eventlet
|
||||
========
|
||||
|
||||
`Eventlet is no longer maintained.`__ Use :doc:`/deploying/gevent` instead.
|
||||
|
||||
__ https://eventlet.readthedocs.io
|
||||
|
|
@ -1,240 +0,0 @@
|
|||
.. _deploying-fastcgi:
|
||||
|
||||
FastCGI
|
||||
=======
|
||||
|
||||
FastCGI is a deployment option on servers like `nginx`_, `lighttpd`_, and
|
||||
`cherokee`_; see :doc:`uwsgi` and :doc:`wsgi-standalone` for other options.
|
||||
To use your WSGI application with any of them you will need a FastCGI
|
||||
server first. The most popular one is `flup`_ which we will use for
|
||||
this guide. Make sure to have it installed to follow along.
|
||||
|
||||
.. admonition:: Watch Out
|
||||
|
||||
Please make sure in advance that any ``app.run()`` calls you might
|
||||
have in your application file are inside an ``if __name__ ==
|
||||
'__main__':`` block or moved to a separate file. Just make sure it's
|
||||
not called because this will always start a local WSGI server which
|
||||
we do not want if we deploy that application to FastCGI.
|
||||
|
||||
Creating a `.fcgi` file
|
||||
-----------------------
|
||||
|
||||
First you need to create the FastCGI server file. Let's call it
|
||||
`yourapplication.fcgi`::
|
||||
|
||||
#!/usr/bin/python
|
||||
from flup.server.fcgi import WSGIServer
|
||||
from yourapplication import app
|
||||
|
||||
if __name__ == '__main__':
|
||||
WSGIServer(app).run()
|
||||
|
||||
This is enough for Apache to work, however nginx and older versions of
|
||||
lighttpd need a socket to be explicitly passed to communicate with the
|
||||
FastCGI server. For that to work you need to pass the path to the
|
||||
socket to the :class:`~flup.server.fcgi.WSGIServer`::
|
||||
|
||||
WSGIServer(application, bindAddress='/path/to/fcgi.sock').run()
|
||||
|
||||
The path has to be the exact same path you define in the server
|
||||
config.
|
||||
|
||||
Save the :file:`yourapplication.fcgi` file somewhere you will find it again.
|
||||
It makes sense to have that in :file:`/var/www/yourapplication` or something
|
||||
similar.
|
||||
|
||||
Make sure to set the executable bit on that file so that the servers
|
||||
can execute it:
|
||||
|
||||
.. sourcecode:: text
|
||||
|
||||
$ chmod +x /var/www/yourapplication/yourapplication.fcgi
|
||||
|
||||
Configuring Apache
|
||||
------------------
|
||||
|
||||
The example above is good enough for a basic Apache deployment but your
|
||||
`.fcgi` file will appear in your application URL e.g.
|
||||
``example.com/yourapplication.fcgi/news/``. There are few ways to configure
|
||||
your application so that yourapplication.fcgi does not appear in the URL.
|
||||
A preferable way is to use the ScriptAlias and SetHandler configuration
|
||||
directives to route requests to the FastCGI server. The following example
|
||||
uses FastCgiServer to start 5 instances of the application which will
|
||||
handle all incoming requests::
|
||||
|
||||
LoadModule fastcgi_module /usr/lib64/httpd/modules/mod_fastcgi.so
|
||||
|
||||
FastCgiServer /var/www/html/yourapplication/app.fcgi -idle-timeout 300 -processes 5
|
||||
|
||||
<VirtualHost *>
|
||||
ServerName webapp1.mydomain.com
|
||||
DocumentRoot /var/www/html/yourapplication
|
||||
|
||||
AddHandler fastcgi-script fcgi
|
||||
ScriptAlias / /var/www/html/yourapplication/app.fcgi/
|
||||
|
||||
<Location />
|
||||
SetHandler fastcgi-script
|
||||
</Location>
|
||||
</VirtualHost>
|
||||
|
||||
These processes will be managed by Apache. If you're using a standalone
|
||||
FastCGI server, you can use the FastCgiExternalServer directive instead.
|
||||
Note that in the following the path is not real, it's simply used as an
|
||||
identifier to other
|
||||
directives such as AliasMatch::
|
||||
|
||||
FastCgiServer /var/www/html/yourapplication -host 127.0.0.1:3000
|
||||
|
||||
If you cannot set ScriptAlias, for example on a shared web host, you can use
|
||||
WSGI middleware to remove yourapplication.fcgi from the URLs. Set .htaccess::
|
||||
|
||||
<IfModule mod_fcgid.c>
|
||||
AddHandler fcgid-script .fcgi
|
||||
<Files ~ (\.fcgi)>
|
||||
SetHandler fcgid-script
|
||||
Options +FollowSymLinks +ExecCGI
|
||||
</Files>
|
||||
</IfModule>
|
||||
|
||||
<IfModule mod_rewrite.c>
|
||||
Options +FollowSymlinks
|
||||
RewriteEngine On
|
||||
RewriteBase /
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteRule ^(.*)$ yourapplication.fcgi/$1 [QSA,L]
|
||||
</IfModule>
|
||||
|
||||
Set yourapplication.fcgi::
|
||||
|
||||
#!/usr/bin/python
|
||||
#: optional path to your local python site-packages folder
|
||||
import sys
|
||||
sys.path.insert(0, '<your_local_path>/lib/python<your_python_version>/site-packages')
|
||||
|
||||
from flup.server.fcgi import WSGIServer
|
||||
from yourapplication import app
|
||||
|
||||
class ScriptNameStripper(object):
|
||||
def __init__(self, app):
|
||||
self.app = app
|
||||
|
||||
def __call__(self, environ, start_response):
|
||||
environ['SCRIPT_NAME'] = ''
|
||||
return self.app(environ, start_response)
|
||||
|
||||
app = ScriptNameStripper(app)
|
||||
|
||||
if __name__ == '__main__':
|
||||
WSGIServer(app).run()
|
||||
|
||||
Configuring lighttpd
|
||||
--------------------
|
||||
|
||||
A basic FastCGI configuration for lighttpd looks like that::
|
||||
|
||||
fastcgi.server = ("/yourapplication.fcgi" =>
|
||||
((
|
||||
"socket" => "/tmp/yourapplication-fcgi.sock",
|
||||
"bin-path" => "/var/www/yourapplication/yourapplication.fcgi",
|
||||
"check-local" => "disable",
|
||||
"max-procs" => 1
|
||||
))
|
||||
)
|
||||
|
||||
alias.url = (
|
||||
"/static/" => "/path/to/your/static/"
|
||||
)
|
||||
|
||||
url.rewrite-once = (
|
||||
"^(/static($|/.*))$" => "$1",
|
||||
"^(/.*)$" => "/yourapplication.fcgi$1"
|
||||
)
|
||||
|
||||
Remember to enable the FastCGI, alias and rewrite modules. This configuration
|
||||
binds the application to ``/yourapplication``. If you want the application to
|
||||
work in the URL root you have to work around a lighttpd bug with the
|
||||
:class:`~werkzeug.contrib.fixers.LighttpdCGIRootFix` middleware.
|
||||
|
||||
Make sure to apply it only if you are mounting the application the URL
|
||||
root. Also, see the Lighty docs for more information on `FastCGI and Python
|
||||
<https://redmine.lighttpd.net/projects/lighttpd/wiki/Docs_ModFastCGI>`_ (note that
|
||||
explicitly passing a socket to run() is no longer necessary).
|
||||
|
||||
Configuring nginx
|
||||
-----------------
|
||||
|
||||
Installing FastCGI applications on nginx is a bit different because by
|
||||
default no FastCGI parameters are forwarded.
|
||||
|
||||
A basic Flask FastCGI configuration for nginx looks like this::
|
||||
|
||||
location = /yourapplication { rewrite ^ /yourapplication/ last; }
|
||||
location /yourapplication { try_files $uri @yourapplication; }
|
||||
location @yourapplication {
|
||||
include fastcgi_params;
|
||||
fastcgi_split_path_info ^(/yourapplication)(.*)$;
|
||||
fastcgi_param PATH_INFO $fastcgi_path_info;
|
||||
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
|
||||
fastcgi_pass unix:/tmp/yourapplication-fcgi.sock;
|
||||
}
|
||||
|
||||
This configuration binds the application to ``/yourapplication``. If you
|
||||
want to have it in the URL root it's a bit simpler because you don't
|
||||
have to figure out how to calculate ``PATH_INFO`` and ``SCRIPT_NAME``::
|
||||
|
||||
location / { try_files $uri @yourapplication; }
|
||||
location @yourapplication {
|
||||
include fastcgi_params;
|
||||
fastcgi_param PATH_INFO $fastcgi_script_name;
|
||||
fastcgi_param SCRIPT_NAME "";
|
||||
fastcgi_pass unix:/tmp/yourapplication-fcgi.sock;
|
||||
}
|
||||
|
||||
Running FastCGI Processes
|
||||
-------------------------
|
||||
|
||||
Since nginx and others do not load FastCGI apps, you have to do it by
|
||||
yourself. `Supervisor can manage FastCGI processes.
|
||||
<http://supervisord.org/configuration.html#fcgi-program-x-section-settings>`_
|
||||
You can look around for other FastCGI process managers or write a script
|
||||
to run your `.fcgi` file at boot, e.g. using a SysV ``init.d`` script.
|
||||
For a temporary solution, you can always run the ``.fcgi`` script inside
|
||||
GNU screen. See ``man screen`` for details, and note that this is a
|
||||
manual solution which does not persist across system restart::
|
||||
|
||||
$ screen
|
||||
$ /var/www/yourapplication/yourapplication.fcgi
|
||||
|
||||
Debugging
|
||||
---------
|
||||
|
||||
FastCGI deployments tend to be hard to debug on most web servers. Very
|
||||
often the only thing the server log tells you is something along the
|
||||
lines of "premature end of headers". In order to debug the application
|
||||
the only thing that can really give you ideas why it breaks is switching
|
||||
to the correct user and executing the application by hand.
|
||||
|
||||
This example assumes your application is called `application.fcgi` and
|
||||
that your web server user is `www-data`::
|
||||
|
||||
$ su www-data
|
||||
$ cd /var/www/yourapplication
|
||||
$ python application.fcgi
|
||||
Traceback (most recent call last):
|
||||
File "yourapplication.fcgi", line 4, in <module>
|
||||
ImportError: No module named yourapplication
|
||||
|
||||
In this case the error seems to be "yourapplication" not being on the
|
||||
python path. Common problems are:
|
||||
|
||||
- Relative paths being used. Don't rely on the current working directory.
|
||||
- The code depending on environment variables that are not set by the
|
||||
web server.
|
||||
- Different python interpreters being used.
|
||||
|
||||
.. _nginx: https://nginx.org/
|
||||
.. _lighttpd: https://www.lighttpd.net/
|
||||
.. _cherokee: http://cherokee-project.com/
|
||||
.. _flup: https://pypi.org/project/flup/
|
||||
76
docs/deploying/gevent.rst
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
gevent
|
||||
======
|
||||
|
||||
Prefer using :doc:`gunicorn` or :doc:`uwsgi` with gevent workers rather
|
||||
than using `gevent`_ directly. Gunicorn and uWSGI provide much more
|
||||
configurable and production-tested servers.
|
||||
|
||||
`gevent`_ allows writing asynchronous, coroutine-based code that looks
|
||||
like standard synchronous Python. It uses `greenlet`_ to enable task
|
||||
switching without writing ``async/await`` or using ``asyncio``. This is
|
||||
not the same as Python's ``async/await``, or the ASGI server spec.
|
||||
|
||||
gevent provides a WSGI server that can handle many connections at once
|
||||
instead of one per worker process. See :doc:`/gevent` for more
|
||||
information about enabling it in your application.
|
||||
|
||||
.. _gevent: https://www.gevent.org/
|
||||
.. _greenlet: https://greenlet.readthedocs.io/en/latest/
|
||||
|
||||
|
||||
Installing
|
||||
----------
|
||||
|
||||
When using gevent, greenlet>=1.0 is required. When using PyPy,
|
||||
PyPy>=7.3.7 is required.
|
||||
|
||||
Create a virtualenv, install your application, then install ``gevent``.
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ cd hello-app
|
||||
$ python -m venv .venv
|
||||
$ . .venv/bin/activate
|
||||
$ pip install . # install your application
|
||||
$ pip install gevent
|
||||
|
||||
|
||||
Running
|
||||
-------
|
||||
|
||||
To use gevent to serve your application, write a script that imports its
|
||||
``WSGIServer``, as well as your app or app factory.
|
||||
|
||||
.. code-block:: python
|
||||
:caption: ``wsgi.py``
|
||||
|
||||
from gevent.pywsgi import WSGIServer
|
||||
from hello import create_app
|
||||
|
||||
app = create_app()
|
||||
http_server = WSGIServer(("127.0.0.1", 8000), app)
|
||||
http_server.serve_forever()
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ python wsgi.py
|
||||
|
||||
No output is shown when the server starts.
|
||||
|
||||
|
||||
Binding Externally
|
||||
------------------
|
||||
|
||||
gevent should not be run as root because it would cause your
|
||||
application code to run as root, which is not secure. However, this
|
||||
means it will not be possible to bind to port 80 or 443. Instead, a
|
||||
reverse proxy such as :doc:`nginx` or :doc:`apache-httpd` should be used
|
||||
in front of gevent.
|
||||
|
||||
You can bind to all external IPs on a non-privileged port by using
|
||||
``0.0.0.0`` in the server arguments shown in the previous section. Don't
|
||||
do this when using a reverse proxy setup, otherwise it will be possible
|
||||
to bypass the proxy.
|
||||
|
||||
``0.0.0.0`` is not a valid address to navigate to, you'd use a specific
|
||||
IP address in your browser.
|
||||
116
docs/deploying/gunicorn.rst
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
Gunicorn
|
||||
========
|
||||
|
||||
`Gunicorn`_ is a pure Python WSGI server with simple configuration and
|
||||
multiple worker implementations for performance tuning.
|
||||
|
||||
* It tends to integrate easily with hosting platforms.
|
||||
* It does not support Windows (but does run on WSL).
|
||||
* It is easy to install as it does not require additional dependencies
|
||||
or compilation.
|
||||
* It has built-in async worker support using gevent.
|
||||
|
||||
This page outlines the basics of running Gunicorn. Be sure to read its
|
||||
`documentation`_ and use ``gunicorn --help`` to understand what features
|
||||
are available.
|
||||
|
||||
.. _Gunicorn: https://gunicorn.org/
|
||||
.. _documentation: https://docs.gunicorn.org/
|
||||
|
||||
|
||||
Installing
|
||||
----------
|
||||
|
||||
Gunicorn is easy to install, as it does not require external
|
||||
dependencies or compilation. It runs on Windows only under WSL.
|
||||
|
||||
Create a virtualenv, install your application, then install
|
||||
``gunicorn``.
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ cd hello-app
|
||||
$ python -m venv .venv
|
||||
$ . .venv/bin/activate
|
||||
$ pip install . # install your application
|
||||
$ pip install gunicorn
|
||||
|
||||
|
||||
Running
|
||||
-------
|
||||
|
||||
The only required argument to Gunicorn tells it how to load your Flask
|
||||
application. The syntax is ``{module_import}:{app_variable}``.
|
||||
``module_import`` is the dotted import name to the module with your
|
||||
application. ``app_variable`` is the variable with the application. It
|
||||
can also be a function call (with any arguments) if you're using the
|
||||
app factory pattern.
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
# equivalent to 'from hello import app'
|
||||
$ gunicorn -w 4 'hello:app'
|
||||
|
||||
# equivalent to 'from hello import create_app; create_app()'
|
||||
$ gunicorn -w 4 'hello:create_app()'
|
||||
|
||||
Starting gunicorn 20.1.0
|
||||
Listening at: http://127.0.0.1:8000 (x)
|
||||
Using worker: sync
|
||||
Booting worker with pid: x
|
||||
Booting worker with pid: x
|
||||
Booting worker with pid: x
|
||||
Booting worker with pid: x
|
||||
|
||||
The ``-w`` option specifies the number of processes to run; a starting
|
||||
value could be ``CPU * 2``. The default is only 1 worker, which is
|
||||
probably not what you want for the default worker type.
|
||||
|
||||
Logs for each request aren't shown by default, only worker info and
|
||||
errors are shown. To show access logs on stdout, use the
|
||||
``--access-logfile=-`` option.
|
||||
|
||||
|
||||
Binding Externally
|
||||
------------------
|
||||
|
||||
Gunicorn should not be run as root because it would cause your
|
||||
application code to run as root, which is not secure. However, this
|
||||
means it will not be possible to bind to port 80 or 443. Instead, a
|
||||
reverse proxy such as :doc:`nginx` or :doc:`apache-httpd` should be used
|
||||
in front of Gunicorn.
|
||||
|
||||
You can bind to all external IPs on a non-privileged port using the
|
||||
``-b 0.0.0.0`` option. Don't do this when using a reverse proxy setup,
|
||||
otherwise it will be possible to bypass the proxy.
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ gunicorn -w 4 -b 0.0.0.0 'hello:create_app()'
|
||||
Listening at: http://0.0.0.0:8000 (x)
|
||||
|
||||
``0.0.0.0`` is not a valid address to navigate to, you'd use a specific
|
||||
IP address in your browser.
|
||||
|
||||
|
||||
Async with gevent
|
||||
-----------------
|
||||
|
||||
The default sync worker is appropriate for most use cases. If you need numerous,
|
||||
long running, concurrent connections, Gunicorn provides an asynchronous worker
|
||||
using `gevent`_. This is not the same as Python's ``async/await``, or the ASGI
|
||||
server spec. See :doc:`/gevent` for more information about enabling it in your
|
||||
application.
|
||||
|
||||
.. _gevent: https://www.gevent.org/
|
||||
|
||||
When using gevent, greenlet>=1.0 is required. When using PyPy, PyPy>=7.3.7 is
|
||||
required.
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ gunicorn -k gevent 'hello:create_app()'
|
||||
Starting gunicorn 20.1.0
|
||||
Listening at: http://127.0.0.1:8000 (x)
|
||||
Using worker: gevent
|
||||
Booting worker with pid: x
|
||||
|
|
@ -1,35 +1,78 @@
|
|||
.. _deployment:
|
||||
Deploying to Production
|
||||
=======================
|
||||
|
||||
Deployment Options
|
||||
==================
|
||||
After developing your application, you'll want to make it available
|
||||
publicly to other users. When you're developing locally, you're probably
|
||||
using the built-in development server, debugger, and reloader. These
|
||||
should not be used in production. Instead, you should use a dedicated
|
||||
WSGI server or hosting platform, some of which will be described here.
|
||||
|
||||
While lightweight and easy to use, **Flask's built-in server is not suitable
|
||||
for production** as it doesn't scale well. Some of the options available for
|
||||
properly running Flask in production are documented here.
|
||||
"Production" means "not development", which applies whether you're
|
||||
serving your application publicly to millions of users or privately /
|
||||
locally to a single user. **Do not use the development server when
|
||||
deploying to production. It is intended for use only during local
|
||||
development. It is not designed to be particularly secure, stable, or
|
||||
efficient.**
|
||||
|
||||
If you want to deploy your Flask application to a WSGI server not listed here,
|
||||
look up the server documentation about how to use a WSGI app with it. Just
|
||||
remember that your :class:`Flask` application object is the actual WSGI
|
||||
application.
|
||||
|
||||
|
||||
Hosted options
|
||||
--------------
|
||||
|
||||
- `Deploying Flask on Heroku <https://devcenter.heroku.com/articles/getting-started-with-python>`_
|
||||
- `Deploying Flask on Google App Engine <https://cloud.google.com/appengine/docs/standard/python3/runtime>`_
|
||||
- `Deploying Flask on AWS Elastic Beanstalk <https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/create-deploy-python-flask.html>`_
|
||||
- `Deploying on Azure (IIS) <https://docs.microsoft.com/en-us/azure/app-service/containers/how-to-configure-python>`_
|
||||
- `Deploying on PythonAnywhere <https://help.pythonanywhere.com/pages/Flask/>`_
|
||||
|
||||
Self-hosted options
|
||||
Self-Hosted Options
|
||||
-------------------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
Flask is a WSGI *application*. A WSGI *server* is used to run the
|
||||
application, converting incoming HTTP requests to the standard WSGI
|
||||
environ, and converting outgoing WSGI responses to HTTP responses.
|
||||
|
||||
wsgi-standalone
|
||||
uwsgi
|
||||
mod_wsgi
|
||||
fastcgi
|
||||
cgi
|
||||
The primary goal of these docs is to familiarize you with the concepts
|
||||
involved in running a WSGI application using a production WSGI server
|
||||
and HTTP server. There are many WSGI servers and HTTP servers, with many
|
||||
configuration possibilities. The pages below discuss the most common
|
||||
servers, and show the basics of running each one. The next section
|
||||
discusses platforms that can manage this for you.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
gunicorn
|
||||
waitress
|
||||
mod_wsgi
|
||||
uwsgi
|
||||
gevent
|
||||
asgi
|
||||
|
||||
WSGI servers have HTTP servers built-in. However, a dedicated HTTP
|
||||
server may be safer, more efficient, or more capable. Putting an HTTP
|
||||
server in front of the WSGI server is called a "reverse proxy."
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
proxy_fix
|
||||
nginx
|
||||
apache-httpd
|
||||
|
||||
This list is not exhaustive, and you should evaluate these and other
|
||||
servers based on your application's needs. Different servers will have
|
||||
different capabilities, configuration, and support.
|
||||
|
||||
|
||||
Hosting Platforms
|
||||
-----------------
|
||||
|
||||
There are many services available for hosting web applications without
|
||||
needing to maintain your own server, networking, domain, etc. Some
|
||||
services may have a free tier up to a certain time or bandwidth. Many of
|
||||
these services use one of the WSGI servers described above, or a similar
|
||||
interface. The links below are for some of the most common platforms,
|
||||
which have instructions for Flask, WSGI, or Python.
|
||||
|
||||
- `PythonAnywhere <https://help.pythonanywhere.com/pages/Flask/>`_
|
||||
- `Google App Engine <https://cloud.google.com/appengine/docs/standard/python3/building-app>`_
|
||||
- `Google Cloud Run <https://cloud.google.com/run/docs/quickstarts/build-and-deploy/deploy-python-service>`_
|
||||
- `AWS Elastic Beanstalk <https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/create-deploy-python-flask.html>`_
|
||||
- `Microsoft Azure <https://docs.microsoft.com/en-us/azure/app-service/quickstart-python>`_
|
||||
|
||||
This list is not exhaustive, and you should evaluate these and other
|
||||
services based on your application's needs. Different services will have
|
||||
different capabilities, configuration, pricing, and support.
|
||||
|
||||
You'll probably need to :doc:`proxy_fix` when using most hosting
|
||||
platforms.
|
||||
|
|
|
|||
|
|
@ -1,223 +1,94 @@
|
|||
.. _mod_wsgi-deployment:
|
||||
mod_wsgi
|
||||
========
|
||||
|
||||
mod_wsgi (Apache)
|
||||
=================
|
||||
`mod_wsgi`_ is a WSGI server integrated with the `Apache httpd`_ server.
|
||||
The modern `mod_wsgi-express`_ command makes it easy to configure and
|
||||
start the server without needing to write Apache httpd configuration.
|
||||
|
||||
If you are using the `Apache`_ webserver, consider using `mod_wsgi`_.
|
||||
* Tightly integrated with Apache httpd.
|
||||
* Supports Windows directly.
|
||||
* Requires a compiler and the Apache development headers to install.
|
||||
* Does not require a reverse proxy setup.
|
||||
|
||||
.. admonition:: Watch Out
|
||||
This page outlines the basics of running mod_wsgi-express, not the more
|
||||
complex installation and configuration with httpd. Be sure to read the
|
||||
`mod_wsgi-express`_, `mod_wsgi`_, and `Apache httpd`_ documentation to
|
||||
understand what features are available.
|
||||
|
||||
Please make sure in advance that any ``app.run()`` calls you might
|
||||
have in your application file are inside an ``if __name__ ==
|
||||
'__main__':`` block or moved to a separate file. Just make sure it's
|
||||
not called because this will always start a local WSGI server which
|
||||
we do not want if we deploy that application to mod_wsgi.
|
||||
.. _mod_wsgi-express: https://pypi.org/project/mod-wsgi/
|
||||
.. _mod_wsgi: https://modwsgi.readthedocs.io/
|
||||
.. _Apache httpd: https://httpd.apache.org/
|
||||
|
||||
.. _Apache: https://httpd.apache.org/
|
||||
|
||||
Installing `mod_wsgi`
|
||||
---------------------
|
||||
Installing
|
||||
----------
|
||||
|
||||
If you don't have `mod_wsgi` installed yet you have to either install it
|
||||
using a package manager or compile it yourself. The mod_wsgi
|
||||
`installation instructions`_ cover source installations on UNIX systems.
|
||||
Installing mod_wsgi requires a compiler and the Apache server and
|
||||
development headers installed. You will get an error if they are not.
|
||||
How to install them depends on the OS and package manager that you use.
|
||||
|
||||
If you are using Ubuntu/Debian you can apt-get it and activate it as
|
||||
follows:
|
||||
Create a virtualenv, install your application, then install
|
||||
``mod_wsgi``.
|
||||
|
||||
.. sourcecode:: text
|
||||
.. code-block:: text
|
||||
|
||||
$ apt-get install libapache2-mod-wsgi-py3
|
||||
$ cd hello-app
|
||||
$ python -m venv .venv
|
||||
$ . .venv/bin/activate
|
||||
$ pip install . # install your application
|
||||
$ pip install mod_wsgi
|
||||
|
||||
If you are using a yum based distribution (Fedora, OpenSUSE, etc..) you
|
||||
can install it as follows:
|
||||
|
||||
.. sourcecode:: text
|
||||
Running
|
||||
-------
|
||||
|
||||
$ yum install mod_wsgi
|
||||
The only argument to ``mod_wsgi-express`` specifies a script containing
|
||||
your Flask application, which must be called ``application``. You can
|
||||
write a small script to import your app with this name, or to create it
|
||||
if using the app factory pattern.
|
||||
|
||||
On FreeBSD install `mod_wsgi` by compiling the `www/mod_wsgi` port or by
|
||||
using pkg_add:
|
||||
.. code-block:: python
|
||||
:caption: ``wsgi.py``
|
||||
|
||||
.. sourcecode:: text
|
||||
from hello import app
|
||||
|
||||
$ pkg install ap24-py37-mod_wsgi
|
||||
application = app
|
||||
|
||||
If you are using pkgsrc you can install `mod_wsgi` by compiling the
|
||||
`www/ap2-wsgi` package.
|
||||
.. code-block:: python
|
||||
:caption: ``wsgi.py``
|
||||
|
||||
If you encounter segfaulting child processes after the first apache
|
||||
reload you can safely ignore them. Just restart the server.
|
||||
from hello import create_app
|
||||
|
||||
Creating a `.wsgi` file
|
||||
-----------------------
|
||||
|
||||
To run your application you need a :file:`yourapplication.wsgi` file.
|
||||
This file contains the code `mod_wsgi` is executing on startup
|
||||
to get the application object. The object called `application`
|
||||
in that file is then used as application.
|
||||
|
||||
For most applications the following file should be sufficient::
|
||||
|
||||
from yourapplication import app as application
|
||||
|
||||
If a factory function is used in a :file:`__init__.py` file, then the function should be imported::
|
||||
|
||||
from yourapplication import create_app
|
||||
application = create_app()
|
||||
|
||||
If you don't have a factory function for application creation but a singleton
|
||||
instance you can directly import that one as `application`.
|
||||
Now run the ``mod_wsgi-express start-server`` command.
|
||||
|
||||
Store that file somewhere that you will find it again (e.g.:
|
||||
:file:`/var/www/yourapplication`) and make sure that `yourapplication` and all
|
||||
the libraries that are in use are on the python load path. If you don't
|
||||
want to install it system wide consider using a `virtual python`_
|
||||
instance. Keep in mind that you will have to actually install your
|
||||
application into the virtualenv as well. Alternatively there is the
|
||||
option to just patch the path in the ``.wsgi`` file before the import::
|
||||
.. code-block:: text
|
||||
|
||||
import sys
|
||||
sys.path.insert(0, '/path/to/the/application')
|
||||
$ mod_wsgi-express start-server wsgi.py --processes 4
|
||||
|
||||
Configuring Apache
|
||||
The ``--processes`` option specifies the number of worker processes to
|
||||
run; a starting value could be ``CPU * 2``.
|
||||
|
||||
Logs for each request aren't show in the terminal. If an error occurs,
|
||||
its information is written to the error log file shown when starting the
|
||||
server.
|
||||
|
||||
|
||||
Binding Externally
|
||||
------------------
|
||||
|
||||
The last thing you have to do is to create an Apache configuration file
|
||||
for your application. In this example we are telling `mod_wsgi` to
|
||||
execute the application under a different user for security reasons:
|
||||
Unlike the other WSGI servers in these docs, mod_wsgi can be run as
|
||||
root to bind to privileged ports like 80 and 443. However, it must be
|
||||
configured to drop permissions to a different user and group for the
|
||||
worker processes.
|
||||
|
||||
.. sourcecode:: apache
|
||||
For example, if you created a ``hello`` user and group, you should
|
||||
install your virtualenv and application as that user, then tell
|
||||
mod_wsgi to drop to that user after starting.
|
||||
|
||||
<VirtualHost *>
|
||||
ServerName example.com
|
||||
.. code-block:: text
|
||||
|
||||
WSGIDaemonProcess yourapplication user=user1 group=group1 threads=5
|
||||
WSGIScriptAlias / /var/www/yourapplication/yourapplication.wsgi
|
||||
|
||||
<Directory /var/www/yourapplication>
|
||||
WSGIProcessGroup yourapplication
|
||||
WSGIApplicationGroup %{GLOBAL}
|
||||
Order deny,allow
|
||||
Allow from all
|
||||
</Directory>
|
||||
</VirtualHost>
|
||||
|
||||
Note: WSGIDaemonProcess isn't implemented in Windows and Apache will
|
||||
refuse to run with the above configuration. On a Windows system, eliminate those lines:
|
||||
|
||||
.. sourcecode:: apache
|
||||
|
||||
<VirtualHost *>
|
||||
ServerName example.com
|
||||
WSGIScriptAlias / C:\yourdir\yourapp.wsgi
|
||||
<Directory C:\yourdir>
|
||||
Order deny,allow
|
||||
Allow from all
|
||||
</Directory>
|
||||
</VirtualHost>
|
||||
|
||||
Note: There have been some changes in access control configuration
|
||||
for `Apache 2.4`_.
|
||||
|
||||
.. _Apache 2.4: https://httpd.apache.org/docs/trunk/upgrading.html
|
||||
|
||||
Most notably, the syntax for directory permissions has changed from httpd 2.2
|
||||
|
||||
.. sourcecode:: apache
|
||||
|
||||
Order allow,deny
|
||||
Allow from all
|
||||
|
||||
to httpd 2.4 syntax
|
||||
|
||||
.. sourcecode:: apache
|
||||
|
||||
Require all granted
|
||||
|
||||
|
||||
For more information consult the `mod_wsgi documentation`_.
|
||||
|
||||
.. _mod_wsgi: https://github.com/GrahamDumpleton/mod_wsgi
|
||||
.. _installation instructions: https://modwsgi.readthedocs.io/en/develop/installation.html
|
||||
.. _virtual python: https://pypi.org/project/virtualenv/
|
||||
.. _mod_wsgi documentation: https://modwsgi.readthedocs.io/en/develop/index.html
|
||||
|
||||
Troubleshooting
|
||||
---------------
|
||||
|
||||
If your application does not run, follow this guide to troubleshoot:
|
||||
|
||||
**Problem:** application does not run, errorlog shows SystemExit ignored
|
||||
You have an ``app.run()`` call in your application file that is not
|
||||
guarded by an ``if __name__ == '__main__':`` condition. Either
|
||||
remove that :meth:`~flask.Flask.run` call from the file and move it
|
||||
into a separate :file:`run.py` file or put it into such an if block.
|
||||
|
||||
**Problem:** application gives permission errors
|
||||
Probably caused by your application running as the wrong user. Make
|
||||
sure the folders the application needs access to have the proper
|
||||
privileges set and the application runs as the correct user
|
||||
(``user`` and ``group`` parameter to the `WSGIDaemonProcess`
|
||||
directive)
|
||||
|
||||
**Problem:** application dies with an error on print
|
||||
Keep in mind that mod_wsgi disallows doing anything with
|
||||
:data:`sys.stdout` and :data:`sys.stderr`. You can disable this
|
||||
protection from the config by setting the `WSGIRestrictStdout` to
|
||||
``off``:
|
||||
|
||||
.. sourcecode:: apache
|
||||
|
||||
WSGIRestrictStdout Off
|
||||
|
||||
Alternatively you can also replace the standard out in the .wsgi file
|
||||
with a different stream::
|
||||
|
||||
import sys
|
||||
sys.stdout = sys.stderr
|
||||
|
||||
**Problem:** accessing resources gives IO errors
|
||||
Your application probably is a single .py file you symlinked into
|
||||
the site-packages folder. Please be aware that this does not work,
|
||||
instead you either have to put the folder into the pythonpath the
|
||||
file is stored in, or convert your application into a package.
|
||||
|
||||
The reason for this is that for non-installed packages, the module
|
||||
filename is used to locate the resources and for symlinks the wrong
|
||||
filename is picked up.
|
||||
|
||||
Support for Automatic Reloading
|
||||
-------------------------------
|
||||
|
||||
To help deployment tools you can activate support for automatic
|
||||
reloading. Whenever something changes the ``.wsgi`` file, `mod_wsgi` will
|
||||
reload all the daemon processes for us.
|
||||
|
||||
For that, just add the following directive to your `Directory` section:
|
||||
|
||||
.. sourcecode:: apache
|
||||
|
||||
WSGIScriptReloading On
|
||||
|
||||
Working with Virtual Environments
|
||||
---------------------------------
|
||||
|
||||
Virtual environments have the advantage that they never install the
|
||||
required dependencies system wide so you have a better control over what
|
||||
is used where. If you want to use a virtual environment with mod_wsgi
|
||||
you have to modify your ``.wsgi`` file slightly.
|
||||
|
||||
Add the following lines to the top of your ``.wsgi`` file::
|
||||
|
||||
activate_this = '/path/to/env/bin/activate_this.py'
|
||||
execfile(activate_this, dict(__file__=activate_this))
|
||||
|
||||
For Python 3 add the following lines to the top of your ``.wsgi`` file::
|
||||
|
||||
activate_this = '/path/to/env/bin/activate_this.py'
|
||||
with open(activate_this) as file_:
|
||||
exec(file_.read(), dict(__file__=activate_this))
|
||||
|
||||
This sets up the load paths according to the settings of the virtual
|
||||
environment. Keep in mind that the path has to be absolute.
|
||||
$ sudo /home/hello/.venv/bin/mod_wsgi-express start-server \
|
||||
/home/hello/wsgi.py \
|
||||
--user hello --group hello --port 80 --processes 4
|
||||
|
|
|
|||
69
docs/deploying/nginx.rst
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
nginx
|
||||
=====
|
||||
|
||||
`nginx`_ is a fast, production level HTTP server. When serving your
|
||||
application with one of the WSGI servers listed in :doc:`index`, it is
|
||||
often good or necessary to put a dedicated HTTP server in front of it.
|
||||
This "reverse proxy" can handle incoming requests, TLS, and other
|
||||
security and performance concerns better than the WSGI server.
|
||||
|
||||
Nginx can be installed using your system package manager, or a pre-built
|
||||
executable for Windows. Installing and running Nginx itself is outside
|
||||
the scope of this doc. This page outlines the basics of configuring
|
||||
Nginx to proxy your application. Be sure to read its documentation to
|
||||
understand what features are available.
|
||||
|
||||
.. _nginx: https://nginx.org/
|
||||
|
||||
|
||||
Domain Name
|
||||
-----------
|
||||
|
||||
Acquiring and configuring a domain name is outside the scope of this
|
||||
doc. In general, you will buy a domain name from a registrar, pay for
|
||||
server space with a hosting provider, and then point your registrar
|
||||
at the hosting provider's name servers.
|
||||
|
||||
To simulate this, you can also edit your ``hosts`` file, located at
|
||||
``/etc/hosts`` on Linux. Add a line that associates a name with the
|
||||
local IP.
|
||||
|
||||
Modern Linux systems may be configured to treat any domain name that
|
||||
ends with ``.localhost`` like this without adding it to the ``hosts``
|
||||
file.
|
||||
|
||||
.. code-block:: python
|
||||
:caption: ``/etc/hosts``
|
||||
|
||||
127.0.0.1 hello.localhost
|
||||
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
The nginx configuration is located at ``/etc/nginx/nginx.conf`` on
|
||||
Linux. It may be different depending on your operating system. Check the
|
||||
docs and look for ``nginx.conf``.
|
||||
|
||||
Remove or comment out any existing ``server`` section. Add a ``server``
|
||||
section and use the ``proxy_pass`` directive to point to the address the
|
||||
WSGI server is listening on. We'll assume the WSGI server is listening
|
||||
locally at ``http://127.0.0.1:8000``.
|
||||
|
||||
.. code-block:: nginx
|
||||
:caption: ``/etc/nginx.conf``
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:8000/;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
proxy_set_header X-Forwarded-Prefix /;
|
||||
}
|
||||
}
|
||||
|
||||
Then :doc:`proxy_fix` so that your application uses these headers.
|
||||
33
docs/deploying/proxy_fix.rst
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
Tell Flask it is Behind a Proxy
|
||||
===============================
|
||||
|
||||
When using a reverse proxy, or many Python hosting platforms, the proxy
|
||||
will intercept and forward all external requests to the local WSGI
|
||||
server.
|
||||
|
||||
From the WSGI server and Flask application's perspectives, requests are
|
||||
now coming from the HTTP server to the local address, rather than from
|
||||
the remote address to the external server address.
|
||||
|
||||
HTTP servers should set ``X-Forwarded-`` headers to pass on the real
|
||||
values to the application. The application can then be told to trust and
|
||||
use those values by wrapping it with the
|
||||
:doc:`werkzeug:middleware/proxy_fix` middleware provided by Werkzeug.
|
||||
|
||||
This middleware should only be used if the application is actually
|
||||
behind a proxy, and should be configured with the number of proxies that
|
||||
are chained in front of it. Not all proxies set all the headers. Since
|
||||
incoming headers can be faked, you must set how many proxies are setting
|
||||
each header so the middleware knows what to trust.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from werkzeug.middleware.proxy_fix import ProxyFix
|
||||
|
||||
app.wsgi_app = ProxyFix(
|
||||
app.wsgi_app, x_for=1, x_proto=1, x_host=1, x_prefix=1
|
||||
)
|
||||
|
||||
Remember, only apply this middleware if you are behind a proxy, and set
|
||||
the correct number of proxies that set each header. It can be a security
|
||||
issue if you get this configuration wrong.
|
||||
|
|
@ -1,73 +1,143 @@
|
|||
.. _deploying-uwsgi:
|
||||
|
||||
uWSGI
|
||||
=====
|
||||
|
||||
uWSGI is a deployment option on servers like `nginx`_, `lighttpd`_, and
|
||||
`cherokee`_; see :doc:`fastcgi` and :doc:`wsgi-standalone` for other options.
|
||||
To use your WSGI application with uWSGI protocol you will need a uWSGI server
|
||||
first. uWSGI is both a protocol and an application server; the application
|
||||
server can serve uWSGI, FastCGI, and HTTP protocols.
|
||||
`uWSGI`_ is a fast, compiled server suite with extensive configuration
|
||||
and capabilities beyond a basic server.
|
||||
|
||||
The most popular uWSGI server is `uwsgi`_, which we will use for this
|
||||
guide. Make sure to have it installed to follow along.
|
||||
* It can be very performant due to being a compiled program.
|
||||
* It is complex to configure beyond the basic application, and has so
|
||||
many options that it can be difficult for beginners to understand.
|
||||
* It does not support Windows (but does run on WSL).
|
||||
* It requires a compiler to install in some cases.
|
||||
|
||||
.. admonition:: Watch Out
|
||||
This page outlines the basics of running uWSGI. Be sure to read its
|
||||
documentation to understand what features are available.
|
||||
|
||||
Please make sure in advance that any ``app.run()`` calls you might
|
||||
have in your application file are inside an ``if __name__ ==
|
||||
'__main__':`` block or moved to a separate file. Just make sure it's
|
||||
not called because this will always start a local WSGI server which
|
||||
we do not want if we deploy that application to uWSGI.
|
||||
.. _uWSGI: https://uwsgi-docs.readthedocs.io/en/latest/
|
||||
|
||||
Starting your app with uwsgi
|
||||
----------------------------
|
||||
|
||||
`uwsgi` is designed to operate on WSGI callables found in python modules.
|
||||
Installing
|
||||
----------
|
||||
|
||||
Given a flask application in myapp.py, use the following command:
|
||||
uWSGI has multiple ways to install it. The most straightforward is to
|
||||
install the ``pyuwsgi`` package, which provides precompiled wheels for
|
||||
common platforms. However, it does not provide SSL support, which can be
|
||||
provided with a reverse proxy instead.
|
||||
|
||||
.. sourcecode:: text
|
||||
Create a virtualenv, install your application, then install ``pyuwsgi``.
|
||||
|
||||
$ uwsgi -s /tmp/yourapplication.sock --manage-script-name --mount /yourapplication=myapp:app
|
||||
.. code-block:: text
|
||||
|
||||
The ``--manage-script-name`` will move the handling of ``SCRIPT_NAME``
|
||||
to uwsgi, since it is smarter about that.
|
||||
It is used together with the ``--mount`` directive which will make
|
||||
requests to ``/yourapplication`` be directed to ``myapp:app``.
|
||||
If your application is accessible at root level, you can use a
|
||||
single ``/`` instead of ``/yourapplication``. ``myapp`` refers to the name of
|
||||
the file of your flask application (without extension) or the module which
|
||||
provides ``app``. ``app`` is the callable inside of your application (usually
|
||||
the line reads ``app = Flask(__name__)``.
|
||||
$ cd hello-app
|
||||
$ python -m venv .venv
|
||||
$ . .venv/bin/activate
|
||||
$ pip install . # install your application
|
||||
$ pip install pyuwsgi
|
||||
|
||||
If you want to deploy your flask application inside of a virtual environment,
|
||||
you need to also add ``--virtualenv /path/to/virtual/environment``. You might
|
||||
also need to add ``--plugin python`` or ``--plugin python3`` depending on which
|
||||
python version you use for your project.
|
||||
If you have a compiler available, you can install the ``uwsgi`` package
|
||||
instead. Or install the ``pyuwsgi`` package from sdist instead of wheel.
|
||||
Either method will include SSL support.
|
||||
|
||||
Configuring nginx
|
||||
.. code-block:: text
|
||||
|
||||
$ pip install uwsgi
|
||||
|
||||
# or
|
||||
$ pip install --no-binary pyuwsgi pyuwsgi
|
||||
|
||||
|
||||
Running
|
||||
-------
|
||||
|
||||
The most basic way to run uWSGI is to tell it to start an HTTP server
|
||||
and import your application.
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ uwsgi --http 127.0.0.1:8000 --master -p 4 -w hello:app
|
||||
|
||||
*** Starting uWSGI 2.0.20 (64bit) on [x] ***
|
||||
*** Operational MODE: preforking ***
|
||||
mounting hello:app on /
|
||||
spawned uWSGI master process (pid: x)
|
||||
spawned uWSGI worker 1 (pid: x, cores: 1)
|
||||
spawned uWSGI worker 2 (pid: x, cores: 1)
|
||||
spawned uWSGI worker 3 (pid: x, cores: 1)
|
||||
spawned uWSGI worker 4 (pid: x, cores: 1)
|
||||
spawned uWSGI http 1 (pid: x)
|
||||
|
||||
If you're using the app factory pattern, you'll need to create a small
|
||||
Python file to create the app, then point uWSGI at that.
|
||||
|
||||
.. code-block:: python
|
||||
:caption: ``wsgi.py``
|
||||
|
||||
from hello import create_app
|
||||
|
||||
app = create_app()
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ uwsgi --http 127.0.0.1:8000 --master -p 4 -w wsgi:app
|
||||
|
||||
The ``--http`` option starts an HTTP server at 127.0.0.1 port 8000. The
|
||||
``--master`` option specifies the standard worker manager. The ``-p``
|
||||
option starts 4 worker processes; a starting value could be ``CPU * 2``.
|
||||
The ``-w`` option tells uWSGI how to import your application
|
||||
|
||||
|
||||
Binding Externally
|
||||
------------------
|
||||
|
||||
uWSGI should not be run as root with the configuration shown in this doc
|
||||
because it would cause your application code to run as root, which is
|
||||
not secure. However, this means it will not be possible to bind to port
|
||||
80 or 443. Instead, a reverse proxy such as :doc:`nginx` or
|
||||
:doc:`apache-httpd` should be used in front of uWSGI. It is possible to
|
||||
run uWSGI as root securely, but that is beyond the scope of this doc.
|
||||
|
||||
uWSGI has optimized integration with `Nginx uWSGI`_ and
|
||||
`Apache mod_proxy_uwsgi`_, and possibly other servers, instead of using
|
||||
a standard HTTP proxy. That configuration is beyond the scope of this
|
||||
doc, see the links for more information.
|
||||
|
||||
.. _Nginx uWSGI: https://uwsgi-docs.readthedocs.io/en/latest/Nginx.html
|
||||
.. _Apache mod_proxy_uwsgi: https://uwsgi-docs.readthedocs.io/en/latest/Apache.html#mod-proxy-uwsgi
|
||||
|
||||
You can bind to all external IPs on a non-privileged port using the
|
||||
``--http 0.0.0.0:8000`` option. Don't do this when using a reverse proxy
|
||||
setup, otherwise it will be possible to bypass the proxy.
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ uwsgi --http 0.0.0.0:8000 --master -p 4 -w wsgi:app
|
||||
|
||||
``0.0.0.0`` is not a valid address to navigate to, you'd use a specific
|
||||
IP address in your browser.
|
||||
|
||||
|
||||
Async with gevent
|
||||
-----------------
|
||||
|
||||
A basic flask nginx configuration looks like this::
|
||||
The default sync worker is appropriate for most use cases. If you need numerous,
|
||||
long running, concurrent connections, uWSGI provides an asynchronous worker
|
||||
using `gevent`_. This is not the same as Python's ``async/await``, or the ASGI
|
||||
server spec. See :doc:`/gevent` for more information about enabling it in your
|
||||
application.
|
||||
|
||||
location = /yourapplication { rewrite ^ /yourapplication/; }
|
||||
location /yourapplication { try_files $uri @yourapplication; }
|
||||
location @yourapplication {
|
||||
include uwsgi_params;
|
||||
uwsgi_pass unix:/tmp/yourapplication.sock;
|
||||
}
|
||||
.. _gevent: https://www.gevent.org/
|
||||
|
||||
This configuration binds the application to ``/yourapplication``. If you want
|
||||
to have it in the URL root its a bit simpler::
|
||||
When using gevent, greenlet>=1.0 is required. When using PyPy, PyPy>=7.3.7 is
|
||||
required.
|
||||
|
||||
location / { try_files $uri @yourapplication; }
|
||||
location @yourapplication {
|
||||
include uwsgi_params;
|
||||
uwsgi_pass unix:/tmp/yourapplication.sock;
|
||||
}
|
||||
.. code-block:: text
|
||||
|
||||
.. _nginx: https://nginx.org/
|
||||
.. _lighttpd: https://www.lighttpd.net/
|
||||
.. _cherokee: http://cherokee-project.com/
|
||||
.. _uwsgi: https://uwsgi-docs.readthedocs.io/en/latest/
|
||||
$ uwsgi --http 127.0.0.1:8000 --master --gevent 100 -w wsgi:app
|
||||
|
||||
*** Starting uWSGI 2.0.20 (64bit) on [x] ***
|
||||
*** Operational MODE: async ***
|
||||
mounting hello:app on /
|
||||
spawned uWSGI master process (pid: x)
|
||||
spawned uWSGI worker 1 (pid: x, cores: 100)
|
||||
spawned uWSGI http 1 (pid: x)
|
||||
*** running gevent loop engine [addr:x] ***
|
||||
|
|
|
|||
75
docs/deploying/waitress.rst
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
Waitress
|
||||
========
|
||||
|
||||
`Waitress`_ is a pure Python WSGI server.
|
||||
|
||||
* It is easy to configure.
|
||||
* It supports Windows directly.
|
||||
* It is easy to install as it does not require additional dependencies
|
||||
or compilation.
|
||||
* It does not support streaming requests, full request data is always
|
||||
buffered.
|
||||
* It uses a single process with multiple thread workers.
|
||||
|
||||
This page outlines the basics of running Waitress. Be sure to read its
|
||||
documentation and ``waitress-serve --help`` to understand what features
|
||||
are available.
|
||||
|
||||
.. _Waitress: https://docs.pylonsproject.org/projects/waitress/
|
||||
|
||||
|
||||
Installing
|
||||
----------
|
||||
|
||||
Create a virtualenv, install your application, then install
|
||||
``waitress``.
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ cd hello-app
|
||||
$ python -m venv .venv
|
||||
$ . .venv/bin/activate
|
||||
$ pip install . # install your application
|
||||
$ pip install waitress
|
||||
|
||||
|
||||
Running
|
||||
-------
|
||||
|
||||
The only required argument to ``waitress-serve`` tells it how to load
|
||||
your Flask application. The syntax is ``{module}:{app}``. ``module`` is
|
||||
the dotted import name to the module with your application. ``app`` is
|
||||
the variable with the application. If you're using the app factory
|
||||
pattern, use ``--call {module}:{factory}`` instead.
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
# equivalent to 'from hello import app'
|
||||
$ waitress-serve --host 127.0.0.1 hello:app
|
||||
|
||||
# equivalent to 'from hello import create_app; create_app()'
|
||||
$ waitress-serve --host 127.0.0.1 --call hello:create_app
|
||||
|
||||
Serving on http://127.0.0.1:8080
|
||||
|
||||
The ``--host`` option binds the server to local ``127.0.0.1`` only.
|
||||
|
||||
Logs for each request aren't shown, only errors are shown. Logging can
|
||||
be configured through the Python interface instead of the command line.
|
||||
|
||||
|
||||
Binding Externally
|
||||
------------------
|
||||
|
||||
Waitress should not be run as root because it would cause your
|
||||
application code to run as root, which is not secure. However, this
|
||||
means it will not be possible to bind to port 80 or 443. Instead, a
|
||||
reverse proxy such as :doc:`nginx` or :doc:`apache-httpd` should be used
|
||||
in front of Waitress.
|
||||
|
||||
You can bind to all external IPs on a non-privileged port by not
|
||||
specifying the ``--host`` option. Don't do this when using a reverse
|
||||
proxy setup, otherwise it will be possible to bypass the proxy.
|
||||
|
||||
``0.0.0.0`` is not a valid address to navigate to, you'd use a specific
|
||||
IP address in your browser.
|
||||
|
|
@ -1,155 +0,0 @@
|
|||
.. _deploying-wsgi-standalone:
|
||||
|
||||
Standalone WSGI Containers
|
||||
==========================
|
||||
|
||||
There are popular servers written in Python that contain WSGI applications and
|
||||
serve HTTP. These servers stand alone when they run; you can proxy to them
|
||||
from your web server. Note the section on :ref:`deploying-proxy-setups` if you
|
||||
run into issues.
|
||||
|
||||
Gunicorn
|
||||
--------
|
||||
|
||||
`Gunicorn`_ 'Green Unicorn' is a WSGI HTTP Server for UNIX. It's a pre-fork
|
||||
worker model ported from Ruby's Unicorn project. It supports both `eventlet`_
|
||||
and `greenlet`_. Running a Flask application on this server is quite simple::
|
||||
|
||||
$ gunicorn myproject:app
|
||||
|
||||
`Gunicorn`_ provides many command-line options -- see ``gunicorn -h``.
|
||||
For example, to run a Flask application with 4 worker processes (``-w
|
||||
4``) binding to localhost port 4000 (``-b 127.0.0.1:4000``)::
|
||||
|
||||
$ gunicorn -w 4 -b 127.0.0.1:4000 myproject:app
|
||||
|
||||
The ``gunicorn`` command expects the names of your application module or
|
||||
package and the application instance within the module. If you use the
|
||||
application factory pattern, you can pass a call to that::
|
||||
|
||||
$ gunicorn "myproject:create_app()"
|
||||
|
||||
.. _Gunicorn: https://gunicorn.org/
|
||||
.. _eventlet: https://eventlet.net/
|
||||
|
||||
|
||||
uWSGI
|
||||
--------
|
||||
|
||||
`uWSGI`_ is a fast application server written in C. It is very configurable
|
||||
which makes it more complicated to setup than gunicorn.
|
||||
|
||||
Running `uWSGI HTTP Router`_::
|
||||
|
||||
$ uwsgi --http 127.0.0.1:5000 --module myproject:app
|
||||
|
||||
For a more optimized setup, see :doc:`configuring uWSGI and NGINX <uwsgi>`.
|
||||
|
||||
.. _uWSGI: https://uwsgi-docs.readthedocs.io/en/latest/
|
||||
.. _uWSGI HTTP Router: https://uwsgi-docs.readthedocs.io/en/latest/HTTP.html#the-uwsgi-http-https-router
|
||||
|
||||
Gevent
|
||||
-------
|
||||
|
||||
`Gevent`_ is a coroutine-based Python networking library that uses
|
||||
`greenlet`_ to provide a high-level synchronous API on top of `libev`_
|
||||
event loop::
|
||||
|
||||
from gevent.pywsgi import WSGIServer
|
||||
from yourapplication import app
|
||||
|
||||
http_server = WSGIServer(('', 5000), app)
|
||||
http_server.serve_forever()
|
||||
|
||||
.. _Gevent: http://www.gevent.org/
|
||||
.. _greenlet: https://greenlet.readthedocs.io/en/latest/
|
||||
.. _libev: http://software.schmorp.de/pkg/libev.html
|
||||
|
||||
Twisted Web
|
||||
-----------
|
||||
|
||||
`Twisted Web`_ is the web server shipped with `Twisted`_, a mature,
|
||||
non-blocking event-driven networking library. Twisted Web comes with a
|
||||
standard WSGI container which can be controlled from the command line using
|
||||
the ``twistd`` utility::
|
||||
|
||||
$ twistd web --wsgi myproject.app
|
||||
|
||||
This example will run a Flask application called ``app`` from a module named
|
||||
``myproject``.
|
||||
|
||||
Twisted Web supports many flags and options, and the ``twistd`` utility does
|
||||
as well; see ``twistd -h`` and ``twistd web -h`` for more information. For
|
||||
example, to run a Twisted Web server in the foreground, on port 8080, with an
|
||||
application from ``myproject``::
|
||||
|
||||
$ twistd -n web --port tcp:8080 --wsgi myproject.app
|
||||
|
||||
.. _Twisted: https://twistedmatrix.com/trac/
|
||||
.. _Twisted Web: https://twistedmatrix.com/trac/wiki/TwistedWeb
|
||||
|
||||
.. _deploying-proxy-setups:
|
||||
|
||||
Proxy Setups
|
||||
------------
|
||||
|
||||
If you deploy your application using one of these servers behind an HTTP proxy
|
||||
you will need to rewrite a few headers in order for the application to work.
|
||||
The two problematic values in the WSGI environment usually are ``REMOTE_ADDR``
|
||||
and ``HTTP_HOST``. You can configure your httpd to pass these headers, or you
|
||||
can fix them in middleware. Werkzeug ships a fixer that will solve some common
|
||||
setups, but you might want to write your own WSGI middleware for specific
|
||||
setups.
|
||||
|
||||
Here's a simple nginx configuration which proxies to an application served on
|
||||
localhost at port 8000, setting appropriate headers:
|
||||
|
||||
.. sourcecode:: nginx
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
|
||||
server_name _;
|
||||
|
||||
access_log /var/log/nginx/access.log;
|
||||
error_log /var/log/nginx/error.log;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:8000/;
|
||||
proxy_redirect off;
|
||||
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
}
|
||||
|
||||
If your httpd is not providing these headers, the most common setup invokes the
|
||||
host being set from ``X-Forwarded-Host`` and the remote address from
|
||||
``X-Forwarded-For``::
|
||||
|
||||
from werkzeug.middleware.proxy_fix import ProxyFix
|
||||
app.wsgi_app = ProxyFix(app.wsgi_app, x_proto=1, x_host=1)
|
||||
|
||||
.. admonition:: Trusting Headers
|
||||
|
||||
Please keep in mind that it is a security issue to use such a middleware in
|
||||
a non-proxy setup because it will blindly trust the incoming headers which
|
||||
might be forged by malicious clients.
|
||||
|
||||
If you want to rewrite the headers from another header, you might want to
|
||||
use a fixer like this::
|
||||
|
||||
class CustomProxyFix(object):
|
||||
|
||||
def __init__(self, app):
|
||||
self.app = app
|
||||
|
||||
def __call__(self, environ, start_response):
|
||||
host = environ.get('HTTP_X_FHOST', '')
|
||||
if host:
|
||||
environ['HTTP_HOST'] = host
|
||||
return self.app(environ, start_response)
|
||||
|
||||
app.wsgi_app = CustomProxyFix(app.wsgi_app)
|
||||
102
docs/design.rst
|
|
@ -1,5 +1,3 @@
|
|||
.. _design:
|
||||
|
||||
Design Decisions in Flask
|
||||
=========================
|
||||
|
||||
|
|
@ -77,7 +75,7 @@ to the application object :meth:`~flask.Flask.wsgi_app`).
|
|||
|
||||
Furthermore this design makes it possible to use a factory function to
|
||||
create the application which is very helpful for unit testing and similar
|
||||
things (:ref:`app-factories`).
|
||||
things (:doc:`/patterns/appfactories`).
|
||||
|
||||
The Routing System
|
||||
------------------
|
||||
|
|
@ -98,10 +96,10 @@ is ambiguous.
|
|||
One Template Engine
|
||||
-------------------
|
||||
|
||||
Flask decides on one template engine: Jinja2. Why doesn't Flask have a
|
||||
Flask decides on one template engine: Jinja. Why doesn't Flask have a
|
||||
pluggable template engine interface? You can obviously use a different
|
||||
template engine, but Flask will still configure Jinja2 for you. While
|
||||
that limitation that Jinja2 is *always* configured will probably go away,
|
||||
template engine, but Flask will still configure Jinja for you. While
|
||||
that limitation that Jinja is *always* configured will probably go away,
|
||||
the decision to bundle one template engine and use that will not.
|
||||
|
||||
Template engines are like programming languages and each of those engines
|
||||
|
|
@ -109,19 +107,19 @@ has a certain understanding about how things work. On the surface they
|
|||
all work the same: you tell the engine to evaluate a template with a set
|
||||
of variables and take the return value as string.
|
||||
|
||||
But that's about where similarities end. Jinja2 for example has an
|
||||
extensive filter system, a certain way to do template inheritance, support
|
||||
for reusable blocks (macros) that can be used from inside templates and
|
||||
also from Python code, uses Unicode for all operations, supports
|
||||
iterative template rendering, configurable syntax and more. On the other
|
||||
hand an engine like Genshi is based on XML stream evaluation, template
|
||||
inheritance by taking the availability of XPath into account and more.
|
||||
Mako on the other hand treats templates similar to Python modules.
|
||||
But that's about where similarities end. Jinja for example has an
|
||||
extensive filter system, a certain way to do template inheritance,
|
||||
support for reusable blocks (macros) that can be used from inside
|
||||
templates and also from Python code, supports iterative template
|
||||
rendering, configurable syntax and more. On the other hand an engine
|
||||
like Genshi is based on XML stream evaluation, template inheritance by
|
||||
taking the availability of XPath into account and more. Mako on the
|
||||
other hand treats templates similar to Python modules.
|
||||
|
||||
When it comes to connecting a template engine with an application or
|
||||
framework there is more than just rendering templates. For instance,
|
||||
Flask uses Jinja2's extensive autoescaping support. Also it provides
|
||||
ways to access macros from Jinja2 templates.
|
||||
Flask uses Jinja's extensive autoescaping support. Also it provides
|
||||
ways to access macros from Jinja templates.
|
||||
|
||||
A template abstraction layer that would not take the unique features of
|
||||
the template engines away is a science on its own and a too large
|
||||
|
|
@ -132,11 +130,27 @@ being present. You can easily use your own templating language, but an
|
|||
extension could still depend on Jinja itself.
|
||||
|
||||
|
||||
Micro with Dependencies
|
||||
What does "micro" mean?
|
||||
-----------------------
|
||||
|
||||
“Micro” does not mean that your whole web application has to fit into a single
|
||||
Python file (although it certainly can), nor does it mean that Flask is lacking
|
||||
in functionality. The "micro" in microframework means Flask aims to keep the
|
||||
core simple but extensible. Flask won't make many decisions for you, such as
|
||||
what database to use. Those decisions that it does make, such as what
|
||||
templating engine to use, are easy to change. Everything else is up to you, so
|
||||
that Flask can be everything you need and nothing you don't.
|
||||
|
||||
By default, Flask does not include a database abstraction layer, form
|
||||
validation or anything else where different libraries already exist that can
|
||||
handle that. Instead, Flask supports extensions to add such functionality to
|
||||
your application as if it was implemented in Flask itself. Numerous extensions
|
||||
provide database integration, form validation, upload handling, various open
|
||||
authentication technologies, and more. Flask may be "micro", but it's ready for
|
||||
production use on a variety of needs.
|
||||
|
||||
Why does Flask call itself a microframework and yet it depends on two
|
||||
libraries (namely Werkzeug and Jinja2). Why shouldn't it? If we look
|
||||
libraries (namely Werkzeug and Jinja). Why shouldn't it? If we look
|
||||
over to the Ruby side of web development there we have a protocol very
|
||||
similar to WSGI. Just that it's called Rack there, but besides that it
|
||||
looks very much like a WSGI rendition for Ruby. But nearly all
|
||||
|
|
@ -155,22 +169,39 @@ infrastructure, packages with dependencies are no longer an issue and
|
|||
there are very few reasons against having libraries that depend on others.
|
||||
|
||||
|
||||
Thread Locals
|
||||
-------------
|
||||
Context Locals
|
||||
--------------
|
||||
|
||||
Flask uses thread local objects (context local objects in fact, they
|
||||
support greenlet contexts as well) for request, session and an extra
|
||||
object you can put your own things on (:data:`~flask.g`). Why is that and
|
||||
isn't that a bad idea?
|
||||
Flask uses special context locals and proxies to provide access to the
|
||||
current app and request data to any code running during a request, CLI command,
|
||||
etc. Context locals are specific to the worker handling the activity, such as a
|
||||
thread, process, coroutine, or greenlet.
|
||||
|
||||
Yes it is usually not such a bright idea to use thread locals. They cause
|
||||
troubles for servers that are not based on the concept of threads and make
|
||||
large applications harder to maintain. However Flask is just not designed
|
||||
for large applications or asynchronous servers. Flask wants to make it
|
||||
quick and easy to write a traditional web application.
|
||||
The context and proxies help solve two development issues: circular imports, and
|
||||
passing around global data. :data:`.current_app` can be used to access the
|
||||
application object without needing to import the app object directly, avoiding
|
||||
circular import issues. :data:`.request`, :data:`.session`, and :data:`.g` can
|
||||
be imported to access the current data for the request, rather than needing to
|
||||
pass them as arguments through every single function in your project.
|
||||
|
||||
Also see the :ref:`becomingbig` section of the documentation for some
|
||||
inspiration for larger applications based on Flask.
|
||||
|
||||
Async/await and ASGI support
|
||||
----------------------------
|
||||
|
||||
Flask supports ``async`` coroutines for view functions by executing the
|
||||
coroutine on a separate thread instead of using an event loop on the
|
||||
main thread as an async-first (ASGI) framework would. This is necessary
|
||||
for Flask to remain backwards compatible with extensions and code built
|
||||
before ``async`` was introduced into Python. This compromise introduces
|
||||
a performance cost compared with the ASGI frameworks, due to the
|
||||
overhead of the threads.
|
||||
|
||||
Due to how tied to WSGI Flask's code is, it's not clear if it's possible
|
||||
to make the ``Flask`` class support ASGI and WSGI at the same time. Work
|
||||
is currently being done in Werkzeug to work with ASGI, which may
|
||||
eventually enable support in Flask as well.
|
||||
|
||||
See :doc:`/async-await` for more discussion.
|
||||
|
||||
|
||||
What Flask is, What Flask is Not
|
||||
|
|
@ -178,7 +209,7 @@ What Flask is, What Flask is Not
|
|||
|
||||
Flask will never have a database layer. It will not have a form library
|
||||
or anything else in that direction. Flask itself just bridges to Werkzeug
|
||||
to implement a proper WSGI application and to Jinja2 to handle templating.
|
||||
to implement a proper WSGI application and to Jinja to handle templating.
|
||||
It also binds to a few common standard library packages such as logging.
|
||||
Everything else is up for extensions.
|
||||
|
||||
|
|
@ -187,5 +218,12 @@ requirements and Flask could not meet those if it would force any of this
|
|||
into the core. The majority of web applications will need a template
|
||||
engine in some sort. However not every application needs a SQL database.
|
||||
|
||||
As your codebase grows, you are free to make the design decisions appropriate
|
||||
for your project. Flask will continue to provide a very simple glue layer to
|
||||
the best that Python has to offer. You can implement advanced patterns in
|
||||
SQLAlchemy or another database tool, introduce non-relational data persistence
|
||||
as appropriate, and take advantage of framework-agnostic tools built for WSGI,
|
||||
the Python web interface.
|
||||
|
||||
The idea of Flask is to build a good foundation for all applications.
|
||||
Everything else is up to you or extensions.
|
||||
|
|
|
|||
|
|
@ -1,14 +1,10 @@
|
|||
.. _application-errors:
|
||||
Handling Application Errors
|
||||
===========================
|
||||
|
||||
Application Errors
|
||||
==================
|
||||
|
||||
.. versionadded:: 0.3
|
||||
|
||||
Applications fail, servers fail. Sooner or later you will see an exception
|
||||
in production. Even if your code is 100% correct, you will still see
|
||||
exceptions from time to time. Why? Because everything else involved will
|
||||
fail. Here are some situations where perfectly fine code can lead to server
|
||||
Applications fail, servers fail. Sooner or later you will see an exception
|
||||
in production. Even if your code is 100% correct, you will still see
|
||||
exceptions from time to time. Why? Because everything else involved will
|
||||
fail. Here are some situations where perfectly fine code can lead to server
|
||||
errors:
|
||||
|
||||
- the client terminated the request early and the application was still
|
||||
|
|
@ -20,13 +16,16 @@ errors:
|
|||
- a programming error in a library you are using
|
||||
- network connection of the server to another system failed
|
||||
|
||||
And that's just a small sample of issues you could be facing. So how do we
|
||||
deal with that sort of problem? By default if your application runs in
|
||||
production mode, Flask will display a very simple page for you and log the
|
||||
exception to the :attr:`~flask.Flask.logger`.
|
||||
And that's just a small sample of issues you could be facing. So how do we
|
||||
deal with that sort of problem? By default if your application runs in
|
||||
production mode, and an exception is raised Flask will display a very simple
|
||||
page for you and log the exception to the :attr:`~flask.Flask.logger`.
|
||||
|
||||
But there is more you can do, and we will cover some better setups to deal
|
||||
with errors.
|
||||
with errors including custom exceptions and 3rd party tools.
|
||||
|
||||
|
||||
.. _error-logging-tools:
|
||||
|
||||
Error Logging Tools
|
||||
-------------------
|
||||
|
|
@ -34,51 +33,66 @@ Error Logging Tools
|
|||
Sending error mails, even if just for critical ones, can become
|
||||
overwhelming if enough users are hitting the error and log files are
|
||||
typically never looked at. This is why we recommend using `Sentry
|
||||
<https://sentry.io/>`_ for dealing with application errors. It's
|
||||
available as an Open Source project `on GitHub
|
||||
<https://sentry.io/>`_ for dealing with application errors. It's
|
||||
available as a source-available project `on GitHub
|
||||
<https://github.com/getsentry/sentry>`_ and is also available as a `hosted version
|
||||
<https://sentry.io/signup/>`_ which you can try for free. Sentry
|
||||
aggregates duplicate errors, captures the full stack trace and local
|
||||
variables for debugging, and sends you mails based on new errors or
|
||||
frequency thresholds.
|
||||
|
||||
To use Sentry you need to install the `sentry-sdk` client with extra `flask` dependencies::
|
||||
To use Sentry you need to install the ``sentry-sdk`` client with extra
|
||||
``flask`` dependencies.
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ pip install sentry-sdk[flask]
|
||||
|
||||
And then add this to your Flask app::
|
||||
And then add this to your Flask app:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import sentry_sdk
|
||||
from sentry_sdk.integrations.flask import FlaskIntegration
|
||||
|
||||
sentry_sdk.init('YOUR_DSN_HERE',integrations=[FlaskIntegration()])
|
||||
sentry_sdk.init('YOUR_DSN_HERE', integrations=[FlaskIntegration()])
|
||||
|
||||
The `YOUR_DSN_HERE` value needs to be replaced with the DSN value you get
|
||||
from your Sentry installation.
|
||||
The ``YOUR_DSN_HERE`` value needs to be replaced with the DSN value you
|
||||
get from your Sentry installation.
|
||||
|
||||
After installation, failures leading to an Internal Server Error
|
||||
are automatically reported to Sentry and from there you can
|
||||
receive error notifications.
|
||||
|
||||
Follow-up reads:
|
||||
See also:
|
||||
|
||||
* Sentry also supports catching errors from your worker queue (RQ, Celery) in a
|
||||
similar fashion. See the `Python SDK docs
|
||||
<https://docs.sentry.io/platforms/python/>`_ for more information.
|
||||
* `Getting started with Sentry <https://docs.sentry.io/quickstart/?platform=python>`_
|
||||
* `Flask-specific documentation <https://docs.sentry.io/platforms/python/guides/flask/>`_.
|
||||
- Sentry also supports catching errors from a worker queue
|
||||
(RQ, Celery, etc.) in a similar fashion. See the `Python SDK docs
|
||||
<https://docs.sentry.io/platforms/python/>`__ for more information.
|
||||
- `Flask-specific documentation <https://docs.sentry.io/platforms/python/guides/flask/>`__
|
||||
|
||||
.. _error-handlers:
|
||||
|
||||
Error handlers
|
||||
Error Handlers
|
||||
--------------
|
||||
|
||||
When an error occurs in Flask, an appropriate `HTTP status code
|
||||
<https://developer.mozilla.org/en-US/docs/Web/HTTP/Status>`__ will be
|
||||
returned. 400-499 indicate errors with the client's request data, or
|
||||
about the data requested. 500-599 indicate errors with the server or
|
||||
application itself.
|
||||
|
||||
You might want to show custom error pages to the user when an error occurs.
|
||||
This can be done by registering error handlers.
|
||||
|
||||
An error handler is a normal view function that returns a response, but instead
|
||||
of being registered for a route, it is registered for an exception or HTTP
|
||||
status code that would be raised while trying to handle a request.
|
||||
An error handler is a function that returns a response when a type of error is
|
||||
raised, similar to how a view is a function that returns a response when a
|
||||
request URL is matched. It is passed the instance of the error being handled,
|
||||
which is most likely a :exc:`~werkzeug.exceptions.HTTPException`.
|
||||
|
||||
The status code of the response will not be set to the handler's code. Make
|
||||
sure to provide the appropriate HTTP status code when returning a response from
|
||||
a handler.
|
||||
|
||||
|
||||
Registering
|
||||
```````````
|
||||
|
|
@ -86,7 +100,9 @@ Registering
|
|||
Register handlers by decorating a function with
|
||||
:meth:`~flask.Flask.errorhandler`. Or use
|
||||
:meth:`~flask.Flask.register_error_handler` to register the function later.
|
||||
Remember to set the error code when returning the response. ::
|
||||
Remember to set the error code when returning the response.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@app.errorhandler(werkzeug.exceptions.BadRequest)
|
||||
def handle_bad_request(e):
|
||||
|
|
@ -102,7 +118,9 @@ when registering handlers. (``BadRequest.code == 400``)
|
|||
Non-standard HTTP codes cannot be registered by code because they are not known
|
||||
by Werkzeug. Instead, define a subclass of
|
||||
:class:`~werkzeug.exceptions.HTTPException` with the appropriate code and
|
||||
register and raise that exception class. ::
|
||||
register and raise that exception class.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class InsufficientStorage(werkzeug.exceptions.HTTPException):
|
||||
code = 507
|
||||
|
|
@ -117,21 +135,36 @@ Handlers can be registered for any exception class, not just
|
|||
codes. Handlers can be registered for a specific class, or for all subclasses
|
||||
of a parent class.
|
||||
|
||||
|
||||
Handling
|
||||
````````
|
||||
|
||||
When an exception is caught by Flask while handling a request, it is first
|
||||
looked up by code. If no handler is registered for the code, it is looked up
|
||||
by its class hierarchy; the most specific handler is chosen. If no handler is
|
||||
registered, :class:`~werkzeug.exceptions.HTTPException` subclasses show a
|
||||
When building a Flask application you *will* run into exceptions. If some part
|
||||
of your code breaks while handling a request (and you have no error handlers
|
||||
registered), a "500 Internal Server Error"
|
||||
(:exc:`~werkzeug.exceptions.InternalServerError`) will be returned by default.
|
||||
Similarly, "404 Not Found"
|
||||
(:exc:`~werkzeug.exceptions.NotFound`) error will occur if a request is sent to an unregistered route.
|
||||
If a route receives an unallowed request method, a "405 Method Not Allowed"
|
||||
(:exc:`~werkzeug.exceptions.MethodNotAllowed`) will be raised. These are all
|
||||
subclasses of :class:`~werkzeug.exceptions.HTTPException` and are provided by
|
||||
default in Flask.
|
||||
|
||||
Flask gives you the ability to raise any HTTP exception registered by
|
||||
Werkzeug. However, the default HTTP exceptions return simple exception
|
||||
pages. You might want to show custom error pages to the user when an error occurs.
|
||||
This can be done by registering error handlers.
|
||||
|
||||
When Flask catches an exception while handling a request, it is first looked up by code.
|
||||
If no handler is registered for the code, Flask looks up the error by its class hierarchy; the most specific handler is chosen.
|
||||
If no handler is registered, :class:`~werkzeug.exceptions.HTTPException` subclasses show a
|
||||
generic message about their code, while other exceptions are converted to a
|
||||
generic 500 Internal Server Error.
|
||||
generic "500 Internal Server Error".
|
||||
|
||||
For example, if an instance of :exc:`ConnectionRefusedError` is raised,
|
||||
and a handler is registered for :exc:`ConnectionError` and
|
||||
:exc:`ConnectionRefusedError`,
|
||||
the more specific :exc:`ConnectionRefusedError` handler is called with the
|
||||
exception instance to generate the response.
|
||||
:exc:`ConnectionRefusedError`, the more specific :exc:`ConnectionRefusedError`
|
||||
handler is called with the exception instance to generate the response.
|
||||
|
||||
Handlers registered on the blueprint take precedence over those registered
|
||||
globally on the application, assuming a blueprint is handling the request that
|
||||
|
|
@ -147,8 +180,8 @@ It is possible to register error handlers for very generic base classes
|
|||
such as ``HTTPException`` or even ``Exception``. However, be aware that
|
||||
these will catch more than you might expect.
|
||||
|
||||
An error handler for ``HTTPException`` might be useful for turning
|
||||
the default HTML errors pages into JSON, for example. However, this
|
||||
For example, an error handler for ``HTTPException`` might be useful for turning
|
||||
the default HTML errors pages into JSON. However, this
|
||||
handler will trigger for things you don't cause directly, such as 404
|
||||
and 405 errors during routing. Be sure to craft your handler carefully
|
||||
so you don't lose information about the HTTP error.
|
||||
|
|
@ -172,12 +205,13 @@ so you don't lose information about the HTTP error.
|
|||
response.content_type = "application/json"
|
||||
return response
|
||||
|
||||
|
||||
An error handler for ``Exception`` might seem useful for changing how
|
||||
all errors, even unhandled ones, are presented to the user. However,
|
||||
this is similar to doing ``except Exception:`` in Python, it will
|
||||
capture *all* otherwise unhandled errors, including all HTTP status
|
||||
codes. In most cases it will be safer to register handlers for more
|
||||
codes.
|
||||
|
||||
In most cases it will be safer to register handlers for more
|
||||
specific exceptions. Since ``HTTPException`` instances are valid WSGI
|
||||
responses, you could also pass them through directly.
|
||||
|
||||
|
|
@ -197,7 +231,8 @@ responses, you could also pass them through directly.
|
|||
Error handlers still respect the exception class hierarchy. If you
|
||||
register handlers for both ``HTTPException`` and ``Exception``, the
|
||||
``Exception`` handler will not handle ``HTTPException`` subclasses
|
||||
because it the ``HTTPException`` handler is more specific.
|
||||
because the ``HTTPException`` handler is more specific.
|
||||
|
||||
|
||||
Unhandled Exceptions
|
||||
````````````````````
|
||||
|
|
@ -210,88 +245,279 @@ behavior.
|
|||
If there is an error handler registered for ``InternalServerError``,
|
||||
this will be invoked. As of Flask 1.1.0, this error handler will always
|
||||
be passed an instance of ``InternalServerError``, not the original
|
||||
unhandled error. The original error is available as ``e.original_exception``.
|
||||
Until Werkzeug 1.0.0, this attribute will only exist during unhandled
|
||||
errors, use ``getattr`` to get access it for compatibility.
|
||||
unhandled error.
|
||||
|
||||
The original error is available as ``e.original_exception``.
|
||||
|
||||
An error handler for "500 Internal Server Error" will be passed uncaught
|
||||
exceptions in addition to explicit 500 errors. In debug mode, a handler
|
||||
for "500 Internal Server Error" will not be used. Instead, the
|
||||
interactive debugger will be shown.
|
||||
|
||||
|
||||
Custom Error Pages
|
||||
------------------
|
||||
|
||||
Sometimes when building a Flask application, you might want to raise a
|
||||
:exc:`~werkzeug.exceptions.HTTPException` to signal to the user that
|
||||
something is wrong with the request. Fortunately, Flask comes with a handy
|
||||
:func:`~flask.abort` function that aborts a request with a HTTP error from
|
||||
werkzeug as desired. It will also provide a plain black and white error page
|
||||
for you with a basic description, but nothing fancy.
|
||||
|
||||
Depending on the error code it is less or more likely for the user to
|
||||
actually see such an error.
|
||||
|
||||
Consider the code below, we might have a user profile route, and if the user
|
||||
fails to pass a username we can raise a "400 Bad Request". If the user passes a
|
||||
username and we can't find it, we raise a "404 Not Found".
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@app.errorhandler(InternalServerError)
|
||||
def handle_500(e):
|
||||
original = getattr(e, "original_exception", None)
|
||||
from flask import abort, render_template, request
|
||||
|
||||
if original is None:
|
||||
# direct 500 error, such as abort(500)
|
||||
return render_template("500.html"), 500
|
||||
# a username needs to be supplied in the query args
|
||||
# a successful request would be like /profile?username=jack
|
||||
@app.route("/profile")
|
||||
def user_profile():
|
||||
username = request.arg.get("username")
|
||||
# if a username isn't supplied in the request, return a 400 bad request
|
||||
if username is None:
|
||||
abort(400)
|
||||
|
||||
# wrapped unhandled error
|
||||
return render_template("500_unhandled.html", e=original), 500
|
||||
user = get_user(username=username)
|
||||
# if a user can't be found by their username, return 404 not found
|
||||
if user is None:
|
||||
abort(404)
|
||||
|
||||
return render_template("profile.html", user=user)
|
||||
|
||||
Here is another example implementation for a "404 Page Not Found" exception:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from flask import render_template
|
||||
|
||||
@app.errorhandler(404)
|
||||
def page_not_found(e):
|
||||
# note that we set the 404 status explicitly
|
||||
return render_template('404.html'), 404
|
||||
|
||||
When using :doc:`/patterns/appfactories`:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from flask import Flask, render_template
|
||||
|
||||
def page_not_found(e):
|
||||
return render_template('404.html'), 404
|
||||
|
||||
def create_app(config_filename):
|
||||
app = Flask(__name__)
|
||||
app.register_error_handler(404, page_not_found)
|
||||
return app
|
||||
|
||||
An example template might be this:
|
||||
|
||||
.. code-block:: html+jinja
|
||||
|
||||
{% extends "layout.html" %}
|
||||
{% block title %}Page Not Found{% endblock %}
|
||||
{% block body %}
|
||||
<h1>Page Not Found</h1>
|
||||
<p>What you were looking for is just not there.
|
||||
<p><a href="{{ url_for('index') }}">go somewhere nice</a>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
Further Examples
|
||||
````````````````
|
||||
|
||||
The above examples wouldn't actually be an improvement on the default
|
||||
exception pages. We can create a custom 500.html template like this:
|
||||
|
||||
.. code-block:: html+jinja
|
||||
|
||||
{% extends "layout.html" %}
|
||||
{% block title %}Internal Server Error{% endblock %}
|
||||
{% block body %}
|
||||
<h1>Internal Server Error</h1>
|
||||
<p>Oops... we seem to have made a mistake, sorry!</p>
|
||||
<p><a href="{{ url_for('index') }}">Go somewhere nice instead</a>
|
||||
{% endblock %}
|
||||
|
||||
It can be implemented by rendering the template on "500 Internal Server Error":
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from flask import render_template
|
||||
|
||||
@app.errorhandler(500)
|
||||
def internal_server_error(e):
|
||||
# note that we set the 500 status explicitly
|
||||
return render_template('500.html'), 500
|
||||
|
||||
When using :doc:`/patterns/appfactories`:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from flask import Flask, render_template
|
||||
|
||||
def internal_server_error(e):
|
||||
return render_template('500.html'), 500
|
||||
|
||||
def create_app():
|
||||
app = Flask(__name__)
|
||||
app.register_error_handler(500, internal_server_error)
|
||||
return app
|
||||
|
||||
When using :doc:`/blueprints`:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from flask import Blueprint
|
||||
|
||||
blog = Blueprint('blog', __name__)
|
||||
|
||||
# as a decorator
|
||||
@blog.errorhandler(500)
|
||||
def internal_server_error(e):
|
||||
return render_template('500.html'), 500
|
||||
|
||||
# or with register_error_handler
|
||||
blog.register_error_handler(500, internal_server_error)
|
||||
|
||||
|
||||
Blueprint Error Handlers
|
||||
------------------------
|
||||
|
||||
In :doc:`/blueprints`, most error handlers will work as expected.
|
||||
However, there is a caveat concerning handlers for 404 and 405
|
||||
exceptions. These error handlers are only invoked from an appropriate
|
||||
``raise`` statement or a call to ``abort`` in another of the blueprint's
|
||||
view functions; they are not invoked by, e.g., an invalid URL access.
|
||||
|
||||
This is because the blueprint does not "own" a certain URL space, so
|
||||
the application instance has no way of knowing which blueprint error
|
||||
handler it should run if given an invalid URL. If you would like to
|
||||
execute different handling strategies for these errors based on URL
|
||||
prefixes, they may be defined at the application level using the
|
||||
``request`` proxy object.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from flask import jsonify, render_template
|
||||
|
||||
# at the application level
|
||||
# not the blueprint level
|
||||
@app.errorhandler(404)
|
||||
def page_not_found(e):
|
||||
# if a request is in our blog URL space
|
||||
if request.path.startswith('/blog/'):
|
||||
# we return a custom blog 404 page
|
||||
return render_template("blog/404.html"), 404
|
||||
else:
|
||||
# otherwise we return our generic site-wide 404 page
|
||||
return render_template("404.html"), 404
|
||||
|
||||
@app.errorhandler(405)
|
||||
def method_not_allowed(e):
|
||||
# if a request has the wrong method to our API
|
||||
if request.path.startswith('/api/'):
|
||||
# we return a json saying so
|
||||
return jsonify(message="Method Not Allowed"), 405
|
||||
else:
|
||||
# otherwise we return a generic site-wide 405 page
|
||||
return render_template("405.html"), 405
|
||||
|
||||
|
||||
Returning API Errors as JSON
|
||||
----------------------------
|
||||
|
||||
When building APIs in Flask, some developers realise that the built-in
|
||||
exceptions are not expressive enough for APIs and that the content type of
|
||||
:mimetype:`text/html` they are emitting is not very useful for API consumers.
|
||||
|
||||
Using the same techniques as above and :func:`~flask.json.jsonify` we can return JSON
|
||||
responses to API errors. :func:`~flask.abort` is called
|
||||
with a ``description`` parameter. The error handler will
|
||||
use that as the JSON error message, and set the status code to 404.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from flask import abort, jsonify
|
||||
|
||||
@app.errorhandler(404)
|
||||
def resource_not_found(e):
|
||||
return jsonify(error=str(e)), 404
|
||||
|
||||
@app.route("/cheese")
|
||||
def get_one_cheese():
|
||||
resource = get_resource()
|
||||
|
||||
if resource is None:
|
||||
abort(404, description="Resource not found")
|
||||
|
||||
return jsonify(resource)
|
||||
|
||||
We can also create custom exception classes. For instance, we can
|
||||
introduce a new custom exception for an API that can take a proper human readable message,
|
||||
a status code for the error and some optional payload to give more context
|
||||
for the error.
|
||||
|
||||
This is a simple example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from flask import jsonify, request
|
||||
|
||||
class InvalidAPIUsage(Exception):
|
||||
status_code = 400
|
||||
|
||||
def __init__(self, message, status_code=None, payload=None):
|
||||
super().__init__()
|
||||
self.message = message
|
||||
if status_code is not None:
|
||||
self.status_code = status_code
|
||||
self.payload = payload
|
||||
|
||||
def to_dict(self):
|
||||
rv = dict(self.payload or ())
|
||||
rv['message'] = self.message
|
||||
return rv
|
||||
|
||||
@app.errorhandler(InvalidAPIUsage)
|
||||
def invalid_api_usage(e):
|
||||
return jsonify(e.to_dict()), e.status_code
|
||||
|
||||
# an API app route for getting user information
|
||||
# a correct request might be /api/user?user_id=420
|
||||
@app.route("/api/user")
|
||||
def user_api(user_id):
|
||||
user_id = request.arg.get("user_id")
|
||||
if not user_id:
|
||||
raise InvalidAPIUsage("No user id provided!")
|
||||
|
||||
user = get_user(user_id=user_id)
|
||||
if not user:
|
||||
raise InvalidAPIUsage("No such user!", status_code=404)
|
||||
|
||||
return jsonify(user.to_dict())
|
||||
|
||||
A view can now raise that exception with an error message. Additionally
|
||||
some extra payload can be provided as a dictionary through the `payload`
|
||||
parameter.
|
||||
|
||||
|
||||
Logging
|
||||
-------
|
||||
|
||||
See :doc:`/logging` for information on how to log exceptions, such as by
|
||||
emailing them to admins.
|
||||
See :doc:`/logging` for information about how to log exceptions, such as
|
||||
by emailing them to admins.
|
||||
|
||||
|
||||
Debugging Application Errors
|
||||
============================
|
||||
Debugging
|
||||
---------
|
||||
|
||||
For production applications, configure your application with logging and
|
||||
notifications as described in :ref:`application-errors`. This section provides
|
||||
pointers when debugging deployment configuration and digging deeper with a
|
||||
full-featured Python debugger.
|
||||
|
||||
|
||||
When in Doubt, Run Manually
|
||||
---------------------------
|
||||
|
||||
Having problems getting your application configured for production? If you
|
||||
have shell access to your host, verify that you can run your application
|
||||
manually from the shell in the deployment environment. Be sure to run under
|
||||
the same user account as the configured deployment to troubleshoot permission
|
||||
issues. You can use Flask's builtin development server with `debug=True` on
|
||||
your production host, which is helpful in catching configuration issues, but
|
||||
**be sure to do this temporarily in a controlled environment.** Do not run in
|
||||
production with `debug=True`.
|
||||
|
||||
|
||||
.. _working-with-debuggers:
|
||||
|
||||
Working with Debuggers
|
||||
----------------------
|
||||
|
||||
To dig deeper, possibly to trace code execution, Flask provides a debugger out
|
||||
of the box (see :ref:`debug-mode`). If you would like to use another Python
|
||||
debugger, note that debuggers interfere with each other. You have to set some
|
||||
options in order to use your favorite debugger:
|
||||
|
||||
* ``debug`` - whether to enable debug mode and catch exceptions
|
||||
* ``use_debugger`` - whether to use the internal Flask debugger
|
||||
* ``use_reloader`` - whether to reload and fork the process if modules
|
||||
were changed
|
||||
|
||||
``debug`` must be True (i.e., exceptions must be caught) in order for the other
|
||||
two options to have any value.
|
||||
|
||||
If you're using Aptana/Eclipse for debugging you'll need to set both
|
||||
``use_debugger`` and ``use_reloader`` to False.
|
||||
|
||||
A possible useful pattern for configuration is to set the following in your
|
||||
config.yaml (change the block as appropriate for your application, of course)::
|
||||
|
||||
FLASK:
|
||||
DEBUG: True
|
||||
DEBUG_WITH_APTANA: True
|
||||
|
||||
Then in your application's entry-point (main.py),
|
||||
you could have something like::
|
||||
|
||||
if __name__ == "__main__":
|
||||
# To allow aptana to receive errors, set use_debugger=False
|
||||
app = create_app(config="config.yaml")
|
||||
|
||||
use_debugger = app.debug and not(app.config.get('DEBUG_WITH_APTANA'))
|
||||
app.run(use_debugger=use_debugger, debug=app.debug,
|
||||
use_reloader=use_debugger, host='0.0.0.0')
|
||||
See :doc:`/debugging` for information about how to debug errors in
|
||||
development and production.
|
||||
|
|
|
|||
|
|
@ -1,312 +1,277 @@
|
|||
Flask Extension Development
|
||||
===========================
|
||||
|
||||
Flask, being a microframework, often requires some repetitive steps to get
|
||||
a third party library working. Many such extensions are already available
|
||||
on `PyPI`_.
|
||||
.. currentmodule:: flask
|
||||
|
||||
If you want to create your own Flask extension for something that does not
|
||||
exist yet, this guide to extension development will help you get your
|
||||
extension running in no time and to feel like users would expect your
|
||||
extension to behave.
|
||||
Extensions are extra packages that add functionality to a Flask
|
||||
application. While `PyPI`_ contains many Flask extensions, you may not
|
||||
find one that fits your need. If this is the case, you can create your
|
||||
own, and publish it for others to use as well.
|
||||
|
||||
Anatomy of an Extension
|
||||
-----------------------
|
||||
This guide will show how to create a Flask extension, and some of the
|
||||
common patterns and requirements involved. Since extensions can do
|
||||
anything, this guide won't be able to cover every possibility.
|
||||
|
||||
Extensions are all located in a package called ``flask_something``
|
||||
where "something" is the name of the library you want to bridge. So for
|
||||
example if you plan to add support for a library named `simplexml` to
|
||||
Flask, you would name your extension's package ``flask_simplexml``.
|
||||
The best ways to learn about extensions are to look at how other
|
||||
extensions you use are written, and discuss with others. Discuss your
|
||||
design ideas with others on our `Discord Chat`_ or
|
||||
`GitHub Discussions`_.
|
||||
|
||||
The name of the actual extension (the human readable name) however would
|
||||
be something like "Flask-SimpleXML". Make sure to include the name
|
||||
"Flask" somewhere in that name and that you check the capitalization.
|
||||
This is how users can then register dependencies to your extension in
|
||||
their :file:`setup.py` files.
|
||||
|
||||
But what do extensions look like themselves? An extension has to ensure
|
||||
that it works with multiple Flask application instances at once. This is
|
||||
a requirement because many people will use patterns like the
|
||||
:ref:`app-factories` pattern to create their application as needed to aid
|
||||
unittests and to support multiple configurations. Because of that it is
|
||||
crucial that your application supports that kind of behavior.
|
||||
|
||||
Most importantly the extension must be shipped with a :file:`setup.py` file and
|
||||
registered on PyPI. Also the development checkout link should work so
|
||||
that people can easily install the development version into their
|
||||
virtualenv without having to download the library by hand.
|
||||
|
||||
Flask extensions must be licensed under a BSD, MIT or more liberal license
|
||||
in order to be listed in the Flask Extension Registry. Keep in mind
|
||||
that the Flask Extension Registry is a moderated place and libraries will
|
||||
be reviewed upfront if they behave as required.
|
||||
|
||||
"Hello Flaskext!"
|
||||
-----------------
|
||||
|
||||
So let's get started with creating such a Flask extension. The extension
|
||||
we want to create here will provide very basic support for SQLite3.
|
||||
|
||||
First we create the following folder structure::
|
||||
|
||||
flask-sqlite3/
|
||||
flask_sqlite3.py
|
||||
LICENSE
|
||||
README
|
||||
|
||||
Here's the contents of the most important files:
|
||||
|
||||
setup.py
|
||||
````````
|
||||
|
||||
The next file that is absolutely required is the :file:`setup.py` file which is
|
||||
used to install your Flask extension. The following contents are
|
||||
something you can work with::
|
||||
|
||||
"""
|
||||
Flask-SQLite3
|
||||
-------------
|
||||
|
||||
This is the description for that library
|
||||
"""
|
||||
from setuptools import setup
|
||||
The best extensions share common patterns, so that anyone familiar with
|
||||
using one extension won't feel completely lost with another. This can
|
||||
only work if collaboration happens early.
|
||||
|
||||
|
||||
setup(
|
||||
name='Flask-SQLite3',
|
||||
version='1.0',
|
||||
url='http://example.com/flask-sqlite3/',
|
||||
license='BSD',
|
||||
author='Your Name',
|
||||
author_email='your-email@example.com',
|
||||
description='Very short description',
|
||||
long_description=__doc__,
|
||||
py_modules=['flask_sqlite3'],
|
||||
# if you would be using a package instead use packages instead
|
||||
# of py_modules:
|
||||
# packages=['flask_sqlite3'],
|
||||
zip_safe=False,
|
||||
include_package_data=True,
|
||||
platforms='any',
|
||||
install_requires=[
|
||||
'Flask'
|
||||
],
|
||||
classifiers=[
|
||||
'Environment :: Web Environment',
|
||||
'Intended Audience :: Developers',
|
||||
'License :: OSI Approved :: BSD License',
|
||||
'Operating System :: OS Independent',
|
||||
'Programming Language :: Python',
|
||||
'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
|
||||
'Topic :: Software Development :: Libraries :: Python Modules'
|
||||
]
|
||||
)
|
||||
Naming
|
||||
------
|
||||
|
||||
That's a lot of code but you can really just copy/paste that from existing
|
||||
extensions and adapt.
|
||||
A Flask extension typically has ``flask`` in its name as a prefix or
|
||||
suffix. If it wraps another library, it should include the library name
|
||||
as well. This makes it easy to search for extensions, and makes their
|
||||
purpose clearer.
|
||||
|
||||
flask_sqlite3.py
|
||||
````````````````
|
||||
A general Python packaging recommendation is that the install name from
|
||||
the package index and the name used in ``import`` statements should be
|
||||
related. The import name is lowercase, with words separated by
|
||||
underscores (``_``). The install name is either lower case or title
|
||||
case, with words separated by dashes (``-``). If it wraps another
|
||||
library, prefer using the same case as that library's name.
|
||||
|
||||
Now this is where your extension code goes. But how exactly should such
|
||||
an extension look like? What are the best practices? Continue reading
|
||||
for some insight.
|
||||
Here are some example install and import names:
|
||||
|
||||
Initializing Extensions
|
||||
-----------------------
|
||||
|
||||
Many extensions will need some kind of initialization step. For example,
|
||||
consider an application that's currently connecting to SQLite like the
|
||||
documentation suggests (:ref:`sqlite3`). So how does the extension
|
||||
know the name of the application object?
|
||||
|
||||
Quite simple: you pass it to it.
|
||||
|
||||
There are two recommended ways for an extension to initialize:
|
||||
|
||||
initialization functions:
|
||||
|
||||
If your extension is called `helloworld` you might have a function
|
||||
called ``init_helloworld(app[, extra_args])`` that initializes the
|
||||
extension for that application. It could attach before / after
|
||||
handlers etc.
|
||||
|
||||
classes:
|
||||
|
||||
Classes work mostly like initialization functions but can later be
|
||||
used to further change the behavior. For an example look at how the
|
||||
`OAuth extension`_ works: there is an `OAuth` object that provides
|
||||
some helper functions like `OAuth.remote_app` to create a reference to
|
||||
a remote application that uses OAuth.
|
||||
|
||||
What to use depends on what you have in mind. For the SQLite 3 extension
|
||||
we will use the class-based approach because it will provide users with an
|
||||
object that handles opening and closing database connections.
|
||||
|
||||
When designing your classes, it's important to make them easily reusable
|
||||
at the module level. This means the object itself must not under any
|
||||
circumstances store any application specific state and must be shareable
|
||||
between different applications.
|
||||
|
||||
The Extension Code
|
||||
------------------
|
||||
|
||||
Here's the contents of the `flask_sqlite3.py` for copy/paste::
|
||||
|
||||
import sqlite3
|
||||
from flask import current_app, _app_ctx_stack
|
||||
- ``Flask-Name`` imported as ``flask_name``
|
||||
- ``flask-name-lower`` imported as ``flask_name_lower``
|
||||
- ``Flask-ComboName`` imported as ``flask_comboname``
|
||||
- ``Name-Flask`` imported as ``name_flask``
|
||||
|
||||
|
||||
class SQLite3(object):
|
||||
The Extension Class and Initialization
|
||||
--------------------------------------
|
||||
|
||||
All extensions will need some entry point that initializes the
|
||||
extension with the application. The most common pattern is to create a
|
||||
class that represents the extension's configuration and behavior, with
|
||||
an ``init_app`` method to apply the extension instance to the given
|
||||
application instance.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class HelloExtension:
|
||||
def __init__(self, app=None):
|
||||
self.app = app
|
||||
if app is not None:
|
||||
self.init_app(app)
|
||||
|
||||
def init_app(self, app):
|
||||
app.config.setdefault('SQLITE3_DATABASE', ':memory:')
|
||||
app.teardown_appcontext(self.teardown)
|
||||
app.before_request(...)
|
||||
|
||||
def connect(self):
|
||||
return sqlite3.connect(current_app.config['SQLITE3_DATABASE'])
|
||||
It is important that the app is not stored on the extension, don't do
|
||||
``self.app = app``. The only time the extension should have direct
|
||||
access to an app is during ``init_app``, otherwise it should use
|
||||
:data:`.current_app`.
|
||||
|
||||
def teardown(self, exception):
|
||||
ctx = _app_ctx_stack.top
|
||||
if hasattr(ctx, 'sqlite3_db'):
|
||||
ctx.sqlite3_db.close()
|
||||
This allows the extension to support the application factory pattern,
|
||||
avoids circular import issues when importing the extension instance
|
||||
elsewhere in a user's code, and makes testing with different
|
||||
configurations easier.
|
||||
|
||||
@property
|
||||
def connection(self):
|
||||
ctx = _app_ctx_stack.top
|
||||
if ctx is not None:
|
||||
if not hasattr(ctx, 'sqlite3_db'):
|
||||
ctx.sqlite3_db = self.connect()
|
||||
return ctx.sqlite3_db
|
||||
.. code-block:: python
|
||||
|
||||
hello = HelloExtension()
|
||||
|
||||
def create_app():
|
||||
app = Flask(__name__)
|
||||
hello.init_app(app)
|
||||
return app
|
||||
|
||||
Above, the ``hello`` extension instance exists independently of the
|
||||
application. This means that other modules in a user's project can do
|
||||
``from project import hello`` and use the extension in blueprints before
|
||||
the app exists.
|
||||
|
||||
The :attr:`Flask.extensions` dict can be used to store a reference to
|
||||
the extension on the application, or some other state specific to the
|
||||
application. Be aware that this is a single namespace, so use a name
|
||||
unique to your extension, such as the extension's name without the
|
||||
"flask" prefix.
|
||||
|
||||
|
||||
So here's what these lines of code do:
|
||||
Adding Behavior
|
||||
---------------
|
||||
|
||||
1. The ``__init__`` method takes an optional app object and, if supplied, will
|
||||
call ``init_app``.
|
||||
2. The ``init_app`` method exists so that the ``SQLite3`` object can be
|
||||
instantiated without requiring an app object. This method supports the
|
||||
factory pattern for creating applications. The ``init_app`` will set the
|
||||
configuration for the database, defaulting to an in memory database if
|
||||
no configuration is supplied. In addition, the ``init_app`` method
|
||||
attaches the ``teardown`` handler.
|
||||
3. Next, we define a ``connect`` method that opens a database connection.
|
||||
4. Finally, we add a ``connection`` property that on first access opens
|
||||
the database connection and stores it on the context. This is also
|
||||
the recommended way to handling resources: fetch resources lazily the
|
||||
first time they are used.
|
||||
There are many ways that an extension can add behavior. Any setup
|
||||
methods that are available on the :class:`Flask` object can be used
|
||||
during an extension's ``init_app`` method.
|
||||
|
||||
Note here that we're attaching our database connection to the top
|
||||
application context via ``_app_ctx_stack.top``. Extensions should use
|
||||
the top context for storing their own information with a sufficiently
|
||||
complex name.
|
||||
A common pattern is to use :meth:`~Flask.before_request` to initialize
|
||||
some data or a connection at the beginning of each request, then
|
||||
:meth:`~Flask.teardown_request` to clean it up at the end. This can be
|
||||
stored on :data:`.g`, discussed more below.
|
||||
|
||||
So why did we decide on a class-based approach here? Because using our
|
||||
extension looks something like this::
|
||||
A more lazy approach is to provide a method that initializes and caches
|
||||
the data or connection. For example, a ``ext.get_db`` method could
|
||||
create a database connection the first time it's called, so that a view
|
||||
that doesn't use the database doesn't create a connection.
|
||||
|
||||
from flask import Flask
|
||||
from flask_sqlite3 import SQLite3
|
||||
Besides doing something before and after every view, your extension
|
||||
might want to add some specific views as well. In this case, you could
|
||||
define a :class:`Blueprint`, then call :meth:`~Flask.register_blueprint`
|
||||
during ``init_app`` to add the blueprint to the app.
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config.from_pyfile('the-config.cfg')
|
||||
db = SQLite3(app)
|
||||
|
||||
You can then use the database from views like this::
|
||||
Configuration Techniques
|
||||
------------------------
|
||||
|
||||
@app.route('/')
|
||||
def show_all():
|
||||
cur = db.connection.cursor()
|
||||
cur.execute(...)
|
||||
There can be multiple levels and sources of configuration for an
|
||||
extension. You should consider what parts of your extension fall into
|
||||
each one.
|
||||
|
||||
Likewise if you are outside of a request you can use the database by
|
||||
pushing an app context::
|
||||
- Configuration per application instance, through ``app.config``
|
||||
values. This is configuration that could reasonably change for each
|
||||
deployment of an application. A common example is a URL to an
|
||||
external resource, such as a database. Configuration keys should
|
||||
start with the extension's name so that they don't interfere with
|
||||
other extensions.
|
||||
- Configuration per extension instance, through ``__init__``
|
||||
arguments. This configuration usually affects how the extension
|
||||
is used, such that it wouldn't make sense to change it per
|
||||
deployment.
|
||||
- Configuration per extension instance, through instance attributes
|
||||
and decorator methods. It might be more ergonomic to assign to
|
||||
``ext.value``, or use a ``@ext.register`` decorator to register a
|
||||
function, after the extension instance has been created.
|
||||
- Global configuration through class attributes. Changing a class
|
||||
attribute like ``Ext.connection_class`` can customize default
|
||||
behavior without making a subclass. This could be combined
|
||||
per-extension configuration to override defaults.
|
||||
- Subclassing and overriding methods and attributes. Making the API of
|
||||
the extension itself something that can be overridden provides a
|
||||
very powerful tool for advanced customization.
|
||||
|
||||
with app.app_context():
|
||||
cur = db.connection.cursor()
|
||||
cur.execute(...)
|
||||
The :class:`~flask.Flask` object itself uses all of these techniques.
|
||||
|
||||
At the end of the ``with`` block the teardown handles will be executed
|
||||
automatically.
|
||||
It's up to you to decide what configuration is appropriate for your
|
||||
extension, based on what you need and what you want to support.
|
||||
|
||||
Additionally, the ``init_app`` method is used to support the factory pattern
|
||||
for creating apps::
|
||||
Configuration should not be changed after the application setup phase is
|
||||
complete and the server begins handling requests. Configuration is
|
||||
global, any changes to it are not guaranteed to be visible to other
|
||||
workers.
|
||||
|
||||
db = SQLite3()
|
||||
# Then later on.
|
||||
app = create_app('the-config.cfg')
|
||||
|
||||
Data During a Request
|
||||
---------------------
|
||||
|
||||
When writing a Flask application, the :data:`~flask.g` object is used to
|
||||
store information during a request. For example the
|
||||
:doc:`tutorial <tutorial/database>` stores a connection to a SQLite
|
||||
database as ``g.db``. Extensions can also use this, with some care.
|
||||
Since ``g`` is a single global namespace, extensions must use unique
|
||||
names that won't collide with user data. For example, use the extension
|
||||
name as a prefix, or as a namespace.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# an internal prefix with the extension name
|
||||
g._hello_user_id = 2
|
||||
|
||||
# or an internal prefix as a namespace
|
||||
from types import SimpleNamespace
|
||||
g._hello = SimpleNamespace()
|
||||
g._hello.user_id = 2
|
||||
|
||||
The data in ``g`` lasts for an application context. An application context is
|
||||
active during a request, CLI command, or ``with app.app_context()`` block. If
|
||||
you're storing something that should be closed, use
|
||||
:meth:`~flask.Flask.teardown_appcontext` to ensure that it gets closed when the
|
||||
app context ends. If it should only be valid during a request, or would not be
|
||||
used in the CLI outside a request, use :meth:`~flask.Flask.teardown_request`.
|
||||
|
||||
|
||||
Views and Models
|
||||
----------------
|
||||
|
||||
Your extension views might want to interact with specific models in your
|
||||
database, or some other extension or data connected to your application.
|
||||
For example, let's consider a ``Flask-SimpleBlog`` extension that works
|
||||
with Flask-SQLAlchemy to provide a ``Post`` model and views to write
|
||||
and read posts.
|
||||
|
||||
The ``Post`` model needs to subclass the Flask-SQLAlchemy ``db.Model``
|
||||
object, but that's only available once you've created an instance of
|
||||
that extension, not when your extension is defining its views. So how
|
||||
can the view code, defined before the model exists, access the model?
|
||||
|
||||
One method could be to use :doc:`views`. During ``__init__``, create
|
||||
the model, then create the views by passing the model to the view
|
||||
class's :meth:`~views.View.as_view` method.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class PostAPI(MethodView):
|
||||
def __init__(self, model):
|
||||
self.model = model
|
||||
|
||||
def get(self, id):
|
||||
post = self.model.query.get(id)
|
||||
return jsonify(post.to_json())
|
||||
|
||||
class BlogExtension:
|
||||
def __init__(self, db):
|
||||
class Post(db.Model):
|
||||
id = db.Column(primary_key=True)
|
||||
title = db.Column(db.String, nullable=False)
|
||||
|
||||
self.post_model = Post
|
||||
|
||||
def init_app(self, app):
|
||||
api_view = PostAPI.as_view(model=self.post_model)
|
||||
|
||||
db = SQLAlchemy()
|
||||
blog = BlogExtension(db)
|
||||
db.init_app(app)
|
||||
blog.init_app(app)
|
||||
|
||||
Keep in mind that supporting this factory pattern for creating apps is required
|
||||
for approved flask extensions (described below).
|
||||
Another technique could be to use an attribute on the extension, such as
|
||||
``self.post_model`` from above. Add the extension to ``app.extensions``
|
||||
in ``init_app``, then access
|
||||
``current_app.extensions["simple_blog"].post_model`` from views.
|
||||
|
||||
.. admonition:: Note on ``init_app``
|
||||
You may also want to provide base classes so that users can provide
|
||||
their own ``Post`` model that conforms to the API your extension
|
||||
expects. So they could implement ``class Post(blog.BasePost)``, then
|
||||
set it as ``blog.post_model``.
|
||||
|
||||
As you noticed, ``init_app`` does not assign ``app`` to ``self``. This
|
||||
is intentional! Class based Flask extensions must only store the
|
||||
application on the object when the application was passed to the
|
||||
constructor. This tells the extension: I am not interested in using
|
||||
multiple applications.
|
||||
|
||||
When the extension needs to find the current application and it does
|
||||
not have a reference to it, it must either use the
|
||||
:data:`~flask.current_app` context local or change the API in a way
|
||||
that you can pass the application explicitly.
|
||||
As you can see, this can get a bit complex. Unfortunately, there's no
|
||||
perfect solution here, only different strategies and tradeoffs depending
|
||||
on your needs and how much customization you want to offer. Luckily,
|
||||
this sort of resource dependency is not a common need for most
|
||||
extensions. Remember, if you need help with design, ask on our
|
||||
`Discord Chat`_ or `GitHub Discussions`_.
|
||||
|
||||
|
||||
Using _app_ctx_stack
|
||||
--------------------
|
||||
Recommended Extension Guidelines
|
||||
--------------------------------
|
||||
|
||||
In the example above, before every request, a ``sqlite3_db`` variable is
|
||||
assigned to ``_app_ctx_stack.top``. In a view function, this variable is
|
||||
accessible using the ``connection`` property of ``SQLite3``. During the
|
||||
teardown of a request, the ``sqlite3_db`` connection is closed. By using
|
||||
this pattern, the *same* connection to the sqlite3 database is accessible
|
||||
to anything that needs it for the duration of the request.
|
||||
|
||||
|
||||
Learn from Others
|
||||
-----------------
|
||||
|
||||
This documentation only touches the bare minimum for extension development.
|
||||
If you want to learn more, it's a very good idea to check out existing extensions
|
||||
on the `PyPI`_. If you feel lost there is still the `mailinglist`_ and the
|
||||
`Discord server`_ to get some ideas for nice looking APIs. Especially if you do
|
||||
something nobody before you did, it might be a very good idea to get some more
|
||||
input. This not only generates useful feedback on what people might want from
|
||||
an extension, but also avoids having multiple developers working in isolation
|
||||
on pretty much the same problem.
|
||||
|
||||
Remember: good API design is hard, so introduce your project on the
|
||||
mailing list, and let other developers give you a helping hand with
|
||||
designing the API.
|
||||
|
||||
The best Flask extensions are extensions that share common idioms for the
|
||||
API. And this can only work if collaboration happens early.
|
||||
|
||||
Approved Extensions
|
||||
-------------------
|
||||
|
||||
Flask previously had the concept of approved extensions. These came with
|
||||
some vetting of support and compatibility. While this list became too
|
||||
difficult to maintain over time, the guidelines are still relevant to
|
||||
all extensions maintained and developed today, as they help the Flask
|
||||
Flask previously had the concept of "approved extensions", where the
|
||||
Flask maintainers evaluated the quality, support, and compatibility of
|
||||
the extensions before listing them. While the list became too difficult
|
||||
to maintain over time, the guidelines are still relevant to all
|
||||
extensions maintained and developed today, as they help the Flask
|
||||
ecosystem remain consistent and compatible.
|
||||
|
||||
0. An approved Flask extension requires a maintainer. In the event an
|
||||
extension author would like to move beyond the project, the project
|
||||
should find a new maintainer and transfer access to the repository,
|
||||
documentation, PyPI, and any other services. If no maintainer
|
||||
is available, give access to the Pallets core team.
|
||||
1. The naming scheme is *Flask-ExtensionName* or *ExtensionName-Flask*.
|
||||
1. An extension requires a maintainer. In the event an extension author
|
||||
would like to move beyond the project, the project should find a new
|
||||
maintainer and transfer access to the repository, documentation,
|
||||
PyPI, and any other services. The `Pallets-Eco`_ organization on
|
||||
GitHub allows for community maintenance with oversight from the
|
||||
Pallets maintainers.
|
||||
2. The naming scheme is *Flask-ExtensionName* or *ExtensionName-Flask*.
|
||||
It must provide exactly one package or module named
|
||||
``flask_extension_name``.
|
||||
2. The extension must be BSD or MIT licensed. It must be open source
|
||||
and publicly available.
|
||||
3. The extension's API must have the following characteristics:
|
||||
3. The extension must use an open source license. The Python web
|
||||
ecosystem tends to prefer BSD or MIT. It must be open source and
|
||||
publicly available.
|
||||
4. The extension's API must have the following characteristics:
|
||||
|
||||
- It must support multiple applications running in the same Python
|
||||
process. Use ``current_app`` instead of ``self.app``, store
|
||||
|
|
@ -314,22 +279,27 @@ ecosystem remain consistent and compatible.
|
|||
- It must be possible to use the factory pattern for creating
|
||||
applications. Use the ``ext.init_app()`` pattern.
|
||||
|
||||
4. From a clone of the repository, an extension with its dependencies
|
||||
must be installable with ``pip install -e .``.
|
||||
5. It must ship a testing suite that can be invoked with ``tox -e py``
|
||||
or ``pytest``. If not using ``tox``, the test dependencies should be
|
||||
specified in a ``requirements.txt`` file. The tests must be part of
|
||||
the sdist distribution.
|
||||
6. The documentation must use the ``flask`` theme from the
|
||||
`Official Pallets Themes`_. A link to the documentation or project
|
||||
website must be in the PyPI metadata or the readme.
|
||||
7. For maximum compatibility, the extension should support the same
|
||||
versions of Python that Flask supports. 3.6+ is recommended as of
|
||||
2020. Use ``python_requires=">= 3.6"`` in ``setup.py`` to indicate
|
||||
supported versions.
|
||||
5. From a clone of the repository, an extension with its dependencies
|
||||
must be installable in editable mode with ``pip install -e .``.
|
||||
6. It must ship tests that can be invoked with a common tool like
|
||||
``tox -e py``, ``nox -s test`` or ``pytest``. If not using ``tox``,
|
||||
the test dependencies should be specified in a requirements file.
|
||||
The tests must be part of the sdist distribution.
|
||||
7. A link to the documentation or project website must be in the PyPI
|
||||
metadata or the readme. The documentation should use the Flask theme
|
||||
from the `Official Pallets Themes`_.
|
||||
8. The extension's dependencies should not use upper bounds or assume
|
||||
any particular version scheme, but should use lower bounds to
|
||||
indicate minimum compatibility support. For example,
|
||||
``sqlalchemy>=1.4``.
|
||||
9. Indicate the versions of Python supported using ``python_requires=">=version"``.
|
||||
Flask and Pallets policy is to support all Python versions that are not
|
||||
within six months of end of life (EOL). See Python's `EOL calendar`_ for
|
||||
timing.
|
||||
|
||||
.. _PyPI: https://pypi.org/search/?c=Framework+%3A%3A+Flask
|
||||
.. _OAuth extension: https://pythonhosted.org/Flask-OAuth/
|
||||
.. _mailinglist: https://mail.python.org/mailman/listinfo/flask
|
||||
.. _Discord server: https://discord.gg/t6rrQZH
|
||||
.. _Discord Chat: https://discord.gg/pallets
|
||||
.. _GitHub Discussions: https://github.com/pallets/flask/discussions
|
||||
.. _Official Pallets Themes: https://pypi.org/project/Pallets-Sphinx-Themes/
|
||||
.. _Pallets-Eco: https://github.com/pallets-eco
|
||||
.. _EOL calendar: https://devguide.python.org/versions/
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
.. _extensions:
|
||||
|
||||
Extensions
|
||||
==========
|
||||
|
||||
|
|
@ -41,10 +39,10 @@ an extension called "Flask-Foo" might be used like this::
|
|||
Building Extensions
|
||||
-------------------
|
||||
|
||||
While the `PyPI <pypi_>`_ contains many Flask extensions, you may
|
||||
not find an extension that fits your need. If this is the case, you can
|
||||
create your own. Read :doc:`/extensiondev` to develop your own Flask
|
||||
extension.
|
||||
While `PyPI <pypi_>`_ contains many Flask extensions, you may not find
|
||||
an extension that fits your need. If this is the case, you can create
|
||||
your own, and publish it for others to use as well. Read
|
||||
:doc:`extensiondev` to develop your own Flask extension.
|
||||
|
||||
|
||||
.. _pypi: https://pypi.org/search/?c=Framework+%3A%3A+Flask
|
||||
|
|
|
|||
|
|
@ -1,58 +0,0 @@
|
|||
Foreword
|
||||
========
|
||||
|
||||
Read this before you get started with Flask. This hopefully answers some
|
||||
questions about the purpose and goals of the project, and when you
|
||||
should or should not be using it.
|
||||
|
||||
What does "micro" mean?
|
||||
-----------------------
|
||||
|
||||
“Micro” does not mean that your whole web application has to fit into a single
|
||||
Python file (although it certainly can), nor does it mean that Flask is lacking
|
||||
in functionality. The "micro" in microframework means Flask aims to keep the
|
||||
core simple but extensible. Flask won't make many decisions for you, such as
|
||||
what database to use. Those decisions that it does make, such as what
|
||||
templating engine to use, are easy to change. Everything else is up to you, so
|
||||
that Flask can be everything you need and nothing you don't.
|
||||
|
||||
By default, Flask does not include a database abstraction layer, form
|
||||
validation or anything else where different libraries already exist that can
|
||||
handle that. Instead, Flask supports extensions to add such functionality to
|
||||
your application as if it was implemented in Flask itself. Numerous extensions
|
||||
provide database integration, form validation, upload handling, various open
|
||||
authentication technologies, and more. Flask may be "micro", but it's ready for
|
||||
production use on a variety of needs.
|
||||
|
||||
Configuration and Conventions
|
||||
-----------------------------
|
||||
|
||||
Flask has many configuration values, with sensible defaults, and a few
|
||||
conventions when getting started. By convention, templates and static
|
||||
files are stored in subdirectories within the application's Python
|
||||
source tree, with the names :file:`templates` and :file:`static`
|
||||
respectively. While this can be changed, you usually don't have to,
|
||||
especially when getting started.
|
||||
|
||||
Growing with Flask
|
||||
------------------
|
||||
|
||||
Once you have Flask up and running, you'll find a variety of extensions
|
||||
available in the community to integrate your project for production. The Flask
|
||||
core team reviews extensions and ensures approved extensions do not break with
|
||||
future releases.
|
||||
|
||||
As your codebase grows, you are free to make the design decisions appropriate
|
||||
for your project. Flask will continue to provide a very simple glue layer to
|
||||
the best that Python has to offer. You can implement advanced patterns in
|
||||
SQLAlchemy or another database tool, introduce non-relational data persistence
|
||||
as appropriate, and take advantage of framework-agnostic tools built for WSGI,
|
||||
the Python web interface.
|
||||
|
||||
Flask includes many hooks to customize its behavior. Should you need more
|
||||
customization, the Flask class is built for subclassing. If you are interested
|
||||
in that, check out the :ref:`becomingbig` chapter. If you are curious about
|
||||
the Flask design principles, head over to the section about :ref:`design`.
|
||||
|
||||
Continue to :ref:`installation`, the :ref:`quickstart`, or the
|
||||
:ref:`advanced_foreword`.
|
||||
125
docs/gevent.rst
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
Async with Gevent
|
||||
=================
|
||||
|
||||
`Gevent`_ patches Python's standard library to run within special async workers
|
||||
called `greenlets`_. Gevent has existed since long before Python's native
|
||||
asyncio was available, and Flask has always worked with it.
|
||||
|
||||
.. _gevent: https://www.gevent.org
|
||||
.. _greenlets: https://greenlet.readthedocs.io
|
||||
|
||||
Gevent is a reliable way to handle numerous, long lived, concurrent connections,
|
||||
and to achieve similar capabilities to ASGI and asyncio. This works without
|
||||
needing to write ``async def`` or ``await`` anywhere, but relies on gevent and
|
||||
greenlet's low level manipulation of the Python interpreter.
|
||||
|
||||
Deciding whether you should use gevent with Flask, or `Quart`_, or something
|
||||
else, is ultimately up to understanding the specific needs of your project.
|
||||
|
||||
.. _quart: https://quart.palletsprojects.com
|
||||
|
||||
|
||||
Enabling gevent
|
||||
---------------
|
||||
|
||||
You need to apply gevent's patching as early as possible in your code. This
|
||||
enables gevent's underlying event loop and converts many Python internals to run
|
||||
inside it. Add the following at the top of your project's module or top
|
||||
``__init__.py``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import gevent.monkey
|
||||
gevent.monkey.patch_all()
|
||||
|
||||
When deploying in production, use :doc:`/deploying/gunicorn` or
|
||||
:doc:`/deploying/uwsgi` with a gevent worker, as described on those pages.
|
||||
|
||||
To run concurrent tasks within your own code, such as views, use
|
||||
|gevent.spawn|_:
|
||||
|
||||
.. |gevent.spawn| replace:: ``gevent.spawn()``
|
||||
.. _gevent.spawn: https://www.gevent.org/api/gevent.html#gevent.spawn
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@app.post("/send")
|
||||
def send_email():
|
||||
gevent.spawn(email.send, to="example@example.example", text="example")
|
||||
return "Email is being sent."
|
||||
|
||||
If you need to access :data:`request` or other Flask context globals within the
|
||||
spawned function, decorate the function with :func:`.stream_with_context` or
|
||||
:func:`.copy_current_request_context`. Prefer passing the exact data you need
|
||||
when spawning the function, rather than using the decorators.
|
||||
|
||||
.. note::
|
||||
|
||||
When using gevent, greenlet>=1.0 is required. When using PyPy, PyPy>=7.3.7
|
||||
is required.
|
||||
|
||||
|
||||
.. _gevent-asyncio:
|
||||
|
||||
Combining with ``async``/``await``
|
||||
----------------------------------
|
||||
|
||||
Gevent's patching does not interact well with Flask's built-in asyncio support.
|
||||
If you want to use Gevent and asyncio in the same app, you'll need to override
|
||||
:meth:`flask.Flask.async_to_sync` to run async functions inside gevent.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import gevent.monkey
|
||||
gevent.monkey.patch_all()
|
||||
|
||||
import asyncio
|
||||
from flask import Flask, request
|
||||
|
||||
loop = asyncio.EventLoop()
|
||||
gevent.spawn(loop.run_forever)
|
||||
|
||||
class GeventFlask(Flask):
|
||||
def async_to_sync(self, func):
|
||||
def run(*args, **kwargs):
|
||||
coro = func(*args, **kwargs)
|
||||
future = asyncio.run_coroutine_threadsafe(coro, loop)
|
||||
return future.result()
|
||||
|
||||
return run
|
||||
|
||||
app = GeventFlask(__name__)
|
||||
|
||||
@app.get("/")
|
||||
async def greet():
|
||||
await asyncio.sleep(1)
|
||||
return f"Hello, {request.args.get("name", "World")}!"
|
||||
|
||||
This starts an asyncio event loop in a gevent worker. Async functions are
|
||||
scheduled on that event loop. This may still have limitations, and may need to
|
||||
be modified further when using other asyncio implementations.
|
||||
|
||||
|
||||
libuv
|
||||
~~~~~
|
||||
|
||||
`libuv`_ is another event loop implementation that `gevent supports`_. There's
|
||||
also a project called `uvloop`_ that enables libuv in asyncio. If you want to
|
||||
use libuv, use gevent's support, not uvloop. It may be possible to further
|
||||
modify the ``async_to_sync`` code from the previous section to work with uvloop,
|
||||
but that's not currently known.
|
||||
|
||||
.. _libuv: https://libuv.org/
|
||||
.. _gevent supports: https://www.gevent.org/loop_impls.html
|
||||
.. _uvloop: https://uvloop.readthedocs.io/
|
||||
|
||||
To enable gevent's libuv support, add the following at the *very* top of your
|
||||
code, before ``gevent.monkey.patch_all()``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import gevent
|
||||
gevent.config.loop = "libuv"
|
||||
|
||||
import gevent.monkey
|
||||
gevent.monkey.patch_all()
|
||||
207
docs/htmlfaq.rst
|
|
@ -1,207 +0,0 @@
|
|||
HTML/XHTML FAQ
|
||||
==============
|
||||
|
||||
The Flask documentation and example applications are using HTML5. You
|
||||
may notice that in many situations, when end tags are optional they are
|
||||
not used, so that the HTML is cleaner and faster to load. Because there
|
||||
is much confusion about HTML and XHTML among developers, this document tries
|
||||
to answer some of the major questions.
|
||||
|
||||
|
||||
History of XHTML
|
||||
----------------
|
||||
|
||||
For a while, it appeared that HTML was about to be replaced by XHTML.
|
||||
However, barely any websites on the Internet are actual XHTML (which is
|
||||
HTML processed using XML rules). There are a couple of major reasons
|
||||
why this is the case. One of them is Internet Explorer's lack of proper
|
||||
XHTML support. The XHTML spec states that XHTML must be served with the MIME
|
||||
type :mimetype:`application/xhtml+xml`, but Internet Explorer refuses
|
||||
to read files with that MIME type.
|
||||
While it is relatively easy to configure Web servers to serve XHTML properly,
|
||||
few people do. This is likely because properly using XHTML can be quite
|
||||
painful.
|
||||
|
||||
One of the most important causes of pain is XML's draconian (strict and
|
||||
ruthless) error handling. When an XML parsing error is encountered,
|
||||
the browser is supposed to show the user an ugly error message, instead
|
||||
of attempting to recover from the error and display what it can. Most of
|
||||
the (X)HTML generation on the web is based on non-XML template engines
|
||||
(such as Jinja, the one used in Flask) which do not protect you from
|
||||
accidentally creating invalid XHTML. There are XML based template engines,
|
||||
such as Kid and the popular Genshi, but they often come with a larger
|
||||
runtime overhead and are not as straightforward to use because they have
|
||||
to obey XML rules.
|
||||
|
||||
The majority of users, however, assumed they were properly using XHTML.
|
||||
They wrote an XHTML doctype at the top of the document and self-closed all
|
||||
the necessary tags (``<br>`` becomes ``<br/>`` or ``<br></br>`` in XHTML).
|
||||
However, even if the document properly validates as XHTML, what really
|
||||
determines XHTML/HTML processing in browsers is the MIME type, which as
|
||||
said before is often not set properly. So the valid XHTML was being treated
|
||||
as invalid HTML.
|
||||
|
||||
XHTML also changed the way JavaScript is used. To properly work with XHTML,
|
||||
programmers have to use the namespaced DOM interface with the XHTML
|
||||
namespace to query for HTML elements.
|
||||
|
||||
History of HTML5
|
||||
----------------
|
||||
|
||||
Development of the HTML5 specification was started in 2004 under the name
|
||||
"Web Applications 1.0" by the Web Hypertext Application Technology Working
|
||||
Group, or WHATWG (which was formed by the major browser vendors Apple,
|
||||
Mozilla, and Opera) with the goal of writing a new and improved HTML
|
||||
specification, based on existing browser behavior instead of unrealistic
|
||||
and backwards-incompatible specifications.
|
||||
|
||||
For example, in HTML4 ``<title/Hello/`` theoretically parses exactly the
|
||||
same as ``<title>Hello</title>``. However, since people were using
|
||||
XHTML-like tags along the lines of ``<link />``, browser vendors implemented
|
||||
the XHTML syntax over the syntax defined by the specification.
|
||||
|
||||
In 2007, the specification was adopted as the basis of a new HTML
|
||||
specification under the umbrella of the W3C, known as HTML5. Currently,
|
||||
it appears that XHTML is losing traction, as the XHTML 2 working group has
|
||||
been disbanded and HTML5 is being implemented by all major browser vendors.
|
||||
|
||||
HTML versus XHTML
|
||||
-----------------
|
||||
|
||||
The following table gives you a quick overview of features available in
|
||||
HTML 4.01, XHTML 1.1 and HTML5. (XHTML 1.0 is not included, as it was
|
||||
superseded by XHTML 1.1 and the barely-used XHTML5.)
|
||||
|
||||
.. tabularcolumns:: |p{9cm}|p{2cm}|p{2cm}|p{2cm}|
|
||||
|
||||
+-----------------------------------------+----------+----------+----------+
|
||||
| | HTML4.01 | XHTML1.1 | HTML5 |
|
||||
+=========================================+==========+==========+==========+
|
||||
| ``<tag/value/`` == ``<tag>value</tag>`` | |Y| [1]_ | |N| | |N| |
|
||||
+-----------------------------------------+----------+----------+----------+
|
||||
| ``<br/>`` supported | |N| | |Y| | |Y| [2]_ |
|
||||
+-----------------------------------------+----------+----------+----------+
|
||||
| ``<script/>`` supported | |N| | |Y| | |N| |
|
||||
+-----------------------------------------+----------+----------+----------+
|
||||
| should be served as `text/html` | |Y| | |N| [3]_ | |Y| |
|
||||
+-----------------------------------------+----------+----------+----------+
|
||||
| should be served as | |N| | |Y| | |N| |
|
||||
| `application/xhtml+xml` | | | |
|
||||
+-----------------------------------------+----------+----------+----------+
|
||||
| strict error handling | |N| | |Y| | |N| |
|
||||
+-----------------------------------------+----------+----------+----------+
|
||||
| inline SVG | |N| | |Y| | |Y| |
|
||||
+-----------------------------------------+----------+----------+----------+
|
||||
| inline MathML | |N| | |Y| | |Y| |
|
||||
+-----------------------------------------+----------+----------+----------+
|
||||
| ``<video>`` tag | |N| | |N| | |Y| |
|
||||
+-----------------------------------------+----------+----------+----------+
|
||||
| ``<audio>`` tag | |N| | |N| | |Y| |
|
||||
+-----------------------------------------+----------+----------+----------+
|
||||
| New semantic tags like ``<article>`` | |N| | |N| | |Y| |
|
||||
+-----------------------------------------+----------+----------+----------+
|
||||
|
||||
.. [1] This is an obscure feature inherited from SGML. It is usually not
|
||||
supported by browsers, for reasons detailed above.
|
||||
.. [2] This is for compatibility with server code that generates XHTML for
|
||||
tags such as ``<br>``. It should not be used in new code.
|
||||
.. [3] XHTML 1.0 is the last XHTML standard that allows to be served
|
||||
as `text/html` for backwards compatibility reasons.
|
||||
|
||||
.. |Y| image:: _static/yes.png
|
||||
:alt: Yes
|
||||
.. |N| image:: _static/no.png
|
||||
:alt: No
|
||||
|
||||
What does "strict" mean?
|
||||
------------------------
|
||||
|
||||
HTML5 has strictly defined parsing rules, but it also specifies exactly
|
||||
how a browser should react to parsing errors - unlike XHTML, which simply
|
||||
states parsing should abort. Some people are confused by apparently
|
||||
invalid syntax that still generates the expected results (for example,
|
||||
missing end tags or unquoted attribute values).
|
||||
|
||||
Some of these work because of the lenient error handling most browsers use
|
||||
when they encounter a markup error, others are actually specified. The
|
||||
following constructs are optional in HTML5 by standard, but have to be
|
||||
supported by browsers:
|
||||
|
||||
- Wrapping the document in an ``<html>`` tag
|
||||
- Wrapping header elements in ``<head>`` or the body elements in
|
||||
``<body>``
|
||||
- Closing the ``<p>``, ``<li>``, ``<dt>``, ``<dd>``, ``<tr>``,
|
||||
``<td>``, ``<th>``, ``<tbody>``, ``<thead>``, or ``<tfoot>`` tags.
|
||||
- Quoting attributes, so long as they contain no whitespace or
|
||||
special characters (like ``<``, ``>``, ``'``, or ``"``).
|
||||
- Requiring boolean attributes to have a value.
|
||||
|
||||
This means the following page in HTML5 is perfectly valid:
|
||||
|
||||
.. sourcecode:: html
|
||||
|
||||
<!doctype html>
|
||||
<title>Hello HTML5</title>
|
||||
<div class=header>
|
||||
<h1>Hello HTML5</h1>
|
||||
<p class=tagline>HTML5 is awesome
|
||||
</div>
|
||||
<ul class=nav>
|
||||
<li><a href=/index>Index</a>
|
||||
<li><a href=/downloads>Downloads</a>
|
||||
<li><a href=/about>About</a>
|
||||
</ul>
|
||||
<div class=body>
|
||||
<h2>HTML5 is probably the future</h2>
|
||||
<p>
|
||||
There might be some other things around but in terms of
|
||||
browser vendor support, HTML5 is hard to beat.
|
||||
<dl>
|
||||
<dt>Key 1
|
||||
<dd>Value 1
|
||||
<dt>Key 2
|
||||
<dd>Value 2
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
|
||||
New technologies in HTML5
|
||||
-------------------------
|
||||
|
||||
HTML5 adds many new features that make Web applications easier to write
|
||||
and to use.
|
||||
|
||||
- The ``<audio>`` and ``<video>`` tags provide a way to embed audio and
|
||||
video without complicated add-ons like QuickTime or Flash.
|
||||
- Semantic elements like ``<article>``, ``<header>``, ``<nav>``, and
|
||||
``<time>`` that make content easier to understand.
|
||||
- The ``<canvas>`` tag, which supports a powerful drawing API, reducing
|
||||
the need for server-generated images to present data graphically.
|
||||
- New form control types like ``<input type="date">`` that allow user
|
||||
agents to make entering and validating values easier.
|
||||
- Advanced JavaScript APIs like Web Storage, Web Workers, Web Sockets,
|
||||
geolocation, and offline applications.
|
||||
|
||||
Many other features have been added, as well. A good guide to new features
|
||||
in HTML5 is Mark Pilgrim's soon-to-be-published book, `Dive Into HTML5`_.
|
||||
Not all of them are supported in browsers yet, however, so use caution.
|
||||
|
||||
.. _Dive Into HTML5: https://diveintohtml5.info/
|
||||
|
||||
What should be used?
|
||||
--------------------
|
||||
|
||||
Currently, the answer is HTML5. There are very few reasons to use XHTML
|
||||
considering the latest developments in Web browsers. To summarize the
|
||||
reasons given above:
|
||||
|
||||
- Internet Explorer (which, sadly, currently leads in market share)
|
||||
has poor support for XHTML.
|
||||
- Many JavaScript libraries also do not support XHTML, due to the more
|
||||
complicated namespacing API it requires.
|
||||
- HTML5 adds several new features, including semantic tags and the
|
||||
long-awaited ``<audio>`` and ``<video>`` tags.
|
||||
- It has the support of most browser vendors behind it.
|
||||
- It is much easier to write, and more compact.
|
||||
|
||||
For most applications, it is undoubtedly better to use HTML5 than XHTML.
|
||||
|
|
@ -1,64 +1,67 @@
|
|||
.. rst-class:: hide-header
|
||||
|
||||
.. rst-class:: hide-header
|
||||
|
||||
Welcome to Flask
|
||||
================
|
||||
|
||||
.. image:: _static/flask-logo.png
|
||||
:alt: Flask: web development, one drop at a time
|
||||
.. image:: _static/flask-name.svg
|
||||
:align: center
|
||||
:target: https://palletsprojects.com/p/flask/
|
||||
:height: 200px
|
||||
|
||||
Welcome to Flask's documentation. Get started with :ref:`installation`
|
||||
and then get an overview with the :ref:`quickstart`. There is also a
|
||||
more detailed :ref:`tutorial` that shows how to create a small but
|
||||
Welcome to Flask's documentation. Flask is a lightweight WSGI web application framework.
|
||||
It is designed to make getting started quick and easy, with the ability to scale up to
|
||||
complex applications.
|
||||
|
||||
Get started with :doc:`installation`
|
||||
and then get an overview with the :doc:`quickstart`. There is also a
|
||||
more detailed :doc:`tutorial/index` that shows how to create a small but
|
||||
complete application with Flask. Common patterns are described in the
|
||||
:ref:`patterns` section. The rest of the docs describe each component of
|
||||
Flask in detail, with a full reference in the :ref:`api` section.
|
||||
:doc:`patterns/index` section. The rest of the docs describe each
|
||||
component of Flask in detail, with a full reference in the :doc:`api`
|
||||
section.
|
||||
|
||||
Flask depends on the `Jinja`_ template engine and the `Werkzeug`_ WSGI
|
||||
toolkit. The documentation for these libraries can be found at:
|
||||
Flask depends on the `Werkzeug`_ WSGI toolkit, the `Jinja`_ template engine, and the
|
||||
`Click`_ CLI toolkit. Be sure to check their documentation as well as Flask's when
|
||||
looking for information.
|
||||
|
||||
- `Jinja documentation <http://jinja.pocoo.org/docs>`_
|
||||
- `Werkzeug documentation <https://werkzeug.palletsprojects.com/>`_
|
||||
|
||||
.. _Jinja: https://www.palletsprojects.com/p/jinja/
|
||||
.. _Werkzeug: https://www.palletsprojects.com/p/werkzeug/
|
||||
.. _Werkzeug: https://werkzeug.palletsprojects.com
|
||||
.. _Jinja: https://jinja.palletsprojects.com
|
||||
.. _Click: https://click.palletsprojects.com
|
||||
|
||||
|
||||
User's Guide
|
||||
------------
|
||||
|
||||
This part of the documentation, which is mostly prose, begins with some
|
||||
background information about Flask, then focuses on step-by-step
|
||||
instructions for web development with Flask.
|
||||
Flask provides configuration and conventions, with sensible defaults, to get started.
|
||||
This section of the documentation explains the different parts of the Flask framework
|
||||
and how they can be used, customized, and extended. Beyond Flask itself, look for
|
||||
community-maintained extensions to add even more functionality.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
foreword
|
||||
advanced_foreword
|
||||
installation
|
||||
quickstart
|
||||
tutorial/index
|
||||
templating
|
||||
testing
|
||||
errorhandling
|
||||
debugging
|
||||
logging
|
||||
config
|
||||
signals
|
||||
views
|
||||
lifecycle
|
||||
appcontext
|
||||
reqcontext
|
||||
blueprints
|
||||
extensions
|
||||
cli
|
||||
server
|
||||
shell
|
||||
patterns/index
|
||||
web-security
|
||||
deploying/index
|
||||
becomingbig
|
||||
gevent
|
||||
async-await
|
||||
|
||||
|
||||
API Reference
|
||||
|
|
@ -76,18 +79,11 @@ method, this part of the documentation is for you.
|
|||
Additional Notes
|
||||
----------------
|
||||
|
||||
Design notes, legal information and changelog are here for the interested.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
design
|
||||
htmlfaq
|
||||
security
|
||||
unicode
|
||||
extensiondev
|
||||
styleguide
|
||||
upgrading
|
||||
changelog
|
||||
license
|
||||
contributing
|
||||
license
|
||||
changes
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
.. _installation:
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
|
||||
Python Version
|
||||
--------------
|
||||
|
||||
We recommend using the latest version of Python 3. Flask supports Python 3.5
|
||||
and newer, Python 2.7, and PyPy.
|
||||
We recommend using the latest version of Python. Flask supports Python 3.10 and newer.
|
||||
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
|
|
@ -24,12 +23,15 @@ These distributions will be installed automatically when installing Flask.
|
|||
to protect Flask's session cookie.
|
||||
* `Click`_ is a framework for writing command line applications. It provides
|
||||
the ``flask`` command and allows adding custom management commands.
|
||||
* `Blinker`_ provides support for :doc:`signals`.
|
||||
|
||||
.. _Werkzeug: https://palletsprojects.com/p/werkzeug/
|
||||
.. _Jinja: https://palletsprojects.com/p/jinja/
|
||||
.. _MarkupSafe: https://palletsprojects.com/p/markupsafe/
|
||||
.. _ItsDangerous: https://palletsprojects.com/p/itsdangerous/
|
||||
.. _Click: https://palletsprojects.com/p/click/
|
||||
.. _Blinker: https://blinker.readthedocs.io/
|
||||
|
||||
|
||||
Optional dependencies
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
|
@ -37,20 +39,26 @@ Optional dependencies
|
|||
These distributions will not be installed automatically. Flask will detect and
|
||||
use them if you install them.
|
||||
|
||||
* `Blinker`_ provides support for :ref:`signals`.
|
||||
* `SimpleJSON`_ is a fast JSON implementation that is compatible with
|
||||
Python's ``json`` module. It is preferred for JSON operations if it is
|
||||
installed.
|
||||
* `python-dotenv`_ enables support for :ref:`dotenv` when running ``flask``
|
||||
commands.
|
||||
* `Watchdog`_ provides a faster, more efficient reloader for the development
|
||||
server.
|
||||
|
||||
.. _Blinker: https://pythonhosted.org/blinker/
|
||||
.. _SimpleJSON: https://simplejson.readthedocs.io/
|
||||
.. _python-dotenv: https://github.com/theskumar/python-dotenv#readme
|
||||
.. _watchdog: https://pythonhosted.org/watchdog/
|
||||
|
||||
|
||||
greenlet
|
||||
~~~~~~~~
|
||||
|
||||
You may choose to use :doc:`/gevent` with your application. In this case,
|
||||
greenlet>=1.0 is required. When using PyPy, PyPy>=7.3.7 is required.
|
||||
|
||||
These are not minimum supported versions, they only indicate the first
|
||||
versions that added necessary features. You should use the latest
|
||||
versions of each.
|
||||
|
||||
|
||||
Virtual environments
|
||||
--------------------
|
||||
|
||||
|
|
@ -66,43 +74,35 @@ Virtual environments are independent groups of Python libraries, one for each
|
|||
project. Packages installed for one project will not affect other projects or
|
||||
the operating system's packages.
|
||||
|
||||
Python 3 comes bundled with the :mod:`venv` module to create virtual
|
||||
environments. If you're using a modern version of Python, you can continue on
|
||||
to the next section.
|
||||
Python comes bundled with the :mod:`venv` module to create virtual
|
||||
environments.
|
||||
|
||||
If you're using Python 2, see :ref:`install-install-virtualenv` first.
|
||||
|
||||
.. _install-create-env:
|
||||
|
||||
Create an environment
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Create a project folder and a :file:`venv` folder within:
|
||||
Create a project folder and a :file:`.venv` folder within:
|
||||
|
||||
.. code-block:: sh
|
||||
.. tabs::
|
||||
|
||||
$ mkdir myproject
|
||||
$ cd myproject
|
||||
$ python3 -m venv venv
|
||||
.. group-tab:: macOS/Linux
|
||||
|
||||
On Windows:
|
||||
.. code-block:: text
|
||||
|
||||
.. code-block:: bat
|
||||
$ mkdir myproject
|
||||
$ cd myproject
|
||||
$ python3 -m venv .venv
|
||||
|
||||
$ py -3 -m venv venv
|
||||
.. group-tab:: Windows
|
||||
|
||||
If you needed to install virtualenv because you are using Python 2, use
|
||||
the following command instead:
|
||||
.. code-block:: text
|
||||
|
||||
.. code-block:: sh
|
||||
> mkdir myproject
|
||||
> cd myproject
|
||||
> py -3 -m venv .venv
|
||||
|
||||
$ python2 -m virtualenv venv
|
||||
|
||||
On Windows:
|
||||
|
||||
.. code-block:: bat
|
||||
|
||||
> \Python27\Scripts\virtualenv.exe venv
|
||||
|
||||
.. _install-activate-env:
|
||||
|
||||
|
|
@ -111,22 +111,29 @@ Activate the environment
|
|||
|
||||
Before you work on your project, activate the corresponding environment:
|
||||
|
||||
.. code-block:: sh
|
||||
.. tabs::
|
||||
|
||||
$ . venv/bin/activate
|
||||
.. group-tab:: macOS/Linux
|
||||
|
||||
On Windows:
|
||||
.. code-block:: text
|
||||
|
||||
.. code-block:: bat
|
||||
$ . .venv/bin/activate
|
||||
|
||||
> venv\Scripts\activate
|
||||
.. group-tab:: Windows
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
> .venv\Scripts\activate
|
||||
|
||||
Your shell prompt will change to show the name of the activated
|
||||
environment.
|
||||
|
||||
Your shell prompt will change to show the name of the activated environment.
|
||||
|
||||
Install Flask
|
||||
-------------
|
||||
|
||||
Within the activated environment, use the following command to install Flask:
|
||||
Within the activated environment, use the following command to install
|
||||
Flask:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
|
|
@ -134,53 +141,3 @@ Within the activated environment, use the following command to install Flask:
|
|||
|
||||
Flask is now installed. Check out the :doc:`/quickstart` or go to the
|
||||
:doc:`Documentation Overview </index>`.
|
||||
|
||||
Living on the edge
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If you want to work with the latest Flask code before it's released, install or
|
||||
update the code from the master branch:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
$ pip install -U https://github.com/pallets/flask/archive/master.tar.gz
|
||||
|
||||
.. _install-install-virtualenv:
|
||||
|
||||
Install virtualenv
|
||||
------------------
|
||||
|
||||
If you are using Python 2, the venv module is not available. Instead,
|
||||
install `virtualenv`_.
|
||||
|
||||
On Linux, virtualenv is provided by your package manager:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
# Debian, Ubuntu
|
||||
$ sudo apt-get install python-virtualenv
|
||||
|
||||
# CentOS, Fedora
|
||||
$ sudo yum install python-virtualenv
|
||||
|
||||
# Arch
|
||||
$ sudo pacman -S python-virtualenv
|
||||
|
||||
If you are on Mac OS X or Windows, download `get-pip.py`_, then:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
$ sudo python2 Downloads/get-pip.py
|
||||
$ sudo python2 -m pip install virtualenv
|
||||
|
||||
On Windows, as an administrator:
|
||||
|
||||
.. code-block:: bat
|
||||
|
||||
> \Python27\python.exe Downloads\get-pip.py
|
||||
> \Python27\python.exe -m pip install virtualenv
|
||||
|
||||
Now you can return above and :ref:`install-create-env`.
|
||||
|
||||
.. _virtualenv: https://virtualenv.pypa.io/
|
||||
.. _get-pip.py: https://bootstrap.pypa.io/get-pip.py
|
||||
|
|
|
|||
|
|
@ -1,19 +1,5 @@
|
|||
License
|
||||
=======
|
||||
BSD-3-Clause License
|
||||
====================
|
||||
|
||||
Source License
|
||||
--------------
|
||||
|
||||
This license applies to all files in the Flask repository and source
|
||||
distribution. This includes Flask's source code, the examples, and
|
||||
tests, as well as the documentation.
|
||||
|
||||
.. include:: ../LICENSE.rst
|
||||
|
||||
|
||||
Artwork License
|
||||
---------------
|
||||
|
||||
This license applies to Flask's logo.
|
||||
|
||||
.. include:: ../artwork/LICENSE.rst
|
||||
.. literalinclude:: ../LICENSE.txt
|
||||
:language: text
|
||||
|
|
|
|||
171
docs/lifecycle.rst
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
Application Structure and Lifecycle
|
||||
===================================
|
||||
|
||||
Flask makes it pretty easy to write a web application. But there are quite a few
|
||||
different parts to an application and to each request it handles. Knowing what happens
|
||||
during application setup, serving, and handling requests will help you know what's
|
||||
possible in Flask and how to structure your application.
|
||||
|
||||
|
||||
Application Setup
|
||||
-----------------
|
||||
|
||||
The first step in creating a Flask application is creating the application object. Each
|
||||
Flask application is an instance of the :class:`.Flask` class, which collects all
|
||||
configuration, extensions, and views.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from flask import Flask
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config.from_mapping(
|
||||
SECRET_KEY="dev",
|
||||
)
|
||||
app.config.from_prefixed_env()
|
||||
|
||||
@app.route("/")
|
||||
def index():
|
||||
return "Hello, World!"
|
||||
|
||||
This is known as the "application setup phase", it's the code you write that's outside
|
||||
any view functions or other handlers. It can be split up between different modules and
|
||||
sub-packages, but all code that you want to be part of your application must be imported
|
||||
in order for it to be registered.
|
||||
|
||||
All application setup must be completed before you start serving your application and
|
||||
handling requests. This is because WSGI servers divide work between multiple workers, or
|
||||
can be distributed across multiple machines. If the configuration changed in one worker,
|
||||
there's no way for Flask to ensure consistency between other workers.
|
||||
|
||||
Flask tries to help developers catch some of these setup ordering issues by showing an
|
||||
error if setup-related methods are called after requests are handled. In that case
|
||||
you'll see this error:
|
||||
|
||||
The setup method 'route' can no longer be called on the application. It has already
|
||||
handled its first request, any changes will not be applied consistently.
|
||||
Make sure all imports, decorators, functions, etc. needed to set up the application
|
||||
are done before running it.
|
||||
|
||||
However, it is not possible for Flask to detect all cases of out-of-order setup. In
|
||||
general, don't do anything to modify the ``Flask`` app object and ``Blueprint`` objects
|
||||
from within view functions that run during requests. This includes:
|
||||
|
||||
- Adding routes, view functions, and other request handlers with ``@app.route``,
|
||||
``@app.errorhandler``, ``@app.before_request``, etc.
|
||||
- Registering blueprints.
|
||||
- Loading configuration with ``app.config``.
|
||||
- Setting up the Jinja template environment with ``app.jinja_env``.
|
||||
- Setting a session interface, instead of the default itsdangerous cookie.
|
||||
- Setting a JSON provider with ``app.json``, instead of the default provider.
|
||||
- Creating and initializing Flask extensions.
|
||||
|
||||
|
||||
Serving the Application
|
||||
-----------------------
|
||||
|
||||
Flask is a WSGI application framework. The other half of WSGI is the WSGI server. During
|
||||
development, Flask, through Werkzeug, provides a development WSGI server with the
|
||||
``flask run`` CLI command. When you are done with development, use a production server
|
||||
to serve your application, see :doc:`deploying/index`.
|
||||
|
||||
Regardless of what server you're using, it will follow the :pep:`3333` WSGI spec. The
|
||||
WSGI server will be told how to access your Flask application object, which is the WSGI
|
||||
application. Then it will start listening for HTTP requests, translate the request data
|
||||
into a WSGI environ, and call the WSGI application with that data. The WSGI application
|
||||
will return data that is translated into an HTTP response.
|
||||
|
||||
#. Browser or other client makes HTTP request.
|
||||
#. WSGI server receives request.
|
||||
#. WSGI server converts HTTP data to WSGI ``environ`` dict.
|
||||
#. WSGI server calls WSGI application with the ``environ``.
|
||||
#. Flask, the WSGI application, does all its internal processing to route the request
|
||||
to a view function, handle errors, etc.
|
||||
#. Flask translates View function return into WSGI response data, passes it to WSGI
|
||||
server.
|
||||
#. WSGI server creates and send an HTTP response.
|
||||
#. Client receives the HTTP response.
|
||||
|
||||
|
||||
Middleware
|
||||
~~~~~~~~~~
|
||||
|
||||
The WSGI application above is a callable that behaves in a certain way. Middleware
|
||||
is a WSGI application that wraps another WSGI application. It's a similar concept to
|
||||
Python decorators. The outermost middleware will be called by the server. It can modify
|
||||
the data passed to it, then call the WSGI application (or further middleware) that it
|
||||
wraps, and so on. And it can take the return value of that call and modify it further.
|
||||
|
||||
From the WSGI server's perspective, there is one WSGI application, the one it calls
|
||||
directly. Typically, Flask is the "real" application at the end of the chain of
|
||||
middleware. But even Flask can call further WSGI applications, although that's an
|
||||
advanced, uncommon use case.
|
||||
|
||||
A common middleware you'll see used with Flask is Werkzeug's
|
||||
:class:`~werkzeug.middleware.proxy_fix.ProxyFix`, which modifies the request to look
|
||||
like it came directly from a client even if it passed through HTTP proxies on the way.
|
||||
There are other middleware that can handle serving static files, authentication, etc.
|
||||
|
||||
|
||||
How a Request is Handled
|
||||
------------------------
|
||||
|
||||
For us, the interesting part of the steps above is when Flask gets called by the WSGI
|
||||
server (or middleware). At that point, it will do quite a lot to handle the request and
|
||||
generate the response. At the most basic, it will match the URL to a view function, call
|
||||
the view function, and pass the return value back to the server. But there are many more
|
||||
parts that you can use to customize its behavior.
|
||||
|
||||
#. WSGI server calls the Flask object, which calls :meth:`.Flask.wsgi_app`.
|
||||
#. An :class:`.AppContext` object is created. This converts the WSGI ``environ``
|
||||
dict into a :class:`.Request` object.
|
||||
#. The :doc:`app context <appcontext>` is pushed, which makes
|
||||
:data:`.current_app`, :data:`.g`, :data:`.request`, and :data:`.session`
|
||||
available.
|
||||
#. The :data:`.appcontext_pushed` signal is sent.
|
||||
#. The URL is matched against the URL rules registered with the :meth:`~.Flask.route`
|
||||
decorator during application setup. If there is no match, the error - usually a 404,
|
||||
405, or redirect - is stored to be handled later.
|
||||
#. The :data:`.request_started` signal is sent.
|
||||
#. Any :meth:`~.Flask.url_value_preprocessor` decorated functions are called.
|
||||
#. Any :meth:`~.Flask.before_request` decorated functions are called. If any of
|
||||
these function returns a value it is treated as the response immediately.
|
||||
#. If the URL didn't match a route a few steps ago, that error is raised now.
|
||||
#. The :meth:`~.Flask.route` decorated view function associated with the matched URL
|
||||
is called and returns a value to be used as the response.
|
||||
#. If any step so far raised an exception, and there is an :meth:`~.Flask.errorhandler`
|
||||
decorated function that matches the exception class or HTTP error code, it is
|
||||
called to handle the error and return a response.
|
||||
#. Whatever returned a response value - a before request function, the view, or an
|
||||
error handler, that value is converted to a :class:`.Response` object.
|
||||
#. Any :func:`~.after_this_request` decorated functions are called, which can modify
|
||||
the response object. They are then cleared.
|
||||
#. Any :meth:`~.Flask.after_request` decorated functions are called, which can modify
|
||||
the response object.
|
||||
#. The session is saved, persisting any modified session data using the app's
|
||||
:attr:`~.Flask.session_interface`.
|
||||
#. The :data:`.request_finished` signal is sent.
|
||||
#. If any step so far raised an exception, and it was not handled by an error handler
|
||||
function, it is handled now. HTTP exceptions are treated as responses with their
|
||||
corresponding status code, other exceptions are converted to a generic 500 response.
|
||||
The :data:`.got_request_exception` signal is sent.
|
||||
#. The response object's status, headers, and body are returned to the WSGI server.
|
||||
#. Any :meth:`~.Flask.teardown_request` decorated functions are called.
|
||||
#. The :data:`.request_tearing_down` signal is sent.
|
||||
#. Any :meth:`~.Flask.teardown_appcontext` decorated functions are called.
|
||||
#. The :data:`.appcontext_tearing_down` signal is sent.
|
||||
#. The app context is popped, :data:`.current_app`, :data:`.g`, :data:`.request`,
|
||||
and :data:`.session` are no longer available.
|
||||
#. The :data:`.appcontext_popped` signal is sent.
|
||||
|
||||
When executing a CLI command or plain app context without request data, the same
|
||||
order of steps is followed, omitting the steps that refer to the request.
|
||||
|
||||
A :class:`Blueprint` can add handlers for these events that are specific to the
|
||||
blueprint. The handlers for a blueprint will run if the blueprint
|
||||
owns the route that matches the request.
|
||||
|
||||
There are even more decorators and customization points than this, but that aren't part
|
||||
of every request lifecycle. They're more specific to certain things you might use during
|
||||
a request, such as templates, building URLs, or handling JSON data. See the rest of this
|
||||
documentation, as well as the :doc:`api` to explore further.
|
||||
|
|
@ -20,6 +20,9 @@ logger can also be used to log your own messages.
|
|||
app.logger.info('%s failed to log in', user.username)
|
||||
abort(401)
|
||||
|
||||
If you don't configure logging, Python's default log level is usually
|
||||
'warning'. Nothing below the configured level will be visible.
|
||||
|
||||
|
||||
Basic Configuration
|
||||
-------------------
|
||||
|
|
@ -156,7 +159,7 @@ Depending on your project, it may be more useful to configure each logger you
|
|||
care about separately, instead of configuring only the root logger. ::
|
||||
|
||||
for logger in (
|
||||
app.logger,
|
||||
logging.getLogger(app.name),
|
||||
logging.getLogger('sqlalchemy'),
|
||||
logging.getLogger('other_package'),
|
||||
):
|
||||
|
|
|
|||
|
|
@ -1,63 +0,0 @@
|
|||
Implementing API Exceptions
|
||||
===========================
|
||||
|
||||
It's very common to implement RESTful APIs on top of Flask. One of the
|
||||
first things that developers run into is the realization that the builtin
|
||||
exceptions are not expressive enough for APIs and that the content type of
|
||||
:mimetype:`text/html` they are emitting is not very useful for API consumers.
|
||||
|
||||
The better solution than using ``abort`` to signal errors for invalid API
|
||||
usage is to implement your own exception type and install an error handler
|
||||
for it that produces the errors in the format the user is expecting.
|
||||
|
||||
Simple Exception Class
|
||||
----------------------
|
||||
|
||||
The basic idea is to introduce a new exception that can take a proper
|
||||
human readable message, a status code for the error and some optional
|
||||
payload to give more context for the error.
|
||||
|
||||
This is a simple example::
|
||||
|
||||
from flask import jsonify
|
||||
|
||||
class InvalidUsage(Exception):
|
||||
status_code = 400
|
||||
|
||||
def __init__(self, message, status_code=None, payload=None):
|
||||
Exception.__init__(self)
|
||||
self.message = message
|
||||
if status_code is not None:
|
||||
self.status_code = status_code
|
||||
self.payload = payload
|
||||
|
||||
def to_dict(self):
|
||||
rv = dict(self.payload or ())
|
||||
rv['message'] = self.message
|
||||
return rv
|
||||
|
||||
A view can now raise that exception with an error message. Additionally
|
||||
some extra payload can be provided as a dictionary through the `payload`
|
||||
parameter.
|
||||
|
||||
Registering an Error Handler
|
||||
----------------------------
|
||||
|
||||
At that point views can raise that error, but it would immediately result
|
||||
in an internal server error. The reason for this is that there is no
|
||||
handler registered for this error class. That however is easy to add::
|
||||
|
||||
@app.errorhandler(InvalidUsage)
|
||||
def handle_invalid_usage(error):
|
||||
response = jsonify(error.to_dict())
|
||||
response.status_code = error.status_code
|
||||
return response
|
||||
|
||||
Usage in Views
|
||||
--------------
|
||||
|
||||
Here is how a view can use that functionality::
|
||||
|
||||
@app.route('/foo')
|
||||
def get_foo():
|
||||
raise InvalidUsage('This view is gone', status_code=410)
|
||||
|
|
@ -1,5 +1,3 @@
|
|||
.. _app-dispatch:
|
||||
|
||||
Application Dispatching
|
||||
=======================
|
||||
|
||||
|
|
@ -10,44 +8,30 @@ Django and a Flask application in the same interpreter side by side if
|
|||
you want. The usefulness of this depends on how the applications work
|
||||
internally.
|
||||
|
||||
The fundamental difference from the :ref:`module approach
|
||||
<larger-applications>` is that in this case you are running the same or
|
||||
different Flask applications that are entirely isolated from each other.
|
||||
They run different configurations and are dispatched on the WSGI level.
|
||||
The fundamental difference from :doc:`packages` is that in this case you
|
||||
are running the same or different Flask applications that are entirely
|
||||
isolated from each other. They run different configurations and are
|
||||
dispatched on the WSGI level.
|
||||
|
||||
|
||||
Working with this Document
|
||||
--------------------------
|
||||
|
||||
Each of the techniques and examples below results in an ``application`` object
|
||||
that can be run with any WSGI server. For production, see :ref:`deployment`.
|
||||
For development, Werkzeug provides a builtin server for development available
|
||||
at :func:`werkzeug.serving.run_simple`::
|
||||
Each of the techniques and examples below results in an ``application``
|
||||
object that can be run with any WSGI server. For development, use the
|
||||
``flask run`` command to start a development server. For production, see
|
||||
:doc:`/deploying/index`.
|
||||
|
||||
from werkzeug.serving import run_simple
|
||||
run_simple('localhost', 5000, application, use_reloader=True)
|
||||
|
||||
Note that :func:`run_simple <werkzeug.serving.run_simple>` is not intended for
|
||||
use in production. Use a :ref:`full-blown WSGI server <deployment>`.
|
||||
|
||||
In order to use the interactive debugger, debugging must be enabled both on
|
||||
the application and the simple server. Here is the "hello world" example with
|
||||
debugging and :func:`run_simple <werkzeug.serving.run_simple>`::
|
||||
.. code-block:: python
|
||||
|
||||
from flask import Flask
|
||||
from werkzeug.serving import run_simple
|
||||
|
||||
app = Flask(__name__)
|
||||
app.debug = True
|
||||
|
||||
@app.route('/')
|
||||
def hello_world():
|
||||
return 'Hello World!'
|
||||
|
||||
if __name__ == '__main__':
|
||||
run_simple('localhost', 5000, app,
|
||||
use_reloader=True, use_debugger=True, use_evalex=True)
|
||||
|
||||
|
||||
Combining Applications
|
||||
----------------------
|
||||
|
|
@ -60,7 +44,9 @@ are combined by the dispatcher middleware into a larger one that is
|
|||
dispatched based on prefix.
|
||||
|
||||
For example you could have your main application run on ``/`` and your
|
||||
backend interface on ``/backend``::
|
||||
backend interface on ``/backend``.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from werkzeug.middleware.dispatcher import DispatcherMiddleware
|
||||
from frontend_app import application as frontend
|
||||
|
|
@ -79,7 +65,7 @@ with different configurations. Assuming the application is created inside
|
|||
a function and you can call that function to instantiate it, that is
|
||||
really easy to implement. In order to develop your application to support
|
||||
creating new instances in functions have a look at the
|
||||
:ref:`app-factories` pattern.
|
||||
:doc:`appfactories` pattern.
|
||||
|
||||
A very common example would be creating applications per subdomain. For
|
||||
instance you configure your webserver to dispatch all requests for all
|
||||
|
|
@ -91,11 +77,13 @@ the dynamic application creation.
|
|||
The perfect level for abstraction in that regard is the WSGI layer. You
|
||||
write your own WSGI application that looks at the request that comes and
|
||||
delegates it to your Flask application. If that application does not
|
||||
exist yet, it is dynamically created and remembered::
|
||||
exist yet, it is dynamically created and remembered.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from threading import Lock
|
||||
|
||||
class SubdomainDispatcher(object):
|
||||
class SubdomainDispatcher:
|
||||
|
||||
def __init__(self, domain, create_app):
|
||||
self.domain = domain
|
||||
|
|
@ -119,7 +107,9 @@ exist yet, it is dynamically created and remembered::
|
|||
return app(environ, start_response)
|
||||
|
||||
|
||||
This dispatcher can then be used like this::
|
||||
This dispatcher can then be used like this:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from myapplication import create_app, get_user_for_subdomain
|
||||
from werkzeug.exceptions import NotFound
|
||||
|
|
@ -145,12 +135,14 @@ Dispatch by Path
|
|||
|
||||
Dispatching by a path on the URL is very similar. Instead of looking at
|
||||
the ``Host`` header to figure out the subdomain one simply looks at the
|
||||
request path up to the first slash::
|
||||
request path up to the first slash.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from threading import Lock
|
||||
from werkzeug.wsgi import pop_path_info, peek_path_info
|
||||
from wsgiref.util import shift_path_info
|
||||
|
||||
class PathDispatcher(object):
|
||||
class PathDispatcher:
|
||||
|
||||
def __init__(self, default_app, create_app):
|
||||
self.default_app = default_app
|
||||
|
|
@ -168,15 +160,24 @@ request path up to the first slash::
|
|||
return app
|
||||
|
||||
def __call__(self, environ, start_response):
|
||||
app = self.get_application(peek_path_info(environ))
|
||||
app = self.get_application(_peek_path_info(environ))
|
||||
if app is not None:
|
||||
pop_path_info(environ)
|
||||
shift_path_info(environ)
|
||||
else:
|
||||
app = self.default_app
|
||||
return app(environ, start_response)
|
||||
|
||||
def _peek_path_info(environ):
|
||||
segments = environ.get("PATH_INFO", "").lstrip("/").split("/", 1)
|
||||
if segments:
|
||||
return segments[0]
|
||||
|
||||
return None
|
||||
|
||||
The big difference between this and the subdomain one is that this one
|
||||
falls back to another application if the creator function returns ``None``::
|
||||
falls back to another application if the creator function returns ``None``.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from myapplication import create_app, default_app, get_user_for_prefix
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
.. _app-factories:
|
||||
|
||||
Application Factories
|
||||
=====================
|
||||
|
||||
If you are already using packages and blueprints for your application
|
||||
(:ref:`blueprints`) there are a couple of really nice ways to further improve
|
||||
(:doc:`/blueprints`) there are a couple of really nice ways to further improve
|
||||
the experience. A common pattern is creating the application object when
|
||||
the blueprint is imported. But if you move the creation of this object
|
||||
into a function, you can then create multiple instances of this app later.
|
||||
|
|
@ -89,19 +87,22 @@ For more information about the design of extensions refer to :doc:`/extensiondev
|
|||
Using Applications
|
||||
------------------
|
||||
|
||||
To run such an application, you can use the :command:`flask` command::
|
||||
To run such an application, you can use the :command:`flask` command:
|
||||
|
||||
$ export FLASK_APP=myapp
|
||||
$ flask run
|
||||
.. code-block:: text
|
||||
|
||||
Flask will automatically detect the factory (``create_app`` or ``make_app``)
|
||||
in ``myapp``. You can also pass arguments to the factory like this::
|
||||
$ flask --app hello run
|
||||
|
||||
$ export FLASK_APP="myapp:create_app('dev')"
|
||||
$ flask run
|
||||
Flask will automatically detect the factory if it is named
|
||||
``create_app`` or ``make_app`` in ``hello``. You can also pass arguments
|
||||
to the factory like this:
|
||||
|
||||
Then the ``create_app`` factory in ``myapp`` is called with the string
|
||||
``'dev'`` as the argument. See :doc:`/cli` for more detail.
|
||||
.. code-block:: text
|
||||
|
||||
$ flask --app 'hello:create_app(local_auth=True)' run
|
||||
|
||||
Then the ``create_app`` factory in ``hello`` is called with the keyword
|
||||
argument ``local_auth=True``. See :doc:`/cli` for more detail.
|
||||
|
||||
Factory Improvements
|
||||
--------------------
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
.. _caching-pattern:
|
||||
|
||||
Caching
|
||||
=======
|
||||
|
||||
|
|
|
|||
|
|
@ -1,101 +1,242 @@
|
|||
Celery Background Tasks
|
||||
=======================
|
||||
Background Tasks with Celery
|
||||
============================
|
||||
|
||||
If your application has a long running task, such as processing some uploaded
|
||||
data or sending email, you don't want to wait for it to finish during a
|
||||
request. Instead, use a task queue to send the necessary data to another
|
||||
process that will run the task in the background while the request returns
|
||||
immediately.
|
||||
If your application has a long running task, such as processing some uploaded data or
|
||||
sending email, you don't want to wait for it to finish during a request. Instead, use a
|
||||
task queue to send the necessary data to another process that will run the task in the
|
||||
background while the request returns immediately.
|
||||
|
||||
`Celery`_ is a powerful task queue that can be used for simple background tasks as well
|
||||
as complex multi-stage programs and schedules. This guide will show you how to configure
|
||||
Celery using Flask. Read Celery's `First Steps with Celery`_ guide to learn how to use
|
||||
Celery itself.
|
||||
|
||||
.. _Celery: https://celery.readthedocs.io
|
||||
.. _First Steps with Celery: https://celery.readthedocs.io/en/latest/getting-started/first-steps-with-celery.html
|
||||
|
||||
The Flask repository contains `an example <https://github.com/pallets/flask/tree/main/examples/celery>`_
|
||||
based on the information on this page, which also shows how to use JavaScript to submit
|
||||
tasks and poll for progress and results.
|
||||
|
||||
Celery is a powerful task queue that can be used for simple background tasks
|
||||
as well as complex multi-stage programs and schedules. This guide will show you
|
||||
how to configure Celery using Flask, but assumes you've already read the
|
||||
`First Steps with Celery <https://celery.readthedocs.io/en/latest/getting-started/first-steps-with-celery.html>`_
|
||||
guide in the Celery documentation.
|
||||
|
||||
Install
|
||||
-------
|
||||
|
||||
Celery is a separate Python package. Install it from PyPI using pip::
|
||||
Install Celery from PyPI, for example using pip:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ pip install celery
|
||||
|
||||
Configure
|
||||
---------
|
||||
|
||||
The first thing you need is a Celery instance, this is called the celery
|
||||
application. It serves the same purpose as the :class:`~flask.Flask`
|
||||
object in Flask, just for Celery. Since this instance is used as the
|
||||
entry-point for everything you want to do in Celery, like creating tasks
|
||||
and managing workers, it must be possible for other modules to import it.
|
||||
Integrate Celery with Flask
|
||||
---------------------------
|
||||
|
||||
For instance you can place this in a ``tasks`` module. While you can use
|
||||
Celery without any reconfiguration with Flask, it becomes a bit nicer by
|
||||
subclassing tasks and adding support for Flask's application contexts and
|
||||
hooking it up with the Flask configuration.
|
||||
You can use Celery without any integration with Flask, but it's convenient to configure
|
||||
it through Flask's config, and to let tasks access the Flask application.
|
||||
|
||||
This is all that is necessary to properly integrate Celery with Flask::
|
||||
Celery uses similar ideas to Flask, with a ``Celery`` app object that has configuration
|
||||
and registers tasks. While creating a Flask app, use the following code to create and
|
||||
configure a Celery app as well.
|
||||
|
||||
from celery import Celery
|
||||
.. code-block:: python
|
||||
|
||||
def make_celery(app):
|
||||
celery = Celery(
|
||||
app.import_name,
|
||||
backend=app.config['CELERY_RESULT_BACKEND'],
|
||||
broker=app.config['CELERY_BROKER_URL']
|
||||
)
|
||||
celery.conf.update(app.config)
|
||||
from celery import Celery, Task
|
||||
|
||||
class ContextTask(celery.Task):
|
||||
def __call__(self, *args, **kwargs):
|
||||
def celery_init_app(app: Flask) -> Celery:
|
||||
class FlaskTask(Task):
|
||||
def __call__(self, *args: object, **kwargs: object) -> object:
|
||||
with app.app_context():
|
||||
return self.run(*args, **kwargs)
|
||||
|
||||
celery.Task = ContextTask
|
||||
return celery
|
||||
celery_app = Celery(app.name, task_cls=FlaskTask)
|
||||
celery_app.config_from_object(app.config["CELERY"])
|
||||
celery_app.set_default()
|
||||
app.extensions["celery"] = celery_app
|
||||
return celery_app
|
||||
|
||||
The function creates a new Celery object, configures it with the broker
|
||||
from the application config, updates the rest of the Celery config from
|
||||
the Flask config and then creates a subclass of the task that wraps the
|
||||
task execution in an application context.
|
||||
This creates and returns a ``Celery`` app object. Celery `configuration`_ is taken from
|
||||
the ``CELERY`` key in the Flask configuration. The Celery app is set as the default, so
|
||||
that it is seen during each request. The ``Task`` subclass automatically runs task
|
||||
functions with a Flask app context active, so that services like your database
|
||||
connections are available.
|
||||
|
||||
An example task
|
||||
---------------
|
||||
.. _configuration: https://celery.readthedocs.io/en/stable/userguide/configuration.html
|
||||
|
||||
Let's write a task that adds two numbers together and returns the result. We
|
||||
configure Celery's broker and backend to use Redis, create a ``celery``
|
||||
application using the factor from above, and then use it to define the task. ::
|
||||
Here's a basic ``example.py`` that configures Celery to use Redis for communication. We
|
||||
enable a result backend, but ignore results by default. This allows us to store results
|
||||
only for tasks where we care about the result.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from flask import Flask
|
||||
|
||||
flask_app = Flask(__name__)
|
||||
flask_app.config.update(
|
||||
CELERY_BROKER_URL='redis://localhost:6379',
|
||||
CELERY_RESULT_BACKEND='redis://localhost:6379'
|
||||
app = Flask(__name__)
|
||||
app.config.from_mapping(
|
||||
CELERY=dict(
|
||||
broker_url="redis://localhost",
|
||||
result_backend="redis://localhost",
|
||||
task_ignore_result=True,
|
||||
),
|
||||
)
|
||||
celery = make_celery(flask_app)
|
||||
celery_app = celery_init_app(app)
|
||||
|
||||
@celery.task()
|
||||
def add_together(a, b):
|
||||
Point the ``celery worker`` command at this and it will find the ``celery_app`` object.
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ celery -A example worker --loglevel INFO
|
||||
|
||||
You can also run the ``celery beat`` command to run tasks on a schedule. See Celery's
|
||||
docs for more information about defining schedules.
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ celery -A example beat --loglevel INFO
|
||||
|
||||
|
||||
Application Factory
|
||||
-------------------
|
||||
|
||||
When using the Flask application factory pattern, call the ``celery_init_app`` function
|
||||
inside the factory. It sets ``app.extensions["celery"]`` to the Celery app object, which
|
||||
can be used to get the Celery app from the Flask app returned by the factory.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def create_app() -> Flask:
|
||||
app = Flask(__name__)
|
||||
app.config.from_mapping(
|
||||
CELERY=dict(
|
||||
broker_url="redis://localhost",
|
||||
result_backend="redis://localhost",
|
||||
task_ignore_result=True,
|
||||
),
|
||||
)
|
||||
app.config.from_prefixed_env()
|
||||
celery_init_app(app)
|
||||
return app
|
||||
|
||||
To use ``celery`` commands, Celery needs an app object, but that's no longer directly
|
||||
available. Create a ``make_celery.py`` file that calls the Flask app factory and gets
|
||||
the Celery app from the returned Flask app.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from example import create_app
|
||||
|
||||
flask_app = create_app()
|
||||
celery_app = flask_app.extensions["celery"]
|
||||
|
||||
Point the ``celery`` command to this file.
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ celery -A make_celery worker --loglevel INFO
|
||||
$ celery -A make_celery beat --loglevel INFO
|
||||
|
||||
|
||||
Defining Tasks
|
||||
--------------
|
||||
|
||||
Using ``@celery_app.task`` to decorate task functions requires access to the
|
||||
``celery_app`` object, which won't be available when using the factory pattern. It also
|
||||
means that the decorated tasks are tied to the specific Flask and Celery app instances,
|
||||
which could be an issue during testing if you change configuration for a test.
|
||||
|
||||
Instead, use Celery's ``@shared_task`` decorator. This creates task objects that will
|
||||
access whatever the "current app" is, which is a similar concept to Flask's blueprints
|
||||
and app context. This is why we called ``celery_app.set_default()`` above.
|
||||
|
||||
Here's an example task that adds two numbers together and returns the result.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from celery import shared_task
|
||||
|
||||
@shared_task(ignore_result=False)
|
||||
def add_together(a: int, b: int) -> int:
|
||||
return a + b
|
||||
|
||||
This task can now be called in the background::
|
||||
Earlier, we configured Celery to ignore task results by default. Since we want to know
|
||||
the return value of this task, we set ``ignore_result=False``. On the other hand, a task
|
||||
that didn't need a result, such as sending an email, wouldn't set this.
|
||||
|
||||
result = add_together.delay(23, 42)
|
||||
result.wait() # 65
|
||||
|
||||
Run a worker
|
||||
------------
|
||||
Calling Tasks
|
||||
-------------
|
||||
|
||||
If you jumped in and already executed the above code you will be
|
||||
disappointed to learn that ``.wait()`` will never actually return.
|
||||
That's because you also need to run a Celery worker to receive and execute the
|
||||
task. ::
|
||||
The decorated function becomes a task object with methods to call it in the background.
|
||||
The simplest way is to use the ``delay(*args, **kwargs)`` method. See Celery's docs for
|
||||
more methods.
|
||||
|
||||
$ celery -A your_application.celery worker
|
||||
A Celery worker must be running to run the task. Starting a worker is shown in the
|
||||
previous sections.
|
||||
|
||||
The ``your_application`` string has to point to your application's package
|
||||
or module that creates the ``celery`` object.
|
||||
.. code-block:: python
|
||||
|
||||
Now that the worker is running, ``wait`` will return the result once the task
|
||||
is finished.
|
||||
from flask import request
|
||||
|
||||
@app.post("/add")
|
||||
def start_add() -> dict[str, object]:
|
||||
a = request.form.get("a", type=int)
|
||||
b = request.form.get("b", type=int)
|
||||
result = add_together.delay(a, b)
|
||||
return {"result_id": result.id}
|
||||
|
||||
The route doesn't get the task's result immediately. That would defeat the purpose by
|
||||
blocking the response. Instead, we return the running task's result id, which we can use
|
||||
later to get the result.
|
||||
|
||||
|
||||
Getting Results
|
||||
---------------
|
||||
|
||||
To fetch the result of the task we started above, we'll add another route that takes the
|
||||
result id we returned before. We return whether the task is finished (ready), whether it
|
||||
finished successfully, and what the return value (or error) was if it is finished.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from celery.result import AsyncResult
|
||||
|
||||
@app.get("/result/<id>")
|
||||
def task_result(id: str) -> dict[str, object]:
|
||||
result = AsyncResult(id)
|
||||
return {
|
||||
"ready": result.ready(),
|
||||
"successful": result.successful(),
|
||||
"value": result.result if result.ready() else None,
|
||||
}
|
||||
|
||||
Now you can start the task using the first route, then poll for the result using the
|
||||
second route. This keeps the Flask request workers from being blocked waiting for tasks
|
||||
to finish.
|
||||
|
||||
The Flask repository contains `an example <https://github.com/pallets/flask/tree/main/examples/celery>`_
|
||||
using JavaScript to submit tasks and poll for progress and results.
|
||||
|
||||
|
||||
Passing Data to Tasks
|
||||
---------------------
|
||||
|
||||
The "add" task above took two integers as arguments. To pass arguments to tasks, Celery
|
||||
has to serialize them to a format that it can pass to other processes. Therefore,
|
||||
passing complex objects is not recommended. For example, it would be impossible to pass
|
||||
a SQLAlchemy model object, since that object is probably not serializable and is tied to
|
||||
the session that queried it.
|
||||
|
||||
Pass the minimal amount of data necessary to fetch or recreate any complex data within
|
||||
the task. Consider a task that will run when the logged in user asks for an archive of
|
||||
their data. The Flask request knows the logged in user, and has the user object queried
|
||||
from the database. It got that by querying the database for a given id, so the task can
|
||||
do the same thing. Pass the user's id rather than the user object.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@shared_task
|
||||
def generate_user_archive(user_id: str) -> None:
|
||||
user = db.session.get(User, user_id)
|
||||
...
|
||||
|
||||
generate_user_archive.delay(current_user.id)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
.. _deferred-callbacks:
|
||||
|
||||
Deferred Request Callbacks
|
||||
==========================
|
||||
|
||||
|
|
@ -16,7 +14,7 @@ response object.
|
|||
|
||||
One way is to avoid the situation. Very often that is possible. For instance
|
||||
you can try to move that logic into a :meth:`~flask.Flask.after_request`
|
||||
callback instead. However, sometimes moving code there makes it more
|
||||
callback instead. However, sometimes moving code there makes it
|
||||
more complicated or awkward to reason about.
|
||||
|
||||
As an alternative, you can use :func:`~flask.after_this_request` to register
|
||||
|
|
|
|||
|
|
@ -1,177 +0,0 @@
|
|||
.. _distribute-deployment:
|
||||
|
||||
Deploying with Setuptools
|
||||
=========================
|
||||
|
||||
`Setuptools`_, is an extension library that is commonly used to
|
||||
distribute Python libraries and extensions. It extends distutils, a basic
|
||||
module installation system shipped with Python to also support various more
|
||||
complex constructs that make larger applications easier to distribute:
|
||||
|
||||
- **support for dependencies**: a library or application can declare a
|
||||
list of other libraries it depends on which will be installed
|
||||
automatically for you.
|
||||
- **package registry**: setuptools registers your package with your
|
||||
Python installation. This makes it possible to query information
|
||||
provided by one package from another package. The best known feature of
|
||||
this system is the entry point support which allows one package to
|
||||
declare an "entry point" that another package can hook into to extend the
|
||||
other package.
|
||||
- **installation manager**: :command:`pip` can install other libraries for you.
|
||||
|
||||
If you have Python 2 (>=2.7.9) or Python 3 (>=3.4) installed from python.org,
|
||||
you will already have pip and setuptools on your system. Otherwise, you
|
||||
will need to install them yourself.
|
||||
|
||||
Flask itself, and all the libraries you can find on PyPI are distributed with
|
||||
either setuptools or distutils.
|
||||
|
||||
In this case we assume your application is called
|
||||
:file:`yourapplication.py` and you are not using a module, but a :ref:`package
|
||||
<larger-applications>`. If you have not yet converted your application into
|
||||
a package, head over to the :ref:`larger-applications` pattern to see
|
||||
how this can be done.
|
||||
|
||||
A working deployment with setuptools is the first step into more complex
|
||||
and more automated deployment scenarios. If you want to fully automate
|
||||
the process, also read the :ref:`fabric-deployment` chapter.
|
||||
|
||||
Basic Setup Script
|
||||
------------------
|
||||
|
||||
Because you have Flask installed, you have setuptools available on your system.
|
||||
Flask already depends upon setuptools.
|
||||
|
||||
Standard disclaimer applies: :ref:`you better use a virtualenv
|
||||
<virtualenv>`.
|
||||
|
||||
Your setup code always goes into a file named :file:`setup.py` next to your
|
||||
application. The name of the file is only convention, but because
|
||||
everybody will look for a file with that name, you better not change it.
|
||||
|
||||
A basic :file:`setup.py` file for a Flask application looks like this::
|
||||
|
||||
from setuptools import setup
|
||||
|
||||
setup(
|
||||
name='Your Application',
|
||||
version='1.0',
|
||||
long_description=__doc__,
|
||||
packages=['yourapplication'],
|
||||
include_package_data=True,
|
||||
zip_safe=False,
|
||||
install_requires=['Flask']
|
||||
)
|
||||
|
||||
Please keep in mind that you have to list subpackages explicitly. If you
|
||||
want setuptools to lookup the packages for you automatically, you can use
|
||||
the ``find_packages`` function::
|
||||
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
setup(
|
||||
...
|
||||
packages=find_packages()
|
||||
)
|
||||
|
||||
Most parameters to the ``setup`` function should be self explanatory,
|
||||
``include_package_data`` and ``zip_safe`` might not be.
|
||||
``include_package_data`` tells setuptools to look for a :file:`MANIFEST.in` file
|
||||
and install all the entries that match as package data. We will use this
|
||||
to distribute the static files and templates along with the Python module
|
||||
(see :ref:`distributing-resources`). The ``zip_safe`` flag can be used to
|
||||
force or prevent zip Archive creation. In general you probably don't want
|
||||
your packages to be installed as zip files because some tools do not
|
||||
support them and they make debugging a lot harder.
|
||||
|
||||
|
||||
Tagging Builds
|
||||
--------------
|
||||
|
||||
It is useful to distinguish between release and development builds. Add a
|
||||
:file:`setup.cfg` file to configure these options. ::
|
||||
|
||||
[egg_info]
|
||||
tag_build = .dev
|
||||
tag_date = 1
|
||||
|
||||
[aliases]
|
||||
release = egg_info -Db ''
|
||||
|
||||
Running ``python setup.py sdist`` will create a development package
|
||||
with ".dev" and the current date appended: ``flaskr-1.0.dev20160314.tar.gz``.
|
||||
Running ``python setup.py release sdist`` will create a release package
|
||||
with only the version: ``flaskr-1.0.tar.gz``.
|
||||
|
||||
|
||||
.. _distributing-resources:
|
||||
|
||||
Distributing Resources
|
||||
----------------------
|
||||
|
||||
If you try to install the package you just created, you will notice that
|
||||
folders like :file:`static` or :file:`templates` are not installed for you. The
|
||||
reason for this is that setuptools does not know which files to add for
|
||||
you. What you should do, is to create a :file:`MANIFEST.in` file next to your
|
||||
:file:`setup.py` file. This file lists all the files that should be added to
|
||||
your tarball::
|
||||
|
||||
recursive-include yourapplication/templates *
|
||||
recursive-include yourapplication/static *
|
||||
|
||||
Don't forget that even if you enlist them in your :file:`MANIFEST.in` file, they
|
||||
won't be installed for you unless you set the `include_package_data`
|
||||
parameter of the ``setup`` function to ``True``!
|
||||
|
||||
|
||||
Declaring Dependencies
|
||||
----------------------
|
||||
|
||||
Dependencies are declared in the ``install_requires`` parameter as a list.
|
||||
Each item in that list is the name of a package that should be pulled from
|
||||
PyPI on installation. By default it will always use the most recent
|
||||
version, but you can also provide minimum and maximum version
|
||||
requirements. Here some examples::
|
||||
|
||||
install_requires=[
|
||||
'Flask>=0.2',
|
||||
'SQLAlchemy>=0.6',
|
||||
'BrokenPackage>=0.7,<=1.0'
|
||||
]
|
||||
|
||||
As mentioned earlier, dependencies are pulled from PyPI. What if you
|
||||
want to depend on a package that cannot be found on PyPI and won't be
|
||||
because it is an internal package you don't want to share with anyone?
|
||||
Just do it as if there was a PyPI entry and provide a list of
|
||||
alternative locations where setuptools should look for tarballs::
|
||||
|
||||
dependency_links=['http://example.com/yourfiles']
|
||||
|
||||
Make sure that page has a directory listing and the links on the page are
|
||||
pointing to the actual tarballs with their correct filenames as this is
|
||||
how setuptools will find the files. If you have an internal company
|
||||
server that contains the packages, provide the URL to that server.
|
||||
|
||||
|
||||
Installing / Developing
|
||||
-----------------------
|
||||
|
||||
To install your application (ideally into a virtualenv) just run the
|
||||
:file:`setup.py` script with the ``install`` parameter. It will install your
|
||||
application into the virtualenv's site-packages folder and also download
|
||||
and install all dependencies::
|
||||
|
||||
$ python setup.py install
|
||||
|
||||
If you are developing on the package and also want the requirements to be
|
||||
installed, you can use the ``develop`` command instead::
|
||||
|
||||
$ python setup.py develop
|
||||
|
||||
This has the advantage of just installing a link to the site-packages
|
||||
folder instead of copying the data over. You can then continue to work on
|
||||
the code without having to run ``install`` again after each change.
|
||||
|
||||
|
||||
.. _pip: https://pypi.org/project/pip/
|
||||
.. _Setuptools: https://pypi.org/project/setuptools/
|
||||
|
|
@ -1,125 +0,0 @@
|
|||
.. _errorpages:
|
||||
|
||||
Custom Error Pages
|
||||
==================
|
||||
|
||||
Flask comes with a handy :func:`~flask.abort` function that aborts a
|
||||
request with an HTTP error code early. It will also provide a plain black
|
||||
and white error page for you with a basic description, but nothing fancy.
|
||||
|
||||
Depending on the error code it is less or more likely for the user to
|
||||
actually see such an error.
|
||||
|
||||
Common Error Codes
|
||||
------------------
|
||||
|
||||
The following error codes are some that are often displayed to the user,
|
||||
even if the application behaves correctly:
|
||||
|
||||
*404 Not Found*
|
||||
The good old "chap, you made a mistake typing that URL" message. So
|
||||
common that even novices to the internet know that 404 means: damn,
|
||||
the thing I was looking for is not there. It's a very good idea to
|
||||
make sure there is actually something useful on a 404 page, at least a
|
||||
link back to the index.
|
||||
|
||||
*403 Forbidden*
|
||||
If you have some kind of access control on your website, you will have
|
||||
to send a 403 code for disallowed resources. So make sure the user
|
||||
is not lost when they try to access a forbidden resource.
|
||||
|
||||
*410 Gone*
|
||||
Did you know that there the "404 Not Found" has a brother named "410
|
||||
Gone"? Few people actually implement that, but the idea is that
|
||||
resources that previously existed and got deleted answer with 410
|
||||
instead of 404. If you are not deleting documents permanently from
|
||||
the database but just mark them as deleted, do the user a favour and
|
||||
use the 410 code instead and display a message that what they were
|
||||
looking for was deleted for all eternity.
|
||||
|
||||
*500 Internal Server Error*
|
||||
Usually happens on programming errors or if the server is overloaded.
|
||||
A terribly good idea is to have a nice page there, because your
|
||||
application *will* fail sooner or later (see also:
|
||||
:ref:`application-errors`).
|
||||
|
||||
|
||||
Error Handlers
|
||||
--------------
|
||||
|
||||
An error handler is a function that returns a response when a type of error is
|
||||
raised, similar to how a view is a function that returns a response when a
|
||||
request URL is matched. It is passed the instance of the error being handled,
|
||||
which is most likely a :exc:`~werkzeug.exceptions.HTTPException`. An error
|
||||
handler for "500 Internal Server Error" will be passed uncaught exceptions in
|
||||
addition to explicit 500 errors.
|
||||
|
||||
An error handler is registered with the :meth:`~flask.Flask.errorhandler`
|
||||
decorator or the :meth:`~flask.Flask.register_error_handler` method. A handler
|
||||
can be registered for a status code, like 404, or for an exception class.
|
||||
|
||||
The status code of the response will not be set to the handler's code. Make
|
||||
sure to provide the appropriate HTTP status code when returning a response from
|
||||
a handler.
|
||||
|
||||
A handler for "500 Internal Server Error" will not be used when running in
|
||||
debug mode. Instead, the interactive debugger will be shown.
|
||||
|
||||
Here is an example implementation for a "404 Page Not Found" exception::
|
||||
|
||||
from flask import render_template
|
||||
|
||||
@app.errorhandler(404)
|
||||
def page_not_found(e):
|
||||
# note that we set the 404 status explicitly
|
||||
return render_template('404.html'), 404
|
||||
|
||||
When using the :ref:`application factory pattern <app-factories>`::
|
||||
|
||||
from flask import Flask, render_template
|
||||
|
||||
def page_not_found(e):
|
||||
return render_template('404.html'), 404
|
||||
|
||||
def create_app(config_filename):
|
||||
app = Flask(__name__)
|
||||
app.register_error_handler(404, page_not_found)
|
||||
return app
|
||||
|
||||
An example template might be this:
|
||||
|
||||
.. sourcecode:: html+jinja
|
||||
|
||||
{% extends "layout.html" %}
|
||||
{% block title %}Page Not Found{% endblock %}
|
||||
{% block body %}
|
||||
<h1>Page Not Found</h1>
|
||||
<p>What you were looking for is just not there.
|
||||
<p><a href="{{ url_for('index') }}">go somewhere nice</a>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
Returning API errors as JSON
|
||||
----------------------------
|
||||
|
||||
When using Flask for web APIs, you can use the same techniques as above
|
||||
to return JSON responses to API errors. :func:`~flask.abort` is called
|
||||
with a ``description`` parameter. The :meth:`~flask.errorhandler` will
|
||||
use that as the JSON error message, and set the status code to 404.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from flask import abort, jsonify
|
||||
|
||||
@app.errorhandler(404)
|
||||
def resource_not_found(e):
|
||||
return jsonify(error=str(e)), 404
|
||||
|
||||
@app.route("/cheese")
|
||||
def get_one_cheese():
|
||||
resource = get_resource()
|
||||
|
||||
if resource is None:
|
||||
abort(404, description="Resource not found")
|
||||
|
||||
return jsonify(resource)
|
||||
|
|
@ -1,186 +0,0 @@
|
|||
.. _fabric-deployment:
|
||||
|
||||
Deploying with Fabric
|
||||
=====================
|
||||
|
||||
`Fabric`_ is a tool for Python similar to Makefiles but with the ability
|
||||
to execute commands on a remote server. In combination with a properly
|
||||
set up Python package (:ref:`larger-applications`) and a good concept for
|
||||
configurations (:ref:`config`) it is very easy to deploy Flask
|
||||
applications to external servers.
|
||||
|
||||
Before we get started, here a quick checklist of things we have to ensure
|
||||
upfront:
|
||||
|
||||
- Fabric 1.0 has to be installed locally. This tutorial assumes the
|
||||
latest version of Fabric.
|
||||
- The application already has to be a package and requires a working
|
||||
:file:`setup.py` file (:ref:`distribute-deployment`).
|
||||
- In the following example we are using `mod_wsgi` for the remote
|
||||
servers. You can of course use your own favourite server there, but
|
||||
for this example we chose Apache + `mod_wsgi` because it's very easy
|
||||
to setup and has a simple way to reload applications without root
|
||||
access.
|
||||
|
||||
Creating the first Fabfile
|
||||
--------------------------
|
||||
|
||||
A fabfile is what controls what Fabric executes. It is named :file:`fabfile.py`
|
||||
and executed by the `fab` command. All the functions defined in that file
|
||||
will show up as `fab` subcommands. They are executed on one or more
|
||||
hosts. These hosts can be defined either in the fabfile or on the command
|
||||
line. In this case we will add them to the fabfile.
|
||||
|
||||
This is a basic first example that has the ability to upload the current
|
||||
source code to the server and install it into a pre-existing
|
||||
virtual environment::
|
||||
|
||||
from fabric.api import *
|
||||
|
||||
# the user to use for the remote commands
|
||||
env.user = 'appuser'
|
||||
# the servers where the commands are executed
|
||||
env.hosts = ['server1.example.com', 'server2.example.com']
|
||||
|
||||
def pack():
|
||||
# build the package
|
||||
local('python setup.py sdist --formats=gztar', capture=False)
|
||||
|
||||
def deploy():
|
||||
# figure out the package name and version
|
||||
dist = local('python setup.py --fullname', capture=True).strip()
|
||||
filename = '%s.tar.gz' % dist
|
||||
|
||||
# upload the package to the temporary folder on the server
|
||||
put('dist/%s' % filename, '/tmp/%s' % filename)
|
||||
|
||||
# install the package in the application's virtualenv with pip
|
||||
run('/var/www/yourapplication/env/bin/pip install /tmp/%s' % filename)
|
||||
|
||||
# remove the uploaded package
|
||||
run('rm -r /tmp/%s' % filename)
|
||||
|
||||
# touch the .wsgi file to trigger a reload in mod_wsgi
|
||||
run('touch /var/www/yourapplication.wsgi')
|
||||
|
||||
Running Fabfiles
|
||||
----------------
|
||||
|
||||
Now how do you execute that fabfile? You use the `fab` command. To
|
||||
deploy the current version of the code on the remote server you would use
|
||||
this command::
|
||||
|
||||
$ fab pack deploy
|
||||
|
||||
However this requires that our server already has the
|
||||
:file:`/var/www/yourapplication` folder created and
|
||||
:file:`/var/www/yourapplication/env` to be a virtual environment. Furthermore
|
||||
are we not creating the configuration or ``.wsgi`` file on the server. So
|
||||
how do we bootstrap a new server into our infrastructure?
|
||||
|
||||
This now depends on the number of servers we want to set up. If we just
|
||||
have one application server (which the majority of applications will
|
||||
have), creating a command in the fabfile for this is overkill. But
|
||||
obviously you can do that. In that case you would probably call it
|
||||
`setup` or `bootstrap` and then pass the servername explicitly on the
|
||||
command line::
|
||||
|
||||
$ fab -H newserver.example.com bootstrap
|
||||
|
||||
To setup a new server you would roughly do these steps:
|
||||
|
||||
1. Create the directory structure in :file:`/var/www`::
|
||||
|
||||
$ mkdir /var/www/yourapplication
|
||||
$ cd /var/www/yourapplication
|
||||
$ virtualenv --distribute env
|
||||
|
||||
2. Upload a new :file:`application.wsgi` file to the server and the
|
||||
configuration file for the application (eg: :file:`application.cfg`)
|
||||
|
||||
3. Create a new Apache config for ``yourapplication`` and activate it.
|
||||
Make sure to activate watching for changes of the ``.wsgi`` file so
|
||||
that we can automatically reload the application by touching it.
|
||||
(See :ref:`mod_wsgi-deployment` for more information)
|
||||
|
||||
So now the question is, where do the :file:`application.wsgi` and
|
||||
:file:`application.cfg` files come from?
|
||||
|
||||
The WSGI File
|
||||
-------------
|
||||
|
||||
The WSGI file has to import the application and also to set an environment
|
||||
variable so that the application knows where to look for the config. This
|
||||
is a short example that does exactly that::
|
||||
|
||||
import os
|
||||
os.environ['YOURAPPLICATION_CONFIG'] = '/var/www/yourapplication/application.cfg'
|
||||
from yourapplication import app
|
||||
|
||||
The application itself then has to initialize itself like this to look for
|
||||
the config at that environment variable::
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config.from_object('yourapplication.default_config')
|
||||
app.config.from_envvar('YOURAPPLICATION_CONFIG')
|
||||
|
||||
This approach is explained in detail in the :ref:`config` section of the
|
||||
documentation.
|
||||
|
||||
The Configuration File
|
||||
----------------------
|
||||
|
||||
Now as mentioned above, the application will find the correct
|
||||
configuration file by looking up the ``YOURAPPLICATION_CONFIG`` environment
|
||||
variable. So we have to put the configuration in a place where the
|
||||
application will able to find it. Configuration files have the unfriendly
|
||||
quality of being different on all computers, so you do not version them
|
||||
usually.
|
||||
|
||||
A popular approach is to store configuration files for different servers
|
||||
in a separate version control repository and check them out on all
|
||||
servers. Then symlink the file that is active for the server into the
|
||||
location where it's expected (eg: :file:`/var/www/yourapplication`).
|
||||
|
||||
Either way, in our case here we only expect one or two servers and we can
|
||||
upload them ahead of time by hand.
|
||||
|
||||
|
||||
First Deployment
|
||||
----------------
|
||||
|
||||
Now we can do our first deployment. We have set up the servers so that
|
||||
they have their virtual environments and activated apache configs. Now we
|
||||
can pack up the application and deploy it::
|
||||
|
||||
$ fab pack deploy
|
||||
|
||||
Fabric will now connect to all servers and run the commands as written
|
||||
down in the fabfile. First it will execute pack so that we have our
|
||||
tarball ready and then it will execute deploy and upload the source code
|
||||
to all servers and install it there. Thanks to the :file:`setup.py` file we
|
||||
will automatically pull in the required libraries into our virtual
|
||||
environment.
|
||||
|
||||
Next Steps
|
||||
----------
|
||||
|
||||
From that point onwards there is so much that can be done to make
|
||||
deployment actually fun:
|
||||
|
||||
- Create a `bootstrap` command that initializes new servers. It could
|
||||
initialize a new virtual environment, setup apache appropriately etc.
|
||||
- Put configuration files into a separate version control repository
|
||||
and symlink the active configs into place.
|
||||
- You could also put your application code into a repository and check
|
||||
out the latest version on the server and then install. That way you
|
||||
can also easily go back to older versions.
|
||||
- hook in testing functionality so that you can deploy to an external
|
||||
server and run the test suite.
|
||||
|
||||
Working with Fabric is fun and you will notice that it's quite magical to
|
||||
type ``fab deploy`` and see your application being deployed automatically
|
||||
to one or more remote servers.
|
||||
|
||||
|
||||
.. _Fabric: https://www.fabfile.org/
|
||||
|
|
@ -24,8 +24,11 @@ the root path of the domain you either need to configure the web server to
|
|||
serve the icon at the root or if you can't do that you're out of luck. If
|
||||
however your application is the root you can simply route a redirect::
|
||||
|
||||
app.add_url_rule('/favicon.ico',
|
||||
redirect_to=url_for('static', filename='favicon.ico'))
|
||||
app.add_url_rule(
|
||||
"/favicon.ico",
|
||||
endpoint="favicon",
|
||||
redirect_to=url_for("static", filename="favicon.ico"),
|
||||
)
|
||||
|
||||
If you want to save the extra redirect request you can also write a view
|
||||
using :func:`~flask.send_from_directory`::
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
.. _uploading-files:
|
||||
|
||||
Uploading Files
|
||||
===============
|
||||
|
||||
|
|
@ -39,7 +37,7 @@ Why do we limit the extensions that are allowed? You probably don't want
|
|||
your users to be able to upload everything there if the server is directly
|
||||
sending out the data to the client. That way you can make sure that users
|
||||
are not able to upload HTML files that would cause XSS problems (see
|
||||
:ref:`xss`). Also make sure to disallow ``.php`` files if the server
|
||||
:ref:`security-xss`). Also make sure to disallow ``.php`` files if the server
|
||||
executes them, but who has PHP installed on their server, right? :)
|
||||
|
||||
Next the functions that check if an extension is valid and that uploads
|
||||
|
|
@ -57,16 +55,15 @@ the file and redirects the user to the URL for the uploaded file::
|
|||
flash('No file part')
|
||||
return redirect(request.url)
|
||||
file = request.files['file']
|
||||
# if user does not select file, browser also
|
||||
# submit an empty part without filename
|
||||
# If the user does not select a file, the browser submits an
|
||||
# empty file without a filename.
|
||||
if file.filename == '':
|
||||
flash('No selected file')
|
||||
return redirect(request.url)
|
||||
if file and allowed_file(file.filename):
|
||||
filename = secure_filename(file.filename)
|
||||
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
|
||||
return redirect(url_for('uploaded_file',
|
||||
filename=filename))
|
||||
return redirect(url_for('download_file', name=filename))
|
||||
return '''
|
||||
<!doctype html>
|
||||
<title>Upload new File</title>
|
||||
|
|
@ -104,31 +101,28 @@ before storing it directly on the filesystem.
|
|||
>>> secure_filename('../../../../home/username/.bashrc')
|
||||
'home_username_.bashrc'
|
||||
|
||||
Now one last thing is missing: the serving of the uploaded files. In the
|
||||
:func:`upload_file()` we redirect the user to
|
||||
``url_for('uploaded_file', filename=filename)``, that is, ``/uploads/filename``.
|
||||
So we write the :func:`uploaded_file` function to return the file of that name. As
|
||||
of Flask 0.5 we can use a function that does that for us::
|
||||
We want to be able to serve the uploaded files so they can be downloaded
|
||||
by users. We'll define a ``download_file`` view to serve files in the
|
||||
upload folder by name. ``url_for("download_file", name=name)`` generates
|
||||
download URLs.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from flask import send_from_directory
|
||||
|
||||
@app.route('/uploads/<filename>')
|
||||
def uploaded_file(filename):
|
||||
return send_from_directory(app.config['UPLOAD_FOLDER'],
|
||||
filename)
|
||||
@app.route('/uploads/<name>')
|
||||
def download_file(name):
|
||||
return send_from_directory(app.config["UPLOAD_FOLDER"], name)
|
||||
|
||||
Alternatively you can register `uploaded_file` as `build_only` rule and
|
||||
use the :class:`~werkzeug.wsgi.SharedDataMiddleware`. This also works with
|
||||
older versions of Flask::
|
||||
If you're using middleware or the HTTP server to serve files, you can
|
||||
register the ``download_file`` endpoint as ``build_only`` so ``url_for``
|
||||
will work without a view function.
|
||||
|
||||
from werkzeug.middleware.shared_data import SharedDataMiddleware
|
||||
app.add_url_rule('/uploads/<filename>', 'uploaded_file',
|
||||
build_only=True)
|
||||
app.wsgi_app = SharedDataMiddleware(app.wsgi_app, {
|
||||
'/uploads': app.config['UPLOAD_FOLDER']
|
||||
})
|
||||
.. code-block:: python
|
||||
|
||||
If you now run the application everything should work as expected.
|
||||
app.add_url_rule(
|
||||
"/uploads/<name>", endpoint="download_file", build_only=True
|
||||
)
|
||||
|
||||
|
||||
Improving Uploads
|
||||
|
|
@ -137,17 +131,17 @@ Improving Uploads
|
|||
.. versionadded:: 0.6
|
||||
|
||||
So how exactly does Flask handle uploads? Well it will store them in the
|
||||
webserver's memory if the files are reasonable small otherwise in a
|
||||
webserver's memory if the files are reasonably small, otherwise in a
|
||||
temporary location (as returned by :func:`tempfile.gettempdir`). But how
|
||||
do you specify the maximum file size after which an upload is aborted? By
|
||||
default Flask will happily accept file uploads to an unlimited amount of
|
||||
default Flask will happily accept file uploads with an unlimited amount of
|
||||
memory, but you can limit that by setting the ``MAX_CONTENT_LENGTH``
|
||||
config key::
|
||||
|
||||
from flask import Flask, Request
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024
|
||||
app.config['MAX_CONTENT_LENGTH'] = 16 * 1000 * 1000
|
||||
|
||||
The code above will limit the maximum allowed payload to 16 megabytes.
|
||||
If a larger file is transmitted, Flask will raise a
|
||||
|
|
@ -169,10 +163,9 @@ Upload Progress Bars
|
|||
|
||||
A while ago many developers had the idea to read the incoming file in
|
||||
small chunks and store the upload progress in the database to be able to
|
||||
poll the progress with JavaScript from the client. Long story short: the
|
||||
client asks the server every 5 seconds how much it has transmitted
|
||||
already. Do you realize the irony? The client is asking for something it
|
||||
should already know.
|
||||
poll the progress with JavaScript from the client. The client asks the
|
||||
server every 5 seconds how much it has transmitted, but this is
|
||||
something it should already know.
|
||||
|
||||
An Easier Solution
|
||||
------------------
|
||||
|
|
@ -182,9 +175,8 @@ are JavaScript libraries like jQuery_ that have form plugins to ease the
|
|||
construction of progress bar.
|
||||
|
||||
Because the common pattern for file uploads exists almost unchanged in all
|
||||
applications dealing with uploads, there is also a Flask extension called
|
||||
`Flask-Uploads`_ that implements a full fledged upload mechanism with white and
|
||||
blacklisting of extensions and more.
|
||||
applications dealing with uploads, there are also some Flask extensions that
|
||||
implement a full fledged upload mechanism that allows controlling which
|
||||
file extensions are allowed to be uploaded.
|
||||
|
||||
.. _jQuery: https://jquery.com/
|
||||
.. _Flask-Uploads: https://pythonhosted.org/Flask-Uploads/
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
.. _message-flashing-pattern:
|
||||
|
||||
Message Flashing
|
||||
================
|
||||
|
||||
|
|
@ -103,7 +101,7 @@ error messages could be displayed with a red background.
|
|||
To flash a message with a different category, just use the second argument
|
||||
to the :func:`~flask.flash` function::
|
||||
|
||||
flash(u'Invalid password provided', 'error')
|
||||
flash('Invalid password provided', 'error')
|
||||
|
||||
Inside the template you then have to tell the
|
||||
:func:`~flask.get_flashed_messages` function to also return the
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
.. _patterns:
|
||||
|
||||
Patterns for Flask
|
||||
==================
|
||||
|
||||
|
|
@ -20,10 +18,7 @@ collected in the following pages.
|
|||
packages
|
||||
appfactories
|
||||
appdispatch
|
||||
apierrors
|
||||
urlprocessors
|
||||
distribute
|
||||
fabric
|
||||
sqlite3
|
||||
sqlalchemy
|
||||
fileuploads
|
||||
|
|
@ -32,8 +27,7 @@ collected in the following pages.
|
|||
wtforms
|
||||
templateinheritance
|
||||
flashing
|
||||
jquery
|
||||
errorpages
|
||||
javascript
|
||||
lazyloading
|
||||
mongoengine
|
||||
favicon
|
||||
|
|
|
|||
261
docs/patterns/javascript.rst
Normal file
|
|
@ -0,0 +1,261 @@
|
|||
JavaScript, ``fetch``, and JSON
|
||||
===============================
|
||||
|
||||
You may want to make your HTML page dynamic, by changing data without
|
||||
reloading the entire page. Instead of submitting an HTML ``<form>`` and
|
||||
performing a redirect to re-render the template, you can add
|
||||
`JavaScript`_ that calls |fetch|_ and replaces content on the page.
|
||||
|
||||
|fetch|_ is the modern, built-in JavaScript solution to making
|
||||
requests from a page. You may have heard of other "AJAX" methods and
|
||||
libraries, such as |XHR|_ or `jQuery`_. These are no longer needed in
|
||||
modern browsers, although you may choose to use them or another library
|
||||
depending on your application's requirements. These docs will only focus
|
||||
on built-in JavaScript features.
|
||||
|
||||
.. _JavaScript: https://developer.mozilla.org/Web/JavaScript
|
||||
.. |fetch| replace:: ``fetch()``
|
||||
.. _fetch: https://developer.mozilla.org/Web/API/Fetch_API
|
||||
.. |XHR| replace:: ``XMLHttpRequest()``
|
||||
.. _XHR: https://developer.mozilla.org/Web/API/XMLHttpRequest
|
||||
.. _jQuery: https://jquery.com/
|
||||
|
||||
|
||||
Rendering Templates
|
||||
-------------------
|
||||
|
||||
It is important to understand the difference between templates and
|
||||
JavaScript. Templates are rendered on the server, before the response is
|
||||
sent to the user's browser. JavaScript runs in the user's browser, after
|
||||
the template is rendered and sent. Therefore, it is impossible to use
|
||||
JavaScript to affect how the Jinja template is rendered, but it is
|
||||
possible to render data into the JavaScript that will run.
|
||||
|
||||
To provide data to JavaScript when rendering the template, use the
|
||||
:func:`~jinja-filters.tojson` filter in a ``<script>`` block. This will
|
||||
convert the data to a valid JavaScript object, and ensure that any
|
||||
unsafe HTML characters are rendered safely. If you do not use the
|
||||
``tojson`` filter, you will get a ``SyntaxError`` in the browser
|
||||
console.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
data = generate_report()
|
||||
return render_template("report.html", chart_data=data)
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
<script>
|
||||
const chart_data = {{ chart_data|tojson }}
|
||||
chartLib.makeChart(chart_data)
|
||||
</script>
|
||||
|
||||
A less common pattern is to add the data to a ``data-`` attribute on an
|
||||
HTML tag. In this case, you must use single quotes around the value, not
|
||||
double quotes, otherwise you will produce invalid or unsafe HTML.
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
<div data-chart='{{ chart_data|tojson }}'></div>
|
||||
|
||||
|
||||
Generating URLs
|
||||
---------------
|
||||
|
||||
The other way to get data from the server to JavaScript is to make a
|
||||
request for it. First, you need to know the URL to request.
|
||||
|
||||
The simplest way to generate URLs is to continue to use
|
||||
:func:`~flask.url_for` when rendering the template. For example:
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
const user_url = {{ url_for("user", id=current_user.id)|tojson }}
|
||||
fetch(user_url).then(...)
|
||||
|
||||
However, you might need to generate a URL based on information you only
|
||||
know in JavaScript. As discussed above, JavaScript runs in the user's
|
||||
browser, not as part of the template rendering, so you can't use
|
||||
``url_for`` at that point.
|
||||
|
||||
In this case, you need to know the "root URL" under which your
|
||||
application is served. In simple setups, this is ``/``, but it might
|
||||
also be something else, like ``https://example.com/myapp/``.
|
||||
|
||||
A simple way to tell your JavaScript code about this root is to set it
|
||||
as a global variable when rendering the template. Then you can use it
|
||||
when generating URLs from JavaScript.
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
const SCRIPT_ROOT = {{ request.script_root|tojson }}
|
||||
let user_id = ... // do something to get a user id from the page
|
||||
let user_url = `${SCRIPT_ROOT}/user/${user_id}`
|
||||
fetch(user_url).then(...)
|
||||
|
||||
|
||||
Making a Request with ``fetch``
|
||||
-------------------------------
|
||||
|
||||
|fetch|_ takes two arguments, a URL and an object with other options,
|
||||
and returns a |Promise|_. We won't cover all the available options, and
|
||||
will only use ``then()`` on the promise, not other callbacks or
|
||||
``await`` syntax. Read the linked MDN docs for more information about
|
||||
those features.
|
||||
|
||||
By default, the GET method is used. If the response contains JSON, it
|
||||
can be used with a ``then()`` callback chain.
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
const room_url = {{ url_for("room_detail", id=room.id)|tojson }}
|
||||
fetch(room_url)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
// data is a parsed JSON object
|
||||
})
|
||||
|
||||
To send data, use a data method such as POST, and pass the ``body``
|
||||
option. The most common types for data are form data or JSON data.
|
||||
|
||||
To send form data, pass a populated |FormData|_ object. This uses the
|
||||
same format as an HTML form, and would be accessed with ``request.form``
|
||||
in a Flask view.
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
let data = new FormData()
|
||||
data.append("name", "Flask Room")
|
||||
data.append("description", "Talk about Flask here.")
|
||||
fetch(room_url, {
|
||||
"method": "POST",
|
||||
"body": data,
|
||||
}).then(...)
|
||||
|
||||
In general, prefer sending request data as form data, as would be used
|
||||
when submitting an HTML form. JSON can represent more complex data, but
|
||||
unless you need that it's better to stick with the simpler format. When
|
||||
sending JSON data, the ``Content-Type: application/json`` header must be
|
||||
sent as well, otherwise Flask will return a 415 Unsupported Media Type
|
||||
error.
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
let data = {
|
||||
"name": "Flask Room",
|
||||
"description": "Talk about Flask here.",
|
||||
}
|
||||
fetch(room_url, {
|
||||
"method": "POST",
|
||||
"headers": {"Content-Type": "application/json"},
|
||||
"body": JSON.stringify(data),
|
||||
}).then(...)
|
||||
|
||||
.. |Promise| replace:: ``Promise``
|
||||
.. _Promise: https://developer.mozilla.org/Web/JavaScript/Reference/Global_Objects/Promise
|
||||
.. |FormData| replace:: ``FormData``
|
||||
.. _FormData: https://developer.mozilla.org/en-US/docs/Web/API/FormData
|
||||
|
||||
|
||||
Following Redirects
|
||||
-------------------
|
||||
|
||||
A response might be a redirect, for example if you logged in with
|
||||
JavaScript instead of a traditional HTML form, and your view returned
|
||||
a redirect instead of JSON. JavaScript requests do follow redirects, but
|
||||
they don't change the page. If you want to make the page change you can
|
||||
inspect the response and apply the redirect manually.
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
fetch("/login", {"body": ...}).then(
|
||||
response => {
|
||||
if (response.redirected) {
|
||||
window.location = response.url
|
||||
} else {
|
||||
showLoginError()
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
Replacing Content
|
||||
-----------------
|
||||
|
||||
A response might be new HTML, either a new section of the page to add or
|
||||
replace, or an entirely new page. In general, if you're returning the
|
||||
entire page, it would be better to handle that with a redirect as shown
|
||||
in the previous section. The following example shows how to replace a
|
||||
``<div>`` with the HTML returned by a request.
|
||||
|
||||
.. code-block:: html
|
||||
|
||||
<div id="geology-fact">
|
||||
{{ include "geology_fact.html" }}
|
||||
</div>
|
||||
<script>
|
||||
const geology_url = {{ url_for("geology_fact")|tojson }}
|
||||
const geology_div = getElementById("geology-fact")
|
||||
fetch(geology_url)
|
||||
.then(response => response.text)
|
||||
.then(text => geology_div.innerHTML = text)
|
||||
</script>
|
||||
|
||||
|
||||
Return JSON from Views
|
||||
----------------------
|
||||
|
||||
To return a JSON object from your API view, you can directly return a
|
||||
dict from the view. It will be serialized to JSON automatically.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@app.route("/user/<int:id>")
|
||||
def user_detail(id):
|
||||
user = User.query.get_or_404(id)
|
||||
return {
|
||||
"username": User.username,
|
||||
"email": User.email,
|
||||
"picture": url_for("static", filename=f"users/{id}/profile.png"),
|
||||
}
|
||||
|
||||
If you want to return another JSON type, use the
|
||||
:func:`~flask.json.jsonify` function, which creates a response object
|
||||
with the given data serialized to JSON.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from flask import jsonify
|
||||
|
||||
@app.route("/users")
|
||||
def user_list():
|
||||
users = User.query.order_by(User.name).all()
|
||||
return jsonify([u.to_json() for u in users])
|
||||
|
||||
It is usually not a good idea to return file data in a JSON response.
|
||||
JSON cannot represent binary data directly, so it must be base64
|
||||
encoded, which can be slow, takes more bandwidth to send, and is not as
|
||||
easy to cache. Instead, serve files using one view, and generate a URL
|
||||
to the desired file to include in the JSON. Then the client can make a
|
||||
separate request to get the linked resource after getting the JSON.
|
||||
|
||||
|
||||
Receiving JSON in Views
|
||||
-----------------------
|
||||
|
||||
Use the :attr:`~flask.Request.json` property of the
|
||||
:data:`~flask.request` object to decode the request's body as JSON. If
|
||||
the body is not valid JSON, a 400 Bad Request error will be raised. If
|
||||
the ``Content-Type`` header is not set to ``application/json``, a 415
|
||||
Unsupported Media Type error will be raised.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from flask import request
|
||||
|
||||
@app.post("/user/<int:id>")
|
||||
def user_update(id):
|
||||
user = User.query.get_or_404(id)
|
||||
user.update_from_json(request.json)
|
||||
db.session.commit()
|
||||
return user.to_json()
|
||||
|
|
@ -1,167 +1,6 @@
|
|||
:orphan:
|
||||
|
||||
AJAX with jQuery
|
||||
================
|
||||
|
||||
`jQuery`_ is a small JavaScript library commonly used to simplify working
|
||||
with the DOM and JavaScript in general. It is the perfect tool to make
|
||||
web applications more dynamic by exchanging JSON between server and
|
||||
client.
|
||||
|
||||
JSON itself is a very lightweight transport format, very similar to how
|
||||
Python primitives (numbers, strings, dicts and lists) look like which is
|
||||
widely supported and very easy to parse. It became popular a few years
|
||||
ago and quickly replaced XML as transport format in web applications.
|
||||
|
||||
.. _jQuery: https://jquery.com/
|
||||
|
||||
Loading jQuery
|
||||
--------------
|
||||
|
||||
In order to use jQuery, you have to download it first and place it in the
|
||||
static folder of your application and then ensure it's loaded. Ideally
|
||||
you have a layout template that is used for all pages where you just have
|
||||
to add a script statement to the bottom of your ``<body>`` to load jQuery:
|
||||
|
||||
.. sourcecode:: html
|
||||
|
||||
<script type=text/javascript src="{{
|
||||
url_for('static', filename='jquery.js') }}"></script>
|
||||
|
||||
Another method is using Google's `AJAX Libraries API
|
||||
<https://developers.google.com/speed/libraries/>`_ to load jQuery:
|
||||
|
||||
.. sourcecode:: html
|
||||
|
||||
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
|
||||
<script>window.jQuery || document.write('<script src="{{
|
||||
url_for('static', filename='jquery.js') }}">\x3C/script>')</script>
|
||||
|
||||
In this case you have to put jQuery into your static folder as a fallback, but it will
|
||||
first try to load it directly from Google. This has the advantage that your
|
||||
website will probably load faster for users if they went to at least one
|
||||
other website before using the same jQuery version from Google because it
|
||||
will already be in the browser cache.
|
||||
|
||||
Where is My Site?
|
||||
-----------------
|
||||
|
||||
Do you know where your application is? If you are developing the answer
|
||||
is quite simple: it's on localhost port something and directly on the root
|
||||
of that server. But what if you later decide to move your application to
|
||||
a different location? For example to ``http://example.com/myapp``? On
|
||||
the server side this never was a problem because we were using the handy
|
||||
:func:`~flask.url_for` function that could answer that question for
|
||||
us, but if we are using jQuery we should not hardcode the path to
|
||||
the application but make that dynamic, so how can we do that?
|
||||
|
||||
A simple method would be to add a script tag to our page that sets a
|
||||
global variable to the prefix to the root of the application. Something
|
||||
like this:
|
||||
|
||||
.. sourcecode:: html+jinja
|
||||
|
||||
<script type=text/javascript>
|
||||
$SCRIPT_ROOT = {{ request.script_root|tojson|safe }};
|
||||
</script>
|
||||
|
||||
The ``|safe`` is necessary in Flask before 0.10 so that Jinja does not
|
||||
escape the JSON encoded string with HTML rules. Usually this would be
|
||||
necessary, but we are inside a ``script`` block here where different rules
|
||||
apply.
|
||||
|
||||
.. admonition:: Information for Pros
|
||||
|
||||
In HTML the ``script`` tag is declared ``CDATA`` which means that entities
|
||||
will not be parsed. Everything until ``</script>`` is handled as script.
|
||||
This also means that there must never be any ``</`` between the script
|
||||
tags. ``|tojson`` is kind enough to do the right thing here and
|
||||
escape slashes for you (``{{ "</script>"|tojson|safe }}`` is rendered as
|
||||
``"<\/script>"``).
|
||||
|
||||
In Flask 0.10 it goes a step further and escapes all HTML tags with
|
||||
unicode escapes. This makes it possible for Flask to automatically
|
||||
mark the result as HTML safe.
|
||||
|
||||
|
||||
JSON View Functions
|
||||
-------------------
|
||||
|
||||
Now let's create a server side function that accepts two URL arguments of
|
||||
numbers which should be added together and then sent back to the
|
||||
application in a JSON object. This is a really ridiculous example and is
|
||||
something you usually would do on the client side alone, but a simple
|
||||
example that shows how you would use jQuery and Flask nonetheless::
|
||||
|
||||
from flask import Flask, jsonify, render_template, request
|
||||
app = Flask(__name__)
|
||||
|
||||
@app.route('/_add_numbers')
|
||||
def add_numbers():
|
||||
a = request.args.get('a', 0, type=int)
|
||||
b = request.args.get('b', 0, type=int)
|
||||
return jsonify(result=a + b)
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
return render_template('index.html')
|
||||
|
||||
As you can see I also added an `index` method here that renders a
|
||||
template. This template will load jQuery as above and have a little form where
|
||||
we can add two numbers and a link to trigger the function on the server
|
||||
side.
|
||||
|
||||
Note that we are using the :meth:`~werkzeug.datastructures.MultiDict.get` method here
|
||||
which will never fail. If the key is missing a default value (here ``0``)
|
||||
is returned. Furthermore it can convert values to a specific type (like
|
||||
in our case `int`). This is especially handy for code that is
|
||||
triggered by a script (APIs, JavaScript etc.) because you don't need
|
||||
special error reporting in that case.
|
||||
|
||||
The HTML
|
||||
--------
|
||||
|
||||
Your index.html template either has to extend a :file:`layout.html` template with
|
||||
jQuery loaded and the `$SCRIPT_ROOT` variable set, or do that on the top.
|
||||
Here's the HTML code needed for our little application (:file:`index.html`).
|
||||
Notice that we also drop the script directly into the HTML here. It is
|
||||
usually a better idea to have that in a separate script file:
|
||||
|
||||
.. sourcecode:: html
|
||||
|
||||
<script type=text/javascript>
|
||||
$(function() {
|
||||
$('a#calculate').bind('click', function() {
|
||||
$.getJSON($SCRIPT_ROOT + '/_add_numbers', {
|
||||
a: $('input[name="a"]').val(),
|
||||
b: $('input[name="b"]').val()
|
||||
}, function(data) {
|
||||
$("#result").text(data.result);
|
||||
});
|
||||
return false;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<h1>jQuery Example</h1>
|
||||
<p><input type=text size=5 name=a> +
|
||||
<input type=text size=5 name=b> =
|
||||
<span id=result>?</span>
|
||||
<p><a href=# id=calculate>calculate server side</a>
|
||||
|
||||
I won't go into detail here about how jQuery works, just a very quick
|
||||
explanation of the little bit of code above:
|
||||
|
||||
1. ``$(function() { ... })`` specifies code that should run once the
|
||||
browser is done loading the basic parts of the page.
|
||||
2. ``$('selector')`` selects an element and lets you operate on it.
|
||||
3. ``element.bind('event', func)`` specifies a function that should run
|
||||
when the user clicked on the element. If that function returns
|
||||
`false`, the default behavior will not kick in (in this case, navigate
|
||||
to the `#` URL).
|
||||
4. ``$.getJSON(url, data, func)`` sends a ``GET`` request to `url` and will
|
||||
send the contents of the `data` object as query parameters. Once the
|
||||
data arrived, it will call the given function with the return value as
|
||||
argument. Note that we can use the `$SCRIPT_ROOT` variable here that
|
||||
we set earlier.
|
||||
|
||||
Check out the :gh:`example source <examples/javascript>` for a full
|
||||
application demonstrating the code on this page, as well as the same
|
||||
thing using ``XMLHttpRequest`` and ``fetch``.
|
||||
Obsolete, see :doc:`/patterns/javascript` instead.
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ write this by having a function that calls into
|
|||
name and a dot, and by wrapping `view_func` in a `LazyView` as needed. ::
|
||||
|
||||
def url(import_name, url_rules=[], **options):
|
||||
view = LazyView('yourapplication.' + import_name)
|
||||
view = LazyView(f"yourapplication.{import_name}")
|
||||
for url_rule in url_rules:
|
||||
app.add_url_rule(url_rule, view_func=view, **options)
|
||||
|
||||
|
|
|
|||
|
|
@ -10,8 +10,7 @@ A running MongoDB server and `Flask-MongoEngine`_ are required. ::
|
|||
pip install flask-mongoengine
|
||||
|
||||
.. _MongoEngine: http://mongoengine.org
|
||||
.. _Flask-MongoEngine: https://flask-mongoengine.readthedocs.io
|
||||
|
||||
.. _Flask-MongoEngine: https://docs.mongoengine.org/projects/flask-mongoengine/en/latest/
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
|
@ -80,7 +79,7 @@ Queries
|
|||
Use the class ``objects`` attribute to make queries. A keyword argument
|
||||
looks for an equal value on the field. ::
|
||||
|
||||
bttf = Movies.objects(title="Back To The Future").get_or_404()
|
||||
bttf = Movie.objects(title="Back To The Future").get_or_404()
|
||||
|
||||
Query operators may be used by concatenating them with the field name
|
||||
using a double-underscore. ``objects``, and queries returned by
|
||||
|
|
|
|||
|
|
@ -1,7 +0,0 @@
|
|||
:orphan:
|
||||
|
||||
MongoDB with MongoKit
|
||||
=====================
|
||||
|
||||
MongoKit is no longer maintained. See :doc:`/patterns/mongoengine`
|
||||
instead.
|
||||
|
|
@ -1,7 +1,5 @@
|
|||
.. _larger-applications:
|
||||
|
||||
Larger Applications
|
||||
===================
|
||||
Large Applications as Packages
|
||||
==============================
|
||||
|
||||
Imagine a simple flask application structure that looks like this::
|
||||
|
||||
|
|
@ -17,7 +15,7 @@ Imagine a simple flask application structure that looks like this::
|
|||
|
||||
While this is fine for small applications, for larger applications
|
||||
it's a good idea to use a package instead of a module.
|
||||
The :ref:`tutorial <tutorial>` is structured to use the package pattern,
|
||||
The :doc:`/tutorial/index` is structured to use the package pattern,
|
||||
see the :gh:`example code <examples/tutorial>`.
|
||||
|
||||
Simple Packages
|
||||
|
|
@ -44,36 +42,34 @@ You should then end up with something like that::
|
|||
But how do you run your application now? The naive ``python
|
||||
yourapplication/__init__.py`` will not work. Let's just say that Python
|
||||
does not want modules in packages to be the startup file. But that is not
|
||||
a big problem, just add a new file called :file:`setup.py` next to the inner
|
||||
:file:`yourapplication` folder with the following contents::
|
||||
a big problem, just add a new file called :file:`pyproject.toml` next to the inner
|
||||
:file:`yourapplication` folder with the following contents:
|
||||
|
||||
from setuptools import setup
|
||||
.. code-block:: toml
|
||||
|
||||
setup(
|
||||
name='yourapplication',
|
||||
packages=['yourapplication'],
|
||||
include_package_data=True,
|
||||
install_requires=[
|
||||
'flask',
|
||||
],
|
||||
)
|
||||
[project]
|
||||
name = "yourapplication"
|
||||
dependencies = [
|
||||
"flask",
|
||||
]
|
||||
|
||||
In order to run the application you need to export an environment variable
|
||||
that tells Flask where to find the application instance::
|
||||
[build-system]
|
||||
requires = ["flit_core<4"]
|
||||
build-backend = "flit_core.buildapi"
|
||||
|
||||
$ export FLASK_APP=yourapplication
|
||||
Install your application so it is importable:
|
||||
|
||||
If you are outside of the project directory make sure to provide the exact
|
||||
path to your application directory. Similarly you can turn on the
|
||||
development features like this::
|
||||
|
||||
$ export FLASK_ENV=development
|
||||
|
||||
In order to install and run the application you need to issue the following
|
||||
commands::
|
||||
.. code-block:: text
|
||||
|
||||
$ pip install -e .
|
||||
$ flask run
|
||||
|
||||
To use the ``flask`` command and run your application you need to set
|
||||
the ``--app`` option that tells Flask where to find the application
|
||||
instance:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ flask --app yourapplication run
|
||||
|
||||
What did we gain from this? Now we can restructure the application a bit
|
||||
into multiple modules. The only thing you have to remember is the
|
||||
|
|
@ -105,7 +101,7 @@ And this is what :file:`views.py` would look like::
|
|||
You should then end up with something like that::
|
||||
|
||||
/yourapplication
|
||||
setup.py
|
||||
pyproject.toml
|
||||
/yourapplication
|
||||
__init__.py
|
||||
views.py
|
||||
|
|
@ -127,12 +123,6 @@ You should then end up with something like that::
|
|||
ensuring the module is imported and we are doing that at the bottom of
|
||||
the file.
|
||||
|
||||
There are still some problems with that approach but if you want to use
|
||||
decorators there is no way around that. Check out the
|
||||
:ref:`becomingbig` section for some inspiration how to deal with that.
|
||||
|
||||
|
||||
.. _working-with-modules:
|
||||
|
||||
Working with Blueprints
|
||||
-----------------------
|
||||
|
|
@ -140,4 +130,4 @@ Working with Blueprints
|
|||
If you have larger applications it's recommended to divide them into
|
||||
smaller groups where each group is implemented with the help of a
|
||||
blueprint. For a gentle introduction into this topic refer to the
|
||||
:ref:`blueprints` chapter of the documentation.
|
||||
:doc:`/blueprints` chapter of the documentation.
|
||||
|
|
|
|||
|
|
@ -52,4 +52,4 @@ Example usage::
|
|||
files = request.files
|
||||
# At this point the hash is fully constructed.
|
||||
checksum = hash.hexdigest()
|
||||
return 'Hash was: %s' % checksum
|
||||
return f"Hash was: {checksum}"
|
||||
|
|
|
|||
|
|
@ -1,12 +1,10 @@
|
|||
.. _sqlalchemy-pattern:
|
||||
|
||||
SQLAlchemy in Flask
|
||||
===================
|
||||
|
||||
Many people prefer `SQLAlchemy`_ for database access. In this case it's
|
||||
encouraged to use a package instead of a module for your flask application
|
||||
and drop the models into a separate module (:ref:`larger-applications`).
|
||||
While that is not necessary, it makes a lot of sense.
|
||||
and drop the models into a separate module (:doc:`packages`). While that
|
||||
is not necessary, it makes a lot of sense.
|
||||
|
||||
There are four very common ways to use SQLAlchemy. I will outline each
|
||||
of them here:
|
||||
|
|
@ -36,10 +34,9 @@ official documentation on the `declarative`_ extension.
|
|||
Here's the example :file:`database.py` module for your application::
|
||||
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import scoped_session, sessionmaker
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.orm import scoped_session, sessionmaker, declarative_base
|
||||
|
||||
engine = create_engine('sqlite:////tmp/test.db', convert_unicode=True)
|
||||
engine = create_engine('sqlite:////tmp/test.db')
|
||||
db_session = scoped_session(sessionmaker(autocommit=False,
|
||||
autoflush=False,
|
||||
bind=engine))
|
||||
|
|
@ -86,7 +83,7 @@ Here is an example model (put this into :file:`models.py`, e.g.)::
|
|||
self.email = email
|
||||
|
||||
def __repr__(self):
|
||||
return '<User %r>' % (self.name)
|
||||
return f'<User {self.name!r}>'
|
||||
|
||||
To create the database you can use the `init_db` function:
|
||||
|
||||
|
|
@ -104,13 +101,12 @@ You can insert entries into the database like this:
|
|||
Querying is simple as well:
|
||||
|
||||
>>> User.query.all()
|
||||
[<User u'admin'>]
|
||||
[<User 'admin'>]
|
||||
>>> User.query.filter(User.name == 'admin').first()
|
||||
<User u'admin'>
|
||||
<User 'admin'>
|
||||
|
||||
.. _SQLAlchemy: https://www.sqlalchemy.org/
|
||||
.. _declarative:
|
||||
https://docs.sqlalchemy.org/en/latest/orm/extensions/declarative/
|
||||
.. _declarative: https://docs.sqlalchemy.org/en/latest/orm/extensions/declarative/
|
||||
|
||||
Manual Object Relational Mapping
|
||||
--------------------------------
|
||||
|
|
@ -127,7 +123,7 @@ Here is an example :file:`database.py` module for your application::
|
|||
from sqlalchemy import create_engine, MetaData
|
||||
from sqlalchemy.orm import scoped_session, sessionmaker
|
||||
|
||||
engine = create_engine('sqlite:////tmp/test.db', convert_unicode=True)
|
||||
engine = create_engine('sqlite:////tmp/test.db')
|
||||
metadata = MetaData()
|
||||
db_session = scoped_session(sessionmaker(autocommit=False,
|
||||
autoflush=False,
|
||||
|
|
@ -135,9 +131,8 @@ Here is an example :file:`database.py` module for your application::
|
|||
def init_db():
|
||||
metadata.create_all(bind=engine)
|
||||
|
||||
As in the declarative approach, you need to close the session after
|
||||
each request or application context shutdown. Put this into your
|
||||
application module::
|
||||
As in the declarative approach, you need to close the session after each app
|
||||
context. Put this into your application module::
|
||||
|
||||
from yourapplication.database import db_session
|
||||
|
||||
|
|
@ -159,7 +154,7 @@ Here is an example table and model (put this into :file:`models.py`)::
|
|||
self.email = email
|
||||
|
||||
def __repr__(self):
|
||||
return '<User %r>' % (self.name)
|
||||
return f'<User {self.name!r}>'
|
||||
|
||||
users = Table('users', metadata,
|
||||
Column('id', Integer, primary_key=True),
|
||||
|
|
@ -179,7 +174,7 @@ you basically only need the engine::
|
|||
|
||||
from sqlalchemy import create_engine, MetaData, Table
|
||||
|
||||
engine = create_engine('sqlite:////tmp/test.db', convert_unicode=True)
|
||||
engine = create_engine('sqlite:////tmp/test.db')
|
||||
metadata = MetaData(bind=engine)
|
||||
|
||||
Then you can either declare the tables in your code like in the examples
|
||||
|
|
@ -200,19 +195,19 @@ SQLAlchemy will automatically commit for us.
|
|||
To query your database, you use the engine directly or use a connection:
|
||||
|
||||
>>> users.select(users.c.id == 1).execute().first()
|
||||
(1, u'admin', u'admin@localhost')
|
||||
(1, 'admin', 'admin@localhost')
|
||||
|
||||
These results are also dict-like tuples:
|
||||
|
||||
>>> r = users.select(users.c.id == 1).execute().first()
|
||||
>>> r['name']
|
||||
u'admin'
|
||||
'admin'
|
||||
|
||||
You can also pass strings of SQL statements to the
|
||||
:meth:`~sqlalchemy.engine.base.Connection.execute` method:
|
||||
|
||||
>>> engine.execute('select * from users where id = :1', [1]).first()
|
||||
(1, u'admin', u'admin@localhost')
|
||||
(1, 'admin', 'admin@localhost')
|
||||
|
||||
For more information about SQLAlchemy, head over to the
|
||||
`website <https://www.sqlalchemy.org/>`_.
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
.. _sqlite3:
|
||||
|
||||
Using SQLite 3 with Flask
|
||||
=========================
|
||||
|
||||
In Flask you can easily implement the opening of database connections on
|
||||
demand and closing them when the context dies (usually at the end of the
|
||||
request).
|
||||
You can implement a few functions to work with a SQLite connection during a
|
||||
request context. The connection is created the first time it's accessed,
|
||||
reused on subsequent access, until it is closed when the request context ends.
|
||||
|
||||
Here is a simple example of how you can use SQLite 3 with Flask::
|
||||
|
||||
|
|
@ -32,10 +30,6 @@ or create an application context itself. At that point the ``get_db``
|
|||
function can be used to get the current database connection. Whenever the
|
||||
context is destroyed the database connection will be terminated.
|
||||
|
||||
Note: if you use Flask 0.9 or older you need to use
|
||||
``flask._app_ctx_stack.top`` instead of ``g`` as the :data:`flask.g`
|
||||
object was bound to the request and not application context.
|
||||
|
||||
Example::
|
||||
|
||||
@app.route('/')
|
||||
|
|
@ -62,7 +56,6 @@ the application context by hand::
|
|||
with app.app_context():
|
||||
# now you can use get_db()
|
||||
|
||||
.. _easy-querying:
|
||||
|
||||
Easy Querying
|
||||
-------------
|
||||
|
|
@ -116,16 +109,16 @@ raw cursor and connection objects.
|
|||
Here is how you can use it::
|
||||
|
||||
for user in query_db('select * from users'):
|
||||
print user['username'], 'has the id', user['user_id']
|
||||
print(user['username'], 'has the id', user['user_id'])
|
||||
|
||||
Or if you just want a single result::
|
||||
|
||||
user = query_db('select * from users where username = ?',
|
||||
[the_username], one=True)
|
||||
if user is None:
|
||||
print 'No such user'
|
||||
print('No such user')
|
||||
else:
|
||||
print the_username, 'has the id', user['user_id']
|
||||
print(the_username, 'has the id', user['user_id'])
|
||||
|
||||
To pass variable parts to the SQL statement, use a question mark in the
|
||||
statement and pass in the arguments as a list. Never directly add them to
|
||||
|
|
|
|||
|
|
@ -8,6 +8,21 @@ roundtrip to the filesystem?
|
|||
|
||||
The answer is by using generators and direct responses.
|
||||
|
||||
HTTP Response Behavior
|
||||
----------------------
|
||||
|
||||
**Headers cannot be changed after the streaming response starts.**
|
||||
|
||||
When using streaming, it's important to be aware of the order than an HTTP
|
||||
response is sent. All headers must be sent first, then the body. More headers
|
||||
cannot be sent after the body has begun. Therefore, you must make sure all
|
||||
headers are set before starting the response, outside the generator.
|
||||
|
||||
In particular, if the generator will access ``session``, be sure to do so in the
|
||||
view as well so that the ``Vary: cookie`` header will be set. Do not modify the
|
||||
session in the generator, as the ``Set-Cookie`` header will already be sent.
|
||||
|
||||
|
||||
Basic Usage
|
||||
-----------
|
||||
|
||||
|
|
@ -15,14 +30,12 @@ This is a basic view function that generates a lot of CSV data on the fly.
|
|||
The trick is to have an inner function that uses a generator to generate
|
||||
data and to then invoke that function and pass it to a response object::
|
||||
|
||||
from flask import Response
|
||||
|
||||
@app.route('/large.csv')
|
||||
def generate_large_csv():
|
||||
def generate():
|
||||
for row in iter_all_rows():
|
||||
yield ','.join(row) + '\n'
|
||||
return Response(generate(), mimetype='text/csv')
|
||||
yield f"{','.join(row)}\n"
|
||||
return generate(), {"Content-Type": "text/csv"}
|
||||
|
||||
Each ``yield`` expression is directly sent to the browser. Note though
|
||||
that some WSGI middlewares might break streaming, so be careful there in
|
||||
|
|
@ -31,54 +44,57 @@ debug environments with profilers and other things you might have enabled.
|
|||
Streaming from Templates
|
||||
------------------------
|
||||
|
||||
The Jinja2 template engine also supports rendering templates piece by
|
||||
piece. This functionality is not directly exposed by Flask because it is
|
||||
quite uncommon, but you can easily do it yourself::
|
||||
The Jinja template engine supports rendering a template piece by
|
||||
piece, returning an iterator of strings. Flask provides the
|
||||
:func:`~flask.stream_template` and :func:`~flask.stream_template_string`
|
||||
functions to make this easier to use.
|
||||
|
||||
from flask import Response
|
||||
.. code-block:: python
|
||||
|
||||
def stream_template(template_name, **context):
|
||||
app.update_template_context(context)
|
||||
t = app.jinja_env.get_template(template_name)
|
||||
rv = t.stream(context)
|
||||
rv.enable_buffering(5)
|
||||
return rv
|
||||
from flask import stream_template
|
||||
|
||||
@app.route('/my-large-page.html')
|
||||
def render_large_template():
|
||||
rows = iter_all_rows()
|
||||
return Response(stream_template('the_template.html', rows=rows))
|
||||
@app.get("/timeline")
|
||||
def timeline():
|
||||
return stream_template("timeline.html")
|
||||
|
||||
The parts yielded by the render stream tend to match statement blocks in
|
||||
the template.
|
||||
|
||||
The trick here is to get the template object from the Jinja2 environment
|
||||
on the application and to call :meth:`~jinja2.Template.stream` instead of
|
||||
:meth:`~jinja2.Template.render` which returns a stream object instead of a
|
||||
string. Since we're bypassing the Flask template render functions and
|
||||
using the template object itself we have to make sure to update the render
|
||||
context ourselves by calling :meth:`~flask.Flask.update_template_context`.
|
||||
The template is then evaluated as the stream is iterated over. Since each
|
||||
time you do a yield the server will flush the content to the client you
|
||||
might want to buffer up a few items in the template which you can do with
|
||||
``rv.enable_buffering(size)``. ``5`` is a sane default.
|
||||
|
||||
Streaming with Context
|
||||
----------------------
|
||||
|
||||
.. versionadded:: 0.9
|
||||
The :data:`.request` proxy will not be active while the generator is
|
||||
running, because the app has already returned control to the WSGI server at that
|
||||
point. If you try to access ``request``, you'll get a ``RuntimeError``.
|
||||
|
||||
Note that when you stream data, the request context is already gone the
|
||||
moment the function executes. Flask 0.9 provides you with a helper that
|
||||
can keep the request context around during the execution of the
|
||||
generator::
|
||||
If your generator function relies on data in ``request``, use the
|
||||
:func:`.stream_with_context` wrapper. This will keep the request context active
|
||||
during the generator.
|
||||
|
||||
from flask import stream_with_context, request, Response
|
||||
.. code-block:: python
|
||||
|
||||
from flask import stream_with_context, request
|
||||
from markupsafe import escape
|
||||
|
||||
@app.route('/stream')
|
||||
def streamed_response():
|
||||
def generate():
|
||||
yield 'Hello '
|
||||
yield request.args['name']
|
||||
yield '!'
|
||||
return Response(stream_with_context(generate()))
|
||||
yield '<p>Hello '
|
||||
yield escape(request.args['name'])
|
||||
yield '!</p>'
|
||||
return stream_with_context(generate())
|
||||
|
||||
Without the :func:`~flask.stream_with_context` function you would get a
|
||||
:class:`RuntimeError` at that point.
|
||||
It can also be used as a decorator.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@stream_with_context
|
||||
def generate():
|
||||
...
|
||||
|
||||
return generate()
|
||||
|
||||
The :func:`~flask.stream_template` and
|
||||
:func:`~flask.stream_template_string` functions automatically
|
||||
use :func:`~flask.stream_with_context` if a request is active.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
.. _template-inheritance:
|
||||
|
||||
Template Inheritance
|
||||
====================
|
||||
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ Caching Decorator
|
|||
Imagine you have a view function that does an expensive calculation and
|
||||
because of that you would like to cache the generated results for a
|
||||
certain amount of time. A decorator would be nice for that. We're
|
||||
assuming you have set up a cache like mentioned in :ref:`caching-pattern`.
|
||||
assuming you have set up a cache like mentioned in :doc:`caching`.
|
||||
|
||||
Here is an example cache function. It generates the cache key from a
|
||||
specific prefix (actually a format string) and the current path of the
|
||||
|
|
@ -82,11 +82,11 @@ Here the code::
|
|||
from functools import wraps
|
||||
from flask import request
|
||||
|
||||
def cached(timeout=5 * 60, key='view/%s'):
|
||||
def cached(timeout=5 * 60, key='view/{}'):
|
||||
def decorator(f):
|
||||
@wraps(f)
|
||||
def decorated_function(*args, **kwargs):
|
||||
cache_key = key % request.path
|
||||
cache_key = key.format(request.path)
|
||||
rv = cache.get(cache_key)
|
||||
if rv is not None:
|
||||
return rv
|
||||
|
|
@ -96,8 +96,8 @@ Here the code::
|
|||
return decorated_function
|
||||
return decorator
|
||||
|
||||
Notice that this assumes an instantiated `cache` object is available, see
|
||||
:ref:`caching-pattern` for more information.
|
||||
Notice that this assumes an instantiated ``cache`` object is available, see
|
||||
:doc:`caching`.
|
||||
|
||||
|
||||
Templating Decorator
|
||||
|
|
@ -142,8 +142,7 @@ Here is the code for that decorator::
|
|||
def decorated_function(*args, **kwargs):
|
||||
template_name = template
|
||||
if template_name is None:
|
||||
template_name = request.endpoint \
|
||||
.replace('.', '/') + '.html'
|
||||
template_name = f"{request.endpoint.replace('.', '/')}.html"
|
||||
ctx = f(*args, **kwargs)
|
||||
if ctx is None:
|
||||
ctx = {}
|
||||
|
|
|
|||