Compare commits
1096 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 | ||
|
|
6f79cb8a23 | ||
|
|
b6a8ccd2cf | ||
|
|
48ee204dd5 | ||
|
|
beae1249f7 | ||
|
|
e06dad62f6 | ||
|
|
1be65b1b69 | ||
|
|
7c5f17a55e | ||
|
|
e37e87140e | ||
|
|
bc094dbe6a | ||
|
|
694eb84f41 | ||
|
|
17c46b2ddd | ||
|
|
9a134da31b | ||
|
|
751d85f3de | ||
|
|
fdac8a5404 | ||
|
|
aab064fee1 | ||
|
|
d03df1fff3 | ||
|
|
e103747c54 | ||
|
|
660994efc7 | ||
|
|
0fb5c2f034 | ||
|
|
93a7f08bbc | ||
|
|
9504de9dd0 | ||
|
|
9e00becf3a | ||
|
|
79b3cecc3d | ||
|
|
a65683a65c | ||
|
|
2952e6a323 | ||
|
|
c627b5e773 | ||
|
|
edac7e3a56 | ||
|
|
9486b6cf57 | ||
|
|
c8ddb948f6 | ||
|
|
04c6a85518 | ||
|
|
47e4bd5059 | ||
|
|
6b0c8cdac1 | ||
|
|
ea66c68553 | ||
|
|
7860c1de87 | ||
|
|
7620cb70db | ||
|
|
15a3e82823 | ||
|
|
48f2afbf90 | ||
|
|
f8cdc78ce1 | ||
|
|
b7501776a1 | ||
|
|
218534a9f2 | ||
|
|
2bd7aed1a4 | ||
|
|
e21e003f62 | ||
|
|
ea93a52d7d | ||
|
|
df806c8035 | ||
|
|
1b552d0b01 | ||
|
|
e609dddd60 | ||
|
|
3c36d043e5 | ||
|
|
0ae0f5957f | ||
|
|
44bc286c03 | ||
|
|
fb82e3cba6 | ||
|
|
1cf43e08e3 | ||
|
|
5684f48f70 | ||
|
|
f4a2c35691 | ||
|
|
aa1d34dc51 | ||
|
|
24aab7a08d | ||
|
|
6f852d0d22 | ||
|
|
4a37b627ae | ||
|
|
d01d26e521 | ||
|
|
c58ff55e19 | ||
|
|
048709c8e7 | ||
|
|
d7199c8785 | ||
|
|
e4373eba1c | ||
|
|
6d32d34233 | ||
|
|
f6e4e8ab9d | ||
|
|
b5bdcb4602 | ||
|
|
53eef278ef | ||
|
|
a430c43736 | ||
|
|
d4b540eb7d | ||
|
|
13c0eef54c | ||
|
|
7e40882e02 | ||
|
|
afc13b9390 | ||
|
|
0826be48ed | ||
|
|
f2b1dc45bd | ||
|
|
e248e09399 | ||
|
|
625595cb1a | ||
|
|
4550beb695 | ||
|
|
f5f51cd09c | ||
|
|
c0f583fdc1 | ||
|
|
3e507a70ca | ||
|
|
7a3b8bbb89 | ||
|
|
bb3217b350 | ||
|
|
7a73171edc | ||
|
|
8e62bf6b17 | ||
|
|
f9ccca97e4 | ||
|
|
28507154fa | ||
|
|
9fa8b0f7ad | ||
|
|
7b3bb7b13f | ||
|
|
c6d8beeb9e | ||
|
|
72078148c0 | ||
|
|
c9796f85c7 | ||
|
|
ae53c32048 | ||
|
|
e145f9c6ec | ||
|
|
d0f19f7905 | ||
|
|
01e804a6e1 | ||
|
|
6fe9235f86 | ||
|
|
ba6db2e307 | ||
|
|
aac67289e5 | ||
|
|
1b10e085d8 | ||
|
|
cd0086bc4b | ||
|
|
5d0104f67e | ||
|
|
6f5870a791 | ||
|
|
7161776824 | ||
|
|
83f7efa047 | ||
|
|
9039534eee | ||
|
|
636f195bb5 | ||
|
|
1ce0f774c9 | ||
|
|
4240ace597 | ||
|
|
d575de5159 | ||
|
|
22d82e70b3 | ||
|
|
19f458f9cd | ||
|
|
905e5c23e8 | ||
|
|
80a4e62096 | ||
|
|
2846abaefe |
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
|
||||
|
|
@ -9,5 +9,5 @@ end_of_line = lf
|
|||
charset = utf-8
|
||||
max_line_length = 88
|
||||
|
||||
[*.{yml,yaml,json,js,css,html}]
|
||||
[*.{css,html,js,json,jsx,scss,ts,tsx,yaml,yml}]
|
||||
indent_size = 2
|
||||
|
|
|
|||
2
.github/ISSUE_TEMPLATE/bug-report.md
vendored
|
|
@ -5,7 +5,7 @@ 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
|
||||
Pallets Discord or Stack Overflow for questions about your own code.
|
||||
GitHub Discussions or the Pallets Discord for questions about your own code.
|
||||
|
||||
Replace this comment with a clear outline of what the bug is.
|
||||
-->
|
||||
|
|
|
|||
14
.github/ISSUE_TEMPLATE/config.yml
vendored
|
|
@ -1,11 +1,11 @@
|
|||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Security issue
|
||||
url: security@palletsprojects.com
|
||||
about: Do not report security issues publicly. Email our security contact.
|
||||
- name: Questions
|
||||
url: https://stackoverflow.com/questions/tagged/flask?tab=Frequent
|
||||
about: Search for and ask questions about your code on Stack Overflow.
|
||||
- name: Questions and discussions
|
||||
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: Discuss questions about your code on our Discord chat.
|
||||
about: Ask questions about your own code on our Discord chat.
|
||||
|
|
|
|||
19
.github/SECURITY.md
vendored
|
|
@ -1,19 +0,0 @@
|
|||
# Security Policy
|
||||
|
||||
If you believe you have identified a security issue with a Pallets
|
||||
project, **do not open a public issue**. To responsibly report a
|
||||
security issue, please email security@palletsprojects.com. A security
|
||||
team member will contact you acknowledging the report and how to
|
||||
continue.
|
||||
|
||||
Be sure to include as much detail as necessary in your report. As with
|
||||
reporting normal issues, a minimal reproducible example will help the
|
||||
maintainers address the issue faster. If you are able, you may also
|
||||
include a fix for the issue generated with `git format-patch`.
|
||||
|
||||
The current and previous release will receive security patches, with
|
||||
older versions evaluated based on usage information and severity.
|
||||
|
||||
After fixing an issue, we will make a security release along with an
|
||||
announcement on our blog. We may obtain a CVE id as well. You may
|
||||
include a name and link if you would like to be credited for the report.
|
||||
9
.github/dependabot.yml
vendored
|
|
@ -1,9 +0,0 @@
|
|||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "monthly"
|
||||
day: "monday"
|
||||
time: "16:00"
|
||||
timezone: "UTC"
|
||||
25
.github/pull_request_template.md
vendored
|
|
@ -1,6 +1,7 @@
|
|||
<!--
|
||||
Before opening a PR, open a ticket describing the issue or feature the
|
||||
PR will address. Follow the steps in CONTRIBUTING.rst.
|
||||
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.
|
||||
|
|
@ -9,22 +10,16 @@ addresses the linked ticket.
|
|||
<!--
|
||||
Link to relevant issues or previous PRs, one per line. Use "fixes" to
|
||||
automatically close an issue.
|
||||
-->
|
||||
|
||||
- fixes #<issue number>
|
||||
fixes #<issue number>
|
||||
-->
|
||||
|
||||
<!--
|
||||
Ensure each step in CONTRIBUTING.rst is complete by adding an "x" to
|
||||
each box below.
|
||||
Ensure each step in CONTRIBUTING.rst is complete, especially the following:
|
||||
|
||||
If only docs were changed, these aren't relevant and can be removed.
|
||||
- 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.
|
||||
-->
|
||||
|
||||
Checklist:
|
||||
|
||||
- [ ] 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.
|
||||
- [ ] Run `pre-commit` hooks and fix any issues.
|
||||
- [ ] Run `pytest` and `tox`, no tests failed.
|
||||
|
|
|
|||
19
.github/workflows/lock.yaml
vendored
|
|
@ -1,15 +1,26 @@
|
|||
name: 'Lock threads'
|
||||
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@v3
|
||||
- uses: dessant/lock-threads@7266a7ce5c1df01b1c6db85bf8cd86c737dadbe7 # v6.0.0
|
||||
with:
|
||||
github-token: ${{ github.token }}
|
||||
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/"
|
||||
91
.github/workflows/tests.yaml
vendored
|
|
@ -1,52 +1,63 @@
|
|||
name: Tests
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- '*.x'
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- '*.md'
|
||||
- '*.rst'
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- '*.x'
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- '*.md'
|
||||
- '*.rst'
|
||||
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 }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
name: ${{ matrix.name || matrix.python }}
|
||||
runs-on: ${{ matrix.os || 'ubuntu-latest' }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- {name: Linux, python: '3.10', os: ubuntu-latest, tox: py310}
|
||||
- {name: Windows, python: '3.10', os: windows-latest, tox: py310}
|
||||
- {name: Mac, python: '3.10', os: macos-latest, tox: py310}
|
||||
- {name: '3.11-dev', python: '3.11-dev', os: ubuntu-latest, tox: py311}
|
||||
- {name: '3.9', python: '3.9', os: ubuntu-latest, tox: py39}
|
||||
- {name: '3.8', python: '3.8', os: ubuntu-latest, tox: py38}
|
||||
- {name: '3.7', python: '3.7', os: ubuntu-latest, tox: py37}
|
||||
- {name: '3.6', python: '3.6', os: ubuntu-latest, tox: py36}
|
||||
- {name: 'PyPy', python: 'pypy-3.7', os: ubuntu-latest, tox: pypy37}
|
||||
- {name: 'Pallets Minimum Versions', python: '3.10', os: ubuntu-latest, tox: py-min}
|
||||
- {name: 'Pallets Development Versions', python: '3.7', os: ubuntu-latest, tox: py-dev}
|
||||
- {name: Typing, python: '3.10', os: ubuntu-latest, tox: typing}
|
||||
- {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@v2
|
||||
- uses: actions/setup-python@v2
|
||||
- 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 }}
|
||||
cache: 'pip'
|
||||
cache-dependency-path: 'requirements/*.txt'
|
||||
- name: update pip
|
||||
run: |
|
||||
pip install -U wheel
|
||||
pip install -U setuptools
|
||||
python -m pip install -U pip
|
||||
- run: pip install tox
|
||||
- run: tox -e ${{ matrix.tox }}
|
||||
- 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
|
||||
30
.gitignore
vendored
|
|
@ -1,26 +1,8 @@
|
|||
.DS_Store
|
||||
.env
|
||||
.flaskenv
|
||||
*.pyc
|
||||
*.pyo
|
||||
env/
|
||||
venv/
|
||||
.venv/
|
||||
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,38 +1,23 @@
|
|||
ci:
|
||||
autoupdate_branch: "2.0.x"
|
||||
autoupdate_schedule: monthly
|
||||
repos:
|
||||
- repo: https://github.com/asottile/pyupgrade
|
||||
rev: v2.31.0
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: 5e2fb545eba1ea9dc051f6f962d52fe8f76a9794 # frozen: v0.15.13
|
||||
hooks:
|
||||
- id: pyupgrade
|
||||
args: ["--py36-plus"]
|
||||
- repo: https://github.com/asottile/reorder_python_imports
|
||||
rev: v2.7.1
|
||||
- id: ruff-check
|
||||
- id: ruff-format
|
||||
- repo: https://github.com/astral-sh/uv-pre-commit
|
||||
rev: fa60a193803535a9e2accdb3ca4b1b584b1150cb # frozen: 0.11.15
|
||||
hooks:
|
||||
- id: reorder-python-imports
|
||||
name: Reorder Python imports (src, tests)
|
||||
files: "^(?!examples/)"
|
||||
args: ["--application-directories", "src"]
|
||||
additional_dependencies: ["setuptools>60.9"]
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 22.1.0
|
||||
- id: uv-lock
|
||||
- repo: https://github.com/codespell-project/codespell
|
||||
rev: 2ccb47ff45ad361a21071a7eedda4c37e6ae8c5a # frozen: v2.4.2
|
||||
hooks:
|
||||
- id: black
|
||||
- repo: https://github.com/PyCQA/flake8
|
||||
rev: 4.0.1
|
||||
hooks:
|
||||
- id: flake8
|
||||
additional_dependencies:
|
||||
- flake8-bugbear
|
||||
- flake8-implicit-str-concat
|
||||
- repo: https://github.com/peterdemin/pip-compile-multi
|
||||
rev: v2.4.3
|
||||
hooks:
|
||||
- id: pip-compile-multi-verify
|
||||
- id: codespell
|
||||
args: ['--write-changes']
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.1.0
|
||||
rev: 3e8a8703264a2f4a69428a0aa4dcb512790b2c8c # frozen: v6.0.0
|
||||
hooks:
|
||||
- id: check-merge-conflict
|
||||
- id: debug-statements
|
||||
- id: fix-byte-order-marker
|
||||
- id: trailing-whitespace
|
||||
- id: end-of-file-fixer
|
||||
|
|
|
|||
|
|
@ -1,13 +1,10 @@
|
|||
version: 2
|
||||
build:
|
||||
os: ubuntu-20.04
|
||||
os: ubuntu-24.04
|
||||
tools:
|
||||
python: "3.10"
|
||||
python:
|
||||
install:
|
||||
- requirements: requirements/docs.txt
|
||||
- method: pip
|
||||
path: .
|
||||
sphinx:
|
||||
builder: dirhtml
|
||||
fail_on_warning: true
|
||||
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
|
||||
|
|
|
|||
904
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
|
||||
229
CONTRIBUTING.rst
|
|
@ -1,229 +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. The issue tracker is a tool
|
||||
to address bugs and feature requests in Flask itself. Use one of the
|
||||
following resources for questions about using Flask or issues with your
|
||||
own code:
|
||||
|
||||
- The ``#questions`` channel on our Discord chat:
|
||||
https://discord.gg/pallets
|
||||
- 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.}``
|
||||
- Ask on our `GitHub Discussions`_.
|
||||
|
||||
.. _Stack Overflow: https://stackoverflow.com/questions/tagged/flask?tab=Frequent
|
||||
.. _GitHub Discussions: https://github.com/pallets/flask/discussions
|
||||
|
||||
|
||||
Reporting issues
|
||||
----------------
|
||||
|
||||
Include the following information in your post:
|
||||
|
||||
- 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 and Flask versions. If possible, check if this
|
||||
issue is already fixed in the latest releases or the latest code in
|
||||
the repository.
|
||||
|
||||
.. _minimal reproducible example: https://stackoverflow.com/help/minimal-reproducible-example
|
||||
|
||||
|
||||
Submitting patches
|
||||
------------------
|
||||
|
||||
If there is not an open issue for what you want to submit, prefer
|
||||
opening one for discussion before working on a PR. You can work on any
|
||||
issue that doesn't have an open PR linked to it or a maintainer assigned
|
||||
to it. These show up in the sidebar. No need to ask if you can work on
|
||||
an issue that interests you.
|
||||
|
||||
Include the following in your patch:
|
||||
|
||||
- Use `Black`_ to format your code. This and other tools will run
|
||||
automatically if you install `pre-commit`_ using the instructions
|
||||
below.
|
||||
- Include tests if your patch adds or changes code. Make sure the test
|
||||
fails without your patch.
|
||||
- Update any relevant docs pages and docstrings. Docs pages and
|
||||
docstrings should be wrapped at 72 characters.
|
||||
- Add an entry in ``CHANGES.rst``. Use the same style as other
|
||||
entries. Also include ``.. versionchanged::`` inline changelogs in
|
||||
relevant docstrings.
|
||||
|
||||
.. _Black: https://black.readthedocs.io
|
||||
.. _pre-commit: https://pre-commit.com
|
||||
|
||||
|
||||
First time setup
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
- Download and install the `latest version of git`_.
|
||||
- Configure git with your `username`_ and `email`_.
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ 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`_ the main repository locally.
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ git clone https://github.com/pallets/flask
|
||||
$ cd flask
|
||||
|
||||
- Add your fork as a remote to push your work to. Replace
|
||||
``{username}`` with your username. This names the remote "fork", the
|
||||
default Pallets remote is "origin".
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ git remote add fork https://github.com/{username}/flask
|
||||
|
||||
- Create a virtualenv.
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. group-tab:: Linux/macOS
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ python3 -m venv env
|
||||
$ . env/bin/activate
|
||||
|
||||
.. group-tab:: Windows
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
> py -3 -m venv env
|
||||
> env\Scripts\activate
|
||||
|
||||
- Upgrade pip and setuptools.
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ python -m pip install --upgrade pip setuptools
|
||||
|
||||
- Install the development dependencies, then install Flask in editable
|
||||
mode.
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ pip install -r requirements/dev.txt && pip install -e .
|
||||
|
||||
- Install the pre-commit hooks.
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ pre-commit install
|
||||
|
||||
.. _latest version of git: https://git-scm.com/downloads
|
||||
.. _username: https://docs.github.com/en/github/using-git/setting-your-username-in-git
|
||||
.. _email: https://docs.github.com/en/github/setting-up-and-managing-your-github-user-account/setting-your-commit-email-address
|
||||
.. _GitHub account: https://github.com/join
|
||||
.. _Fork: https://github.com/pallets/flask/fork
|
||||
.. _Clone: https://docs.github.com/en/github/getting-started-with-github/fork-a-repo#step-2-create-a-local-clone-of-your-fork
|
||||
|
||||
|
||||
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.
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ git fetch origin
|
||||
$ git checkout -b your-branch-name origin/2.0.x
|
||||
|
||||
If you're submitting a feature addition or change, branch off of the
|
||||
"main" branch.
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ git fetch origin
|
||||
$ git checkout -b your-branch-name origin/main
|
||||
|
||||
- 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 as described below.
|
||||
- Push your commits to your fork on GitHub and
|
||||
`create a pull request`_. Link to the issue being addressed with
|
||||
``fixes #123`` in the pull request.
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ git push --set-upstream fork your-branch-name
|
||||
|
||||
.. _committing as you go: https://dont-be-afraid-to-commit.readthedocs.io/en/latest/git/commandlinegit.html#commit-your-changes
|
||||
.. _create a pull request: https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request
|
||||
|
||||
|
||||
Running the tests
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Run the basic test suite with pytest.
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ pytest
|
||||
|
||||
This runs the tests for the current environment, which is usually
|
||||
sufficient. CI will run the full suite when you submit your pull
|
||||
request. You can run the full test suite with tox if you don't want to
|
||||
wait.
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ 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.
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ pip install coverage
|
||||
$ coverage run -m pytest
|
||||
$ coverage html
|
||||
|
||||
Open ``htmlcov/index.html`` in your browser to explore the report.
|
||||
|
||||
Read more about `coverage <https://coverage.readthedocs.io>`__.
|
||||
|
||||
|
||||
Building the docs
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Build the docs in the ``docs`` directory using Sphinx.
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ cd docs
|
||||
$ make html
|
||||
|
||||
Open ``_build/html/index.html`` in your browser to view the docs.
|
||||
|
||||
Read more about `Sphinx <https://www.sphinx-doc.org/en/stable/>`__.
|
||||
11
MANIFEST.in
|
|
@ -1,11 +0,0 @@
|
|||
include CHANGES.rst
|
||||
include CONTRIBUTING.rst
|
||||
include tox.ini
|
||||
include requirements/*.txt
|
||||
graft artwork
|
||||
graft docs
|
||||
prune docs/_build
|
||||
graft examples
|
||||
graft tests
|
||||
include src/flask/py.typed
|
||||
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/
|
||||
82
README.rst
|
|
@ -1,82 +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.
|
||||
|
||||
.. _WSGI: https://wsgi.readthedocs.io/
|
||||
.. _Werkzeug: https://werkzeug.palletsprojects.com/
|
||||
.. _Jinja: https://jinja.palletsprojects.com/
|
||||
|
||||
|
||||
Installing
|
||||
----------
|
||||
|
||||
Install and update using `pip`_:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ pip install -U Flask
|
||||
|
||||
.. _pip: https://pip.pypa.io/en/stable/getting-started/
|
||||
|
||||
|
||||
A Simple Example
|
||||
----------------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# save this as app.py
|
||||
from flask import Flask
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
@app.route("/")
|
||||
def hello():
|
||||
return "Hello, World!"
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ flask run
|
||||
* 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/main/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://palletsprojects.com/donate
|
||||
|
||||
|
||||
Links
|
||||
-----
|
||||
|
||||
- Documentation: https://flask.palletsprojects.com/
|
||||
- Changes: https://flask.palletsprojects.com/changes/
|
||||
- PyPI Releases: https://pypi.org/project/Flask/
|
||||
- Source Code: https://github.com/pallets/flask/
|
||||
- Issue Tracker: https://github.com/pallets/flask/issues/
|
||||
- Website: https://palletsprojects.com/p/flask/
|
||||
- Twitter: https://twitter.com/PalletsTeam
|
||||
- Chat: https://discord.gg/pallets
|
||||
|
|
@ -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.
|
||||
|
||||
2. 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,46 +0,0 @@
|
|||
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.
|
||||
309
docs/api.rst
|
|
@ -3,7 +3,7 @@ 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.
|
||||
|
||||
|
|
@ -31,17 +31,15 @@ Incoming Request Data
|
|||
:inherited-members:
|
||||
:exclude-members: json_module
|
||||
|
||||
.. attribute:: request
|
||||
.. data:: 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.
|
||||
A proxy to the request data for the current request, an instance of
|
||||
:class:`.Request`.
|
||||
|
||||
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 request object is an instance of a :class:`~flask.Request`.
|
||||
This is a proxy. See :ref:`context-visibility` for more information.
|
||||
|
||||
|
||||
Response Objects
|
||||
|
|
@ -62,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
|
||||
|
|
@ -125,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
|
||||
|
|
@ -156,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. For
|
||||
example, a ``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 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.
|
||||
|
|
@ -186,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
|
||||
|
||||
|
|
@ -218,12 +208,6 @@ Useful Functions and Classes
|
|||
|
||||
.. autofunction:: send_from_directory
|
||||
|
||||
.. autofunction:: safe_join
|
||||
|
||||
.. autofunction:: escape
|
||||
|
||||
.. autoclass:: Markup
|
||||
:members: escape, unescape, striptags
|
||||
|
||||
Message Flashing
|
||||
----------------
|
||||
|
|
@ -238,26 +222,20 @@ JSON Support
|
|||
|
||||
.. module:: flask.json
|
||||
|
||||
Flask uses the built-in :mod:`json` module for handling JSON. It will
|
||||
use the current blueprint's or application's JSON encoder and decoder
|
||||
for easier customization. By default it handles some extra data types:
|
||||
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.
|
||||
|
||||
- :class:`datetime.datetime` and :class:`datetime.date` are serialized
|
||||
to :rfc:`822` strings. This is the same as the HTTP date format.
|
||||
- :class:`uuid.UUID` is serialized to a string.
|
||||
- :class:`dataclasses.dataclass` is passed to
|
||||
:func:`dataclasses.asdict`.
|
||||
- :class:`~markupsafe.Markup` (or any object with a ``__html__``
|
||||
method) will call the ``__html__`` method to get a string.
|
||||
|
||||
Jinja's ``|tojson`` filter is configured to use Flask's :func:`dumps`
|
||||
function. The filter marks the output with ``|safe`` automatically. Use
|
||||
the filter to render data inside ``<script>`` tags.
|
||||
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>
|
||||
const names = {{ names|tosjon }};
|
||||
const names = {{ names|tojson }};
|
||||
renderChart(names, {{ axis_data|tojson }});
|
||||
</script>
|
||||
|
||||
|
|
@ -271,11 +249,13 @@ the filter to render data inside ``<script>`` tags.
|
|||
|
||||
.. 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
|
||||
|
||||
|
|
@ -289,6 +269,10 @@ Template Rendering
|
|||
|
||||
.. autofunction:: render_template_string
|
||||
|
||||
.. autofunction:: stream_template
|
||||
|
||||
.. autofunction:: stream_template_string
|
||||
|
||||
.. autofunction:: get_template_attribute
|
||||
|
||||
Configuration
|
||||
|
|
@ -306,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:
|
||||
|
|
@ -368,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`).
|
||||
|
||||
|
|
@ -413,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`.
|
||||
|
||||
|
|
@ -433,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
|
||||
|
|
@ -470,8 +421,8 @@ The following signals exist in Flask:
|
|||
|
||||
.. 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.
|
||||
|
||||
|
|
@ -489,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.
|
||||
|
||||
|
|
@ -507,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::
|
||||
|
|
@ -536,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`.
|
||||
|
||||
|
|
@ -560,23 +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
|
||||
-----------------
|
||||
|
|
@ -604,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>``.
|
||||
|
||||
|
|
@ -638,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
|
||||
|
|
@ -647,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})
|
||||
|
|
@ -663,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. ::
|
||||
|
|
@ -674,33 +607,33 @@ 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.
|
||||
=============== ==========================================================
|
||||
|
||||
|
||||
|
|
@ -712,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,74 +1,63 @@
|
|||
.. currentmodule:: flask
|
||||
The App and Request Context
|
||||
===========================
|
||||
|
||||
The Application 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 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.
|
||||
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.
|
||||
|
||||
This is similar to :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__)
|
||||
|
|
@ -78,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.
|
||||
|
|
|
|||
|
|
@ -7,8 +7,7 @@ Using ``async`` and ``await``
|
|||
|
||||
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]``). It requires Python 3.7+
|
||||
where ``contextvars.ContextVar`` is available. This allows views to be
|
||||
``async`` extra (``pip install flask[async]``). This allows views to be
|
||||
defined with ``async def`` and use ``await``.
|
||||
|
||||
.. code-block:: python
|
||||
|
|
@ -24,12 +23,6 @@ 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.
|
||||
|
||||
.. admonition:: Using ``async`` on Windows on Python 3.8
|
||||
|
||||
Python 3.8 has a bug related to asyncio on Windows. If you encounter
|
||||
something like ``ValueError: set_wakeup_fd only works in main thread``,
|
||||
please upgrade to Python 3.9.
|
||||
|
||||
|
||||
Performance
|
||||
-----------
|
||||
|
|
@ -65,8 +58,8 @@ 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 :ref:`asgi`. This works as the adapter creates an
|
||||
event loop that runs continually.
|
||||
as described in :doc:`deploying/asgi`. This works as the adapter creates
|
||||
an event loop that runs continually.
|
||||
|
||||
|
||||
When to use Quart instead
|
||||
|
|
@ -79,15 +72,15 @@ 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 run Flask with Gevent or Eventlet
|
||||
to get many of the benefits of async request handling. These libraries
|
||||
patch low-level Python functions to accomplish this, whereas ``async``/
|
||||
``await`` and ASGI use standard, modern Python capabilities. Deciding
|
||||
whether you should use Flask, Quart, or something else is ultimately up
|
||||
to understanding the specific needs of your project.
|
||||
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://gitlab.com/pgjones/quart
|
||||
.. _ASGI: https://asgi.readthedocs.io/en/latest/
|
||||
.. _Quart: https://quart.palletsprojects.com
|
||||
.. _ASGI: https://asgi.readthedocs.io
|
||||
|
||||
|
||||
Extensions
|
||||
|
|
@ -121,6 +114,6 @@ 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.
|
||||
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,100 +0,0 @@
|
|||
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 :doc:`/api` docs are full of available overrides, hook points, and
|
||||
:doc:`/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 :doc:`/patterns/appfactories`.
|
||||
See :doc:`/patterns/subclassing` for an example.
|
||||
|
||||
Wrap with middleware.
|
||||
---------------------
|
||||
|
||||
The :doc:`/patterns/appdispatch` pattern 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.
|
||||
|
|
@ -140,6 +140,19 @@ name, and child URLs will be prefixed with the parent's URL prefix.
|
|||
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.
|
||||
|
|
|
|||
390
docs/cli.rst
|
|
@ -15,40 +15,10 @@ 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.
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. group-tab:: Bash
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ export FLASK_APP=hello
|
||||
$ flask run
|
||||
|
||||
.. group-tab:: Fish
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ set -x FLASK_APP hello
|
||||
$ flask run
|
||||
|
||||
.. group-tab:: CMD
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
> set FLASK_APP=hello
|
||||
> flask run
|
||||
|
||||
.. group-tab:: Powershell
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
> $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)
|
||||
|
|
@ -56,32 +26,32 @@ application, most use cases should be simple. Here are the typical values:
|
|||
automatically detecting an app (``app`` or ``application``) or
|
||||
factory (``create_app`` or ``make_app``).
|
||||
|
||||
``FLASK_APP=hello``
|
||||
``--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.
|
||||
|
||||
|
|
@ -101,7 +71,7 @@ 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)
|
||||
|
||||
|
|
@ -116,6 +86,50 @@ 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
|
||||
------------
|
||||
|
||||
|
|
@ -124,166 +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.
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. group-tab:: Bash
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ export 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
|
||||
|
||||
.. group-tab:: Fish
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ set -x 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
|
||||
|
||||
.. group-tab:: CMD
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
> set 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
|
||||
|
||||
.. group-tab:: Powershell
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
> $env: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.
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. group-tab:: Bash
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ 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
|
||||
|
||||
.. group-tab:: Fish
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ flask run --extra-files file1:dirA/file2:dirB/
|
||||
# or
|
||||
$ set -x 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
|
||||
|
||||
.. group-tab:: CMD
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
> flask run --extra-files file1:dirA/file2:dirB/
|
||||
# or
|
||||
> set 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
|
||||
|
||||
.. group-tab:: Powershell
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
> flask run --extra-files file1:dirA/file2:dirB/
|
||||
# or
|
||||
> $env: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
|
||||
|
|
@ -291,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
|
||||
|
|
@ -408,25 +288,25 @@ script. Activating the virtualenv will set the variables.
|
|||
|
||||
.. group-tab:: Bash
|
||||
|
||||
Unix Bash, :file:`venv/bin/activate`::
|
||||
Unix Bash, :file:`.venv/bin/activate`::
|
||||
|
||||
$ export FLASK_APP=hello
|
||||
|
||||
.. group-tab:: Fish
|
||||
|
||||
Fish, :file:`venv/bin/activate.fish`::
|
||||
Fish, :file:`.venv/bin/activate.fish`::
|
||||
|
||||
$ set -x FLASK_APP hello
|
||||
|
||||
.. group-tab:: CMD
|
||||
|
||||
Windows CMD, :file:`venv\\Scripts\\activate.bat`::
|
||||
Windows CMD, :file:`.venv\\Scripts\\activate.bat`::
|
||||
|
||||
> set FLASK_APP=hello
|
||||
|
||||
.. group-tab:: Powershell
|
||||
|
||||
Windows Powershell, :file:`venv\\Scripts\\activate.ps1`::
|
||||
Windows Powershell, :file:`.venv\\Scripts\\activate.ps1`::
|
||||
|
||||
> $env:FLASK_APP = "hello"
|
||||
|
||||
|
|
@ -541,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
|
||||
|
|
@ -558,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
|
||||
|
|
@ -606,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.
|
||||
|
||||
|
|
@ -625,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
|
||||
|
|
@ -660,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.
|
||||
|
|
|
|||
27
docs/conf.py
|
|
@ -11,16 +11,23 @@ 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",
|
||||
"pallets_sphinx_themes",
|
||||
"sphinx_issues",
|
||||
"sphinx_tabs.tabs",
|
||||
"pallets_sphinx_themes",
|
||||
]
|
||||
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),
|
||||
|
|
@ -29,9 +36,8 @@ intersphinx_mapping = {
|
|||
"itsdangerous": ("https://itsdangerous.palletsprojects.com/", None),
|
||||
"sqlalchemy": ("https://docs.sqlalchemy.org/", None),
|
||||
"wtforms": ("https://wtforms.readthedocs.io/", None),
|
||||
"blinker": ("https://pythonhosted.org/blinker/", None),
|
||||
"blinker": ("https://blinker.readthedocs.io/", None),
|
||||
}
|
||||
issues_github_path = "pallets/flask"
|
||||
|
||||
# HTML -----------------------------------------------------------------
|
||||
|
||||
|
|
@ -43,8 +49,6 @@ html_context = {
|
|||
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("Website", "https://palletsprojects.com/p/flask/"),
|
||||
ProjectLink("Twitter", "https://twitter.com/PalletsTeam"),
|
||||
ProjectLink("Chat", "https://discord.gg/pallets"),
|
||||
]
|
||||
}
|
||||
|
|
@ -54,14 +58,13 @@ html_sidebars = {
|
|||
}
|
||||
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_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, f"Flask-{version}.tex", html_title, author, "manual")]
|
||||
gettext_uuid = True
|
||||
gettext_compact = False
|
||||
|
||||
# Local Extensions -----------------------------------------------------
|
||||
|
||||
|
|
|
|||
383
docs/config.rst
|
|
@ -42,66 +42,22 @@ method::
|
|||
)
|
||||
|
||||
|
||||
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`:
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. group-tab:: Bash
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ export FLASK_ENV=development
|
||||
$ flask run
|
||||
|
||||
.. group-tab:: Fish
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ set -x FLASK_ENV development
|
||||
$ flask run
|
||||
|
||||
.. group-tab:: CMD
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
> set FLASK_ENV=development
|
||||
> flask run
|
||||
|
||||
.. group-tab:: Powershell
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
> $env:FLASK_ENV = "development"
|
||||
> flask run
|
||||
|
||||
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
|
||||
|
|
@ -109,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.
|
||||
|
||||
|
|
@ -154,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
|
||||
|
|
@ -194,6 +125,25 @@ The following configuration values are used internally by Flask:
|
|||
|
||||
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
|
||||
|
|
@ -203,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
|
||||
|
|
@ -231,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
|
||||
|
|
@ -283,24 +261,43 @@ The following configuration values are used internally by Flask:
|
|||
|
||||
Default: ``None``
|
||||
|
||||
.. py:data:: SERVER_NAME
|
||||
.. py:data:: TRUSTED_HOSTS
|
||||
|
||||
Inform the application what host and port it is bound to. Required
|
||||
for subdomain route matching support.
|
||||
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.
|
||||
|
||||
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. ::
|
||||
|
||||
127.0.0.1 localhost.dev
|
||||
|
||||
If set, ``url_for`` can generate external URLs with only an application
|
||||
context instead of a request context.
|
||||
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.
|
||||
|
||||
Must be set if ``subdomain_matching`` is enabled, to be able to extract the
|
||||
subdomain from the request.
|
||||
|
||||
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 /
|
||||
|
|
@ -322,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 returned from ``jsonify`` will contain Unicode characters. 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
|
||||
|
||||
|
|
@ -380,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``
|
||||
|
||||
|
|
@ -420,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``.
|
||||
|
||||
.. 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.
|
||||
|
||||
.. versionchanged:: 2.3
|
||||
``ENV`` was removed.
|
||||
|
||||
.. 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. This makes
|
||||
packaging and distributing your application possible via various package
|
||||
handling tools (:doc:`/patterns/distribute`) and finally modifying the
|
||||
configuration file afterwards.
|
||||
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.
|
||||
|
||||
So a common pattern is this::
|
||||
A common pattern is this::
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config.from_object('yourapplication.default_settings')
|
||||
|
|
@ -501,8 +528,8 @@ from a TOML file:
|
|||
|
||||
.. code-block:: python
|
||||
|
||||
import toml
|
||||
app.config.from_file("config.toml", load=toml.load)
|
||||
import tomllib
|
||||
app.config.from_file("config.toml", load=tomllib.load, text=False)
|
||||
|
||||
Or from a JSON file:
|
||||
|
||||
|
|
@ -515,11 +542,14 @@ Or from a JSON file:
|
|||
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 in the shell before starting the server:
|
||||
Environment variables can be set in the shell before starting the
|
||||
server:
|
||||
|
||||
.. tabs::
|
||||
|
||||
|
|
@ -527,8 +557,8 @@ Environment variables can be set in the shell before starting the server:
|
|||
|
||||
.. code-block:: text
|
||||
|
||||
$ export SECRET_KEY="5f352379324c22463451387a0aec5d2f"
|
||||
$ export MAIL_ENABLED=false
|
||||
$ export FLASK_SECRET_KEY="5f352379324c22463451387a0aec5d2f"
|
||||
$ export FLASK_MAIL_ENABLED=false
|
||||
$ flask run
|
||||
* Running on http://127.0.0.1:5000/
|
||||
|
||||
|
|
@ -536,8 +566,8 @@ Environment variables can be set in the shell before starting the server:
|
|||
|
||||
.. code-block:: text
|
||||
|
||||
$ set -x SECRET_KEY "5f352379324c22463451387a0aec5d2f"
|
||||
$ set -x MAIL_ENABLED false
|
||||
$ set -x FLASK_SECRET_KEY "5f352379324c22463451387a0aec5d2f"
|
||||
$ set -x FLASK_MAIL_ENABLED false
|
||||
$ flask run
|
||||
* Running on http://127.0.0.1:5000/
|
||||
|
||||
|
|
@ -545,8 +575,8 @@ Environment variables can be set in the shell before starting the server:
|
|||
|
||||
.. code-block:: text
|
||||
|
||||
> set SECRET_KEY="5f352379324c22463451387a0aec5d2f"
|
||||
> set MAIL_ENABLED=false
|
||||
> set FLASK_SECRET_KEY="5f352379324c22463451387a0aec5d2f"
|
||||
> set FLASK_MAIL_ENABLED=false
|
||||
> flask run
|
||||
* Running on http://127.0.0.1:5000/
|
||||
|
||||
|
|
@ -554,36 +584,51 @@ Environment variables can be set in the shell before starting the server:
|
|||
|
||||
.. code-block:: text
|
||||
|
||||
> $env:SECRET_KEY = "5f352379324c22463451387a0aec5d2f"
|
||||
> $env:MAIL_ENABLED = "false"
|
||||
> $env:FLASK_SECRET_KEY = "5f352379324c22463451387a0aec5d2f"
|
||||
> $env:FLASK_MAIL_ENABLED = "false"
|
||||
> flask run
|
||||
* Running on http://127.0.0.1:5000/
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
Here is an example of a configuration file that uses environment variables::
|
||||
.. code-block:: python
|
||||
|
||||
import os
|
||||
app.config.from_prefixed_env()
|
||||
app.config["SECRET_KEY"] # Is "5f352379324c22463451387a0aec5d2f"
|
||||
|
||||
_mail_enabled = os.environ.get("MAIL_ENABLED", default="true")
|
||||
MAIL_ENABLED = _mail_enabled.lower() in {"1", "t", "true"}
|
||||
The prefix is ``FLASK_`` by default. This is configurable via the
|
||||
``prefix`` argument of :meth:`~flask.Config.from_prefixed_env`.
|
||||
|
||||
SECRET_KEY = os.environ.get("SECRET_KEY")
|
||||
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`.
|
||||
|
||||
if not SECRET_KEY:
|
||||
raise ValueError("No SECRET_KEY set for Flask application")
|
||||
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.
|
||||
|
||||
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``.
|
||||
.. code-block:: text
|
||||
|
||||
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.
|
||||
$ 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
|
||||
|
|
@ -603,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
|
||||
|
|
@ -700,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
|
||||
:doc:`/patterns/fabric` pattern.
|
||||
- Use a tool like `fabric`_ to push code and configuration separately
|
||||
to the production server(s).
|
||||
|
||||
.. _fabric: https://www.fabfile.org/
|
||||
|
||||
|
|
|
|||
|
|
@ -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/
|
||||
|
|
|
|||
|
|
@ -39,54 +39,22 @@ during a request. This debugger should only be used during development.
|
|||
security risk. Do not run the development server or debugger in a
|
||||
production environment.
|
||||
|
||||
To enable the debugger, run the development server with the
|
||||
``FLASK_ENV`` environment variable set to ``development``. This puts
|
||||
Flask in debug mode, which changes how it handles some errors, and
|
||||
enables the debugger and reloader.
|
||||
The debugger is enabled by default when the development server is run in debug mode.
|
||||
|
||||
.. tabs::
|
||||
.. code-block:: text
|
||||
|
||||
.. group-tab:: Bash
|
||||
$ flask --app hello run --debug
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ export FLASK_ENV=development
|
||||
$ flask run
|
||||
|
||||
.. group-tab:: Fish
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ set -x FLASK_ENV development
|
||||
$ flask run
|
||||
|
||||
.. group-tab:: CMD
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
> set FLASK_ENV=development
|
||||
> flask run
|
||||
|
||||
.. group-tab:: Powershell
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
> $env:FLASK_ENV = "development"
|
||||
> flask run
|
||||
|
||||
``FLASK_ENV`` can only be set as an environment variable. When running
|
||||
from Python code, passing ``debug=True`` enables debug mode, which is
|
||||
mostly equivalent. Debug mode can be controlled separately from
|
||||
``FLASK_ENV`` with the ``FLASK_DEBUG`` environment variable as well.
|
||||
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, debug mode, and development mode. More information about the
|
||||
debugger can be found in the `Werkzeug documentation
|
||||
<https://werkzeug.palletsprojects.com/debug/>`__.
|
||||
: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
|
||||
|
|
@ -98,41 +66,13 @@ 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,
|
||||
but it can be useful to disable the built-in debugger and reloader,
|
||||
which can interfere.
|
||||
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.
|
||||
|
||||
When running from the command line:
|
||||
.. code-block:: text
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. group-tab:: Bash
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ export FLASK_ENV=development
|
||||
$ flask run --no-debugger --no-reload
|
||||
|
||||
.. group-tab:: Fish
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ set -x FLASK_ENV development
|
||||
$ flask run --no-debugger --no-reload
|
||||
|
||||
.. group-tab:: CMD
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
> set FLASK_ENV=development
|
||||
> flask run --no-debugger --no-reload
|
||||
|
||||
.. group-tab:: Powershell
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
> $env:FLASK_ENV = "development"
|
||||
> flask run --no-debugger --no-reload
|
||||
$ flask --app hello run --debug --no-debugger --no-reload
|
||||
|
||||
When running from Python:
|
||||
|
||||
|
|
@ -140,8 +80,20 @@ When running from 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 debugging.
|
||||
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``.
|
||||
|
|
@ -1,5 +1,3 @@
|
|||
.. _asgi:
|
||||
|
||||
ASGI
|
||||
====
|
||||
|
||||
|
|
@ -22,7 +20,7 @@ wrapping the Flask app,
|
|||
asgi_app = WsgiToAsgi(app)
|
||||
|
||||
and then serving the ``asgi_app`` with the ASGI server, e.g. using
|
||||
`Hypercorn <https://gitlab.com/pgjones/hypercorn>`_,
|
||||
`Hypercorn <https://github.com/pgjones/hypercorn>`_,
|
||||
|
||||
.. sourcecode:: text
|
||||
|
||||
|
|
|
|||
|
|
@ -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,238 +0,0 @@
|
|||
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: https://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 Options
|
||||
==================
|
||||
Deploying to Production
|
||||
=======================
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
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.
|
||||
"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.**
|
||||
|
||||
|
||||
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 Google Cloud Run <https://cloud.google.com/run/docs/quickstarts/build-and-deploy/python>`_
|
||||
- `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
|
||||
asgi
|
||||
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,216 +1,94 @@
|
|||
mod_wsgi (Apache)
|
||||
=================
|
||||
mod_wsgi
|
||||
========
|
||||
|
||||
If you are using the `Apache`_ webserver, consider using `mod_wsgi`_.
|
||||
`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.
|
||||
|
||||
.. admonition:: Watch Out
|
||||
* 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.
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
.. _Apache: https://httpd.apache.org/
|
||||
.. _mod_wsgi-express: https://pypi.org/project/mod-wsgi/
|
||||
.. _mod_wsgi: https://modwsgi.readthedocs.io/
|
||||
.. _Apache httpd: https://httpd.apache.org/
|
||||
|
||||
Installing `mod_wsgi`
|
||||
---------------------
|
||||
|
||||
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
|
||||
----------
|
||||
|
||||
If you are using Ubuntu/Debian you can apt-get it and activate it as
|
||||
follows:
|
||||
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.
|
||||
|
||||
.. sourcecode:: text
|
||||
Create a virtualenv, install your application, then install
|
||||
``mod_wsgi``.
|
||||
|
||||
$ apt-get install libapache2-mod-wsgi-py3
|
||||
.. code-block:: text
|
||||
|
||||
If you are using a yum based distribution (Fedora, OpenSUSE, etc..) you
|
||||
can install it as follows:
|
||||
$ cd hello-app
|
||||
$ python -m venv .venv
|
||||
$ . .venv/bin/activate
|
||||
$ pip install . # install your application
|
||||
$ pip install mod_wsgi
|
||||
|
||||
.. sourcecode:: text
|
||||
|
||||
$ yum install mod_wsgi
|
||||
Running
|
||||
-------
|
||||
|
||||
On FreeBSD install `mod_wsgi` by compiling the `www/mod_wsgi` port or by
|
||||
using pkg_add:
|
||||
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.
|
||||
|
||||
.. sourcecode:: text
|
||||
.. code-block:: python
|
||||
:caption: ``wsgi.py``
|
||||
|
||||
$ pkg install ap24-py37-mod_wsgi
|
||||
from hello import app
|
||||
|
||||
If you are using pkgsrc you can install `mod_wsgi` by compiling the
|
||||
`www/ap2-wsgi` package.
|
||||
application = app
|
||||
|
||||
If you encounter segfaulting child processes after the first apache
|
||||
reload you can safely ignore them. Just restart the server.
|
||||
.. code-block:: python
|
||||
:caption: ``wsgi.py``
|
||||
|
||||
Creating a `.wsgi` file
|
||||
-----------------------
|
||||
from hello import create_app
|
||||
|
||||
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'
|
||||
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,71 +1,143 @@
|
|||
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: https://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,153 +0,0 @@
|
|||
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)
|
||||
|
|
@ -96,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
|
||||
|
|
@ -107,7 +107,7 @@ 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
|
||||
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
|
||||
|
|
@ -118,8 +118,8 @@ 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
|
||||
|
|
@ -130,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
|
||||
|
|
@ -153,22 +169,20 @@ 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.
|
||||
|
||||
Also see the :doc:`/becomingbig` section of the documentation for some
|
||||
inspiration for larger applications based on Flask.
|
||||
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.
|
||||
|
||||
|
||||
Async/await and ASGI support
|
||||
|
|
@ -195,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.
|
||||
|
||||
|
|
@ -204,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.
|
||||
|
|
|
|||
|
|
@ -69,7 +69,6 @@ See also:
|
|||
- 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.
|
||||
- `Getting started with Sentry <https://docs.sentry.io/quickstart/?platform=python>`__
|
||||
- `Flask-specific documentation <https://docs.sentry.io/platforms/python/guides/flask/>`__
|
||||
|
||||
|
||||
|
|
@ -151,7 +150,7 @@ If a route receives an unallowed request method, a "405 Method Not Allowed"
|
|||
subclasses of :class:`~werkzeug.exceptions.HTTPException` and are provided by
|
||||
default in Flask.
|
||||
|
||||
Flask gives you to the ability to raise any HTTP exception registered by
|
||||
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.
|
||||
|
|
@ -232,7 +231,7 @@ 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
|
||||
|
|
@ -489,7 +488,7 @@ This is a simple example:
|
|||
|
||||
@app.errorhandler(InvalidAPIUsage)
|
||||
def invalid_api_usage(e):
|
||||
return jsonify(e.to_dict())
|
||||
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
|
||||
|
|
|
|||
|
|
@ -1,310 +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
|
||||
:doc:`/patterns/appfactories` 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 (:doc:`/patterns/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.
|
||||
|
||||
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
|
||||
|
|
@ -312,21 +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
|
||||
.. _mailinglist: https://mail.python.org/mailman/listinfo/flask
|
||||
.. _Discord server: https://discord.gg/pallets
|
||||
.. _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/
|
||||
|
|
|
|||
|
|
@ -39,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,53 +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.
|
||||
|
||||
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 :doc:`becomingbig` chapter. If you are curious about
|
||||
the Flask design principles, head over to the section about :doc:`design`.
|
||||
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()
|
||||
206
docs/htmlfaq.rst
|
|
@ -1,206 +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 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 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.
|
||||
|
|
@ -3,12 +3,15 @@
|
|||
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 :doc:`installation`
|
||||
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
|
||||
|
|
@ -16,28 +19,26 @@ complete application with Flask. Common patterns are described in the
|
|||
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 <https://jinja.palletsprojects.com/>`_
|
||||
- `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
|
||||
|
|
@ -49,16 +50,17 @@ instructions for web development with Flask.
|
|||
config
|
||||
signals
|
||||
views
|
||||
lifecycle
|
||||
appcontext
|
||||
reqcontext
|
||||
blueprints
|
||||
extensions
|
||||
cli
|
||||
server
|
||||
shell
|
||||
patterns/index
|
||||
web-security
|
||||
deploying/index
|
||||
becomingbig
|
||||
gevent
|
||||
async-await
|
||||
|
||||
|
||||
|
|
@ -77,14 +79,10 @@ 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
|
||||
extensiondev
|
||||
contributing
|
||||
license
|
||||
|
|
|
|||
|
|
@ -5,10 +5,7 @@ Installation
|
|||
Python Version
|
||||
--------------
|
||||
|
||||
We recommend using the latest version of Python. Flask supports Python
|
||||
3.6 and newer.
|
||||
|
||||
``async`` support in Flask requires Python 3.7+ for ``contextvars.ContextVar``.
|
||||
We recommend using the latest version of Python. Flask supports Python 3.10 and newer.
|
||||
|
||||
|
||||
Dependencies
|
||||
|
|
@ -26,12 +23,14 @@ 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
|
||||
|
|
@ -40,17 +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 :doc:`signals`.
|
||||
* `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/
|
||||
.. _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
|
||||
--------------------
|
||||
|
||||
|
|
@ -75,7 +83,7 @@ environments.
|
|||
Create an environment
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Create a project folder and a :file:`venv` folder within:
|
||||
Create a project folder and a :file:`.venv` folder within:
|
||||
|
||||
.. tabs::
|
||||
|
||||
|
|
@ -85,7 +93,7 @@ Create a project folder and a :file:`venv` folder within:
|
|||
|
||||
$ mkdir myproject
|
||||
$ cd myproject
|
||||
$ python3 -m venv venv
|
||||
$ python3 -m venv .venv
|
||||
|
||||
.. group-tab:: Windows
|
||||
|
||||
|
|
@ -93,7 +101,7 @@ Create a project folder and a :file:`venv` folder within:
|
|||
|
||||
> mkdir myproject
|
||||
> cd myproject
|
||||
> py -3 -m venv venv
|
||||
> py -3 -m venv .venv
|
||||
|
||||
|
||||
.. _install-activate-env:
|
||||
|
|
@ -109,13 +117,13 @@ Before you work on your project, activate the corresponding environment:
|
|||
|
||||
.. code-block:: text
|
||||
|
||||
$ . venv/bin/activate
|
||||
$ . .venv/bin/activate
|
||||
|
||||
.. group-tab:: Windows
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
> venv\Scripts\activate
|
||||
> .venv\Scripts\activate
|
||||
|
||||
Your shell prompt will change to show the name of the activated
|
||||
environment.
|
||||
|
|
|
|||
|
|
@ -1,19 +1,5 @@
|
|||
License
|
||||
=======
|
||||
BSD-3-Clause License
|
||||
====================
|
||||
|
||||
BSD-3-Clause Source License
|
||||
---------------------------
|
||||
|
||||
The BSD-3-Clause 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.
|
||||
|
|
@ -159,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'),
|
||||
):
|
||||
|
|
|
|||
|
|
@ -18,34 +18,20 @@ 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
|
||||
:doc:`/deploying/index`. For development, Werkzeug provides a server
|
||||
through :func:`werkzeug.serving.run_simple`::
|
||||
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 production WSGI server. See :doc:`/deploying/index`.
|
||||
|
||||
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
|
||||
----------------------
|
||||
|
|
@ -58,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
|
||||
|
|
@ -89,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
|
||||
|
|
@ -117,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
|
||||
|
|
@ -143,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
|
||||
|
|
@ -166,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
|
||||
|
||||
|
|
|
|||
|
|
@ -89,71 +89,20 @@ Using Applications
|
|||
|
||||
To run such an application, you can use the :command:`flask` command:
|
||||
|
||||
.. tabs::
|
||||
.. code-block:: text
|
||||
|
||||
.. group-tab:: Bash
|
||||
$ flask --app hello run
|
||||
|
||||
.. code-block:: text
|
||||
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:
|
||||
|
||||
$ export FLASK_APP=myapp
|
||||
$ flask run
|
||||
.. code-block:: text
|
||||
|
||||
.. group-tab:: Fish
|
||||
$ flask --app 'hello:create_app(local_auth=True)' run
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ set -x FLASK_APP myapp
|
||||
$ flask run
|
||||
|
||||
.. group-tab:: CMD
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
> set FLASK_APP=myapp
|
||||
> flask run
|
||||
|
||||
.. group-tab:: Powershell
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
> $env:FLASK_APP = "myapp"
|
||||
> flask run
|
||||
|
||||
Flask will automatically detect the factory (``create_app`` or ``make_app``)
|
||||
in ``myapp``. You can also pass arguments to the factory like this:
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. group-tab:: Bash
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ export FLASK_APP="myapp:create_app('dev')"
|
||||
$ flask run
|
||||
|
||||
.. group-tab:: Fish
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ set -x FLASK_APP "myapp:create_app('dev')"
|
||||
$ flask run
|
||||
|
||||
.. group-tab:: CMD
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
> set FLASK_APP="myapp:create_app('dev')"
|
||||
> flask run
|
||||
|
||||
.. group-tab:: Powershell
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
> $env:FLASK_APP = "myapp:create_app('dev')"
|
||||
> flask run
|
||||
|
||||
Then the ``create_app`` factory in ``myapp`` is called with the string
|
||||
``'dev'`` as the argument. See :doc:`/cli` for more detail.
|
||||
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,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 factory 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,170 +0,0 @@
|
|||
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.
|
||||
|
||||
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
|
||||
package. If you have not yet converted your application into a package,
|
||||
head over to :doc:`packages` 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 :doc:`fabric` 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:`use a virtualenv
|
||||
<install-create-env>`.
|
||||
|
||||
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,184 +0,0 @@
|
|||
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 (:doc:`packages`) and a good concept for
|
||||
configurations (:doc:`/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 (:doc:`distribute`).
|
||||
- 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 = f'{dist}.tar.gz'
|
||||
|
||||
# upload the package to the temporary folder on the server
|
||||
put(f'dist/{filename}', f'/tmp/{filename}')
|
||||
|
||||
# install the package in the application's virtualenv with pip
|
||||
run(f'/var/www/yourapplication/env/bin/pip install /tmp/{filename}')
|
||||
|
||||
# remove the uploaded package
|
||||
run(f'rm -r /tmp/{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 :doc:`/deploying/mod_wsgi`.
|
||||
|
||||
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 :doc:`/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`::
|
||||
|
|
|
|||
|
|
@ -19,8 +19,6 @@ collected in the following pages.
|
|||
appfactories
|
||||
appdispatch
|
||||
urlprocessors
|
||||
distribute
|
||||
fabric
|
||||
sqlite3
|
||||
sqlalchemy
|
||||
fileuploads
|
||||
|
|
@ -29,7 +27,7 @@ collected in the following pages.
|
|||
wtforms
|
||||
templateinheritance
|
||||
flashing
|
||||
jquery
|
||||
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,148 +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 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>
|
||||
$SCRIPT_ROOT = {{ request.script_root|tojson }};
|
||||
</script>
|
||||
|
||||
|
||||
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>
|
||||
$(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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -42,84 +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"
|
||||
|
||||
.. tabs::
|
||||
Install your application so it is importable:
|
||||
|
||||
.. group-tab:: Bash
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ export FLASK_APP=yourapplication
|
||||
|
||||
.. group-tab:: Fish
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ set -x FLASK_APP yourapplication
|
||||
|
||||
.. group-tab:: CMD
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
> set FLASK_APP=yourapplication
|
||||
|
||||
.. group-tab:: Powershell
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
> $env:FLASK_APP = "yourapplication"
|
||||
|
||||
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:
|
||||
|
||||
.. tabs::
|
||||
|
||||
.. group-tab:: Bash
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ export FLASK_ENV=development
|
||||
|
||||
.. group-tab:: Fish
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ set -x FLASK_ENV development
|
||||
|
||||
.. group-tab:: CMD
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
> set FLASK_ENV=development
|
||||
|
||||
.. group-tab:: Powershell
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
> $env: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
|
||||
|
|
@ -151,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
|
||||
|
|
@ -173,10 +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
|
||||
:doc:`/becomingbig` section for some inspiration how to deal with that.
|
||||
|
||||
|
||||
Working with Blueprints
|
||||
-----------------------
|
||||
|
|
|
|||
|
|
@ -34,8 +34,7 @@ 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')
|
||||
db_session = scoped_session(sessionmaker(autocommit=False,
|
||||
|
|
@ -132,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
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
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::
|
||||
|
||||
|
|
@ -30,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('/')
|
||||
|
|
|
|||
|
|
@ -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
|
||||
-----------
|
||||
|
||||
|
|
@ -20,7 +35,7 @@ data and to then invoke that function and pass it to a response object::
|
|||
def generate():
|
||||
for row in iter_all_rows():
|
||||
yield f"{','.join(row)}\n"
|
||||
return app.response_class(generate(), mimetype='text/csv')
|
||||
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
|
||||
|
|
@ -29,52 +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.
|
||||
|
||||
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
|
||||
.. code-block:: python
|
||||
|
||||
@app.route('/my-large-page.html')
|
||||
def render_large_template():
|
||||
rows = iter_all_rows()
|
||||
return app.response_class(stream_template('the_template.html', rows=rows))
|
||||
from flask import stream_template
|
||||
|
||||
@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.
|
||||
|
||||
.. 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 app.response_class(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.
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ WTForm's field function, which renders the field for us. The keyword
|
|||
arguments will be inserted as HTML attributes. So, for example, you can
|
||||
call ``render_field(form.username, class='username')`` to add a class to
|
||||
the input element. Note that WTForms returns standard Python strings,
|
||||
so we have to tell Jinja2 that this data is already HTML-escaped with
|
||||
so we have to tell Jinja that this data is already HTML-escaped with
|
||||
the ``|safe`` filter.
|
||||
|
||||
Here is the :file:`register.html` template for the function we used above, which
|
||||
|
|
|
|||
|
|
@ -39,50 +39,20 @@ Save it as :file:`hello.py` or something similar. Make sure to not call
|
|||
your application :file:`flask.py` because this would conflict with Flask
|
||||
itself.
|
||||
|
||||
To run the application, use the :command:`flask` command or
|
||||
:command:`python -m flask`. Before you can do that you need
|
||||
to tell your terminal the application to work with by exporting the
|
||||
``FLASK_APP`` environment variable:
|
||||
To run the application, use the ``flask`` command or
|
||||
``python -m flask``. You need to tell the Flask where your application
|
||||
is with the ``--app`` option.
|
||||
|
||||
.. tabs::
|
||||
.. code-block:: text
|
||||
|
||||
.. group-tab:: Bash
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ export FLASK_APP=hello
|
||||
$ flask run
|
||||
* Running on http://127.0.0.1:5000/
|
||||
|
||||
.. group-tab:: Fish
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ set -x FLASK_APP hello
|
||||
$ flask run
|
||||
* Running on http://127.0.0.1:5000/
|
||||
|
||||
.. group-tab:: CMD
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
> set FLASK_APP=hello
|
||||
> flask run
|
||||
* Running on http://127.0.0.1:5000/
|
||||
|
||||
.. group-tab:: Powershell
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
> $env:FLASK_APP = "hello"
|
||||
> flask run
|
||||
* Running on http://127.0.0.1:5000/
|
||||
$ flask --app hello run
|
||||
* Serving Flask app 'hello'
|
||||
* Running on http://127.0.0.1:5000 (Press CTRL+C to quit)
|
||||
|
||||
.. admonition:: Application Discovery Behavior
|
||||
|
||||
As a shortcut, if the file is named ``app.py`` or ``wsgi.py``, you
|
||||
don't have to set the ``FLASK_APP`` environment variable. See
|
||||
:doc:`/cli` for more details.
|
||||
don't have to use ``--app``. See :doc:`/cli` for more details.
|
||||
|
||||
This launches a very simple builtin server, which is good enough for
|
||||
testing but probably not what you want to use in production. For
|
||||
|
|
@ -114,34 +84,6 @@ handle that.
|
|||
This tells your operating system to listen on all public IPs.
|
||||
|
||||
|
||||
What to do if the Server does not Start
|
||||
---------------------------------------
|
||||
|
||||
In case the :command:`python -m flask` fails or :command:`flask`
|
||||
does not exist, there are multiple reasons this might be the case.
|
||||
First of all you need to look at the error message.
|
||||
|
||||
Old Version of Flask
|
||||
````````````````````
|
||||
|
||||
Versions of Flask older than 0.11 used to have different ways to start the
|
||||
application. In short, the :command:`flask` command did not exist, and
|
||||
neither did :command:`python -m flask`. In that case you have two options:
|
||||
either upgrade to newer Flask versions or have a look at :doc:`/server`
|
||||
to see the alternative method for running a server.
|
||||
|
||||
Invalid Import Name
|
||||
```````````````````
|
||||
|
||||
The ``FLASK_APP`` environment variable is the name of the module to import at
|
||||
:command:`flask run`. In case that module is incorrectly named you will get an
|
||||
import error upon start (or if debug is enabled when you navigate to the
|
||||
application). It will tell you what it tried to import and why it failed.
|
||||
|
||||
The most common reason is a typo or because you did not actually create an
|
||||
``app`` object.
|
||||
|
||||
|
||||
Debug Mode
|
||||
----------
|
||||
|
||||
|
|
@ -162,43 +104,21 @@ error occurs during a request.
|
|||
security risk. Do not run the development server or debugger in a
|
||||
production environment.
|
||||
|
||||
To enable all development features, set the ``FLASK_ENV`` environment
|
||||
variable to ``development`` before calling ``flask run``.
|
||||
To enable debug mode, use the ``--debug`` option.
|
||||
|
||||
.. tabs::
|
||||
.. code-block:: text
|
||||
|
||||
.. group-tab:: Bash
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ export FLASK_ENV=development
|
||||
$ flask run
|
||||
|
||||
.. group-tab:: Fish
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ set -x FLASK_ENV development
|
||||
$ flask run
|
||||
|
||||
.. group-tab:: CMD
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
> set FLASK_ENV=development
|
||||
> flask run
|
||||
|
||||
.. group-tab:: Powershell
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
> $env:FLASK_ENV = "development"
|
||||
> flask run
|
||||
$ 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 stat
|
||||
* Debugger is active!
|
||||
* Debugger PIN: nnn-nnn-nnn
|
||||
|
||||
See also:
|
||||
|
||||
- :doc:`/server` and :doc:`/cli` for information about running in
|
||||
development mode.
|
||||
- :doc:`/server` and :doc:`/cli` for information about running in debug mode.
|
||||
- :doc:`/debugging` for information about using the built-in debugger
|
||||
and other debuggers.
|
||||
- :doc:`/logging` and :doc:`/errorhandling` to log errors and display
|
||||
|
|
@ -219,18 +139,16 @@ how you're using untrusted data.
|
|||
|
||||
.. code-block:: python
|
||||
|
||||
from flask import request
|
||||
from markupsafe import escape
|
||||
|
||||
@app.route("/<name>")
|
||||
def hello(name):
|
||||
@app.route("/hello")
|
||||
def hello():
|
||||
name = request.args.get("name", "Flask")
|
||||
return f"Hello, {escape(name)}!"
|
||||
|
||||
If a user managed to submit the name ``<script>alert("bad")</script>``,
|
||||
escaping causes it to be rendered as text, rather than running the
|
||||
script in the user's browser.
|
||||
|
||||
``<name>`` in the route captures a value from the URL and passes it to
|
||||
the view function. These variable rules are explained below.
|
||||
If a user submits ``/hello?name=<script>alert("bad")</script>``, escaping causes
|
||||
it to be rendered as text, rather than running the script in the user's browser.
|
||||
|
||||
|
||||
Routing
|
||||
|
|
@ -340,7 +258,7 @@ Why would you want to build URLs using the URL reversing function
|
|||
For example, here we use the :meth:`~flask.Flask.test_request_context` method
|
||||
to try out :func:`~flask.url_for`. :meth:`~flask.Flask.test_request_context`
|
||||
tells Flask to behave as though it's handling a request even while we use a
|
||||
Python shell. See :ref:`context-locals`.
|
||||
Python shell. See :doc:`/appcontext`.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
|
|
@ -390,6 +308,24 @@ of the :meth:`~flask.Flask.route` decorator to handle different HTTP methods.
|
|||
else:
|
||||
return show_the_login_form()
|
||||
|
||||
The example above keeps all methods for the route within one function,
|
||||
which can be useful if each part uses some common data.
|
||||
|
||||
You can also separate views for different methods into different
|
||||
functions. Flask provides a shortcut for decorating such routes with
|
||||
:meth:`~flask.Flask.get`, :meth:`~flask.Flask.post`, etc. for each
|
||||
common HTTP method.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@app.get('/login')
|
||||
def login_get():
|
||||
return show_the_login_form()
|
||||
|
||||
@app.post('/login')
|
||||
def login_post():
|
||||
return do_the_login()
|
||||
|
||||
If ``GET`` is present, Flask automatically adds support for the ``HEAD`` method
|
||||
and handles ``HEAD`` requests according to the `HTTP RFC`_. Likewise,
|
||||
``OPTIONS`` is automatically implemented for you.
|
||||
|
|
@ -416,9 +352,17 @@ Rendering Templates
|
|||
|
||||
Generating HTML from within Python is not fun, and actually pretty
|
||||
cumbersome because you have to do the HTML escaping on your own to keep
|
||||
the application secure. Because of that Flask configures the `Jinja2
|
||||
the application secure. Because of that Flask configures the `Jinja
|
||||
<https://palletsprojects.com/p/jinja/>`_ template engine for you automatically.
|
||||
|
||||
Templates can be used to generate any type of text file. For web applications, you'll
|
||||
primarily be generating HTML pages, but you can also generate markdown, plain text for
|
||||
emails, and anything else.
|
||||
|
||||
For a reference to HTML, CSS, and other web APIs, use the `MDN Web Docs`_.
|
||||
|
||||
.. _MDN Web Docs: https://developer.mozilla.org/
|
||||
|
||||
To render a template you can use the :func:`~flask.render_template`
|
||||
method. All you have to do is provide the name of the template and the
|
||||
variables you want to pass to the template engine as keyword arguments.
|
||||
|
|
@ -429,7 +373,7 @@ Here's a simple example of how to render a template::
|
|||
@app.route('/hello/')
|
||||
@app.route('/hello/<name>')
|
||||
def hello(name=None):
|
||||
return render_template('hello.html', name=name)
|
||||
return render_template('hello.html', person=name)
|
||||
|
||||
Flask will look for templates in the :file:`templates` folder. So if your
|
||||
application is a module, this folder is next to that module, if it's a
|
||||
|
|
@ -448,8 +392,8 @@ package it's actually inside your package:
|
|||
/templates
|
||||
/hello.html
|
||||
|
||||
For templates you can use the full power of Jinja2 templates. Head over
|
||||
to the official `Jinja2 Template Documentation
|
||||
For templates you can use the full power of Jinja templates. Head over
|
||||
to the official `Jinja Template Documentation
|
||||
<https://jinja.palletsprojects.com/templates/>`_ for more information.
|
||||
|
||||
Here is an example template:
|
||||
|
|
@ -458,8 +402,8 @@ Here is an example template:
|
|||
|
||||
<!doctype html>
|
||||
<title>Hello from Flask</title>
|
||||
{% if name %}
|
||||
<h1>Hello {{ name }}!</h1>
|
||||
{% if person %}
|
||||
<h1>Hello {{ person }}!</h1>
|
||||
{% else %}
|
||||
<h1>Hello, World!</h1>
|
||||
{% endif %}
|
||||
|
|
@ -473,7 +417,7 @@ know how that works, see :doc:`patterns/templateinheritance`. Basically
|
|||
template inheritance makes it possible to keep certain elements on each
|
||||
page (like header, navigation and footer).
|
||||
|
||||
Automatic escaping is enabled, so if ``name`` contains HTML it will be escaped
|
||||
Automatic escaping is enabled, so if ``person`` contains HTML it will be escaped
|
||||
automatically. If you can trust a variable and you know that it will be
|
||||
safe HTML (for example because it came from a module that converts wiki
|
||||
markup to HTML) you can mark it as safe by using the
|
||||
|
|
@ -505,105 +449,58 @@ Here is a basic introduction to how the :class:`~markupsafe.Markup` class works:
|
|||
Accessing Request Data
|
||||
----------------------
|
||||
|
||||
For web applications it's crucial to react to the data a client sends to
|
||||
the server. In Flask this information is provided by the global
|
||||
:class:`~flask.request` object. If you have some experience with Python
|
||||
you might be wondering how that object can be global and how Flask
|
||||
manages to still be threadsafe. The answer is context locals:
|
||||
For web applications it's crucial to react to the data a client sends to the
|
||||
server. In Flask this information is provided by the global :data:`.request`
|
||||
object, which is an instance of :class:`.Request`. This object has many
|
||||
attributes and methods to work with the incoming request data, but here is a
|
||||
broad overview. First it needs to be imported.
|
||||
|
||||
|
||||
.. _context-locals:
|
||||
|
||||
Context Locals
|
||||
``````````````
|
||||
|
||||
.. admonition:: Insider Information
|
||||
|
||||
If you want to understand how that works and how you can implement
|
||||
tests with context locals, read this section, otherwise just skip it.
|
||||
|
||||
Certain objects in Flask are global objects, but not of the usual kind.
|
||||
These objects are actually proxies to objects that are local to a specific
|
||||
context. What a mouthful. But that is actually quite easy to understand.
|
||||
|
||||
Imagine the context being the handling thread. A request comes in and the
|
||||
web server decides to spawn a new thread (or something else, the
|
||||
underlying object is capable of dealing with concurrency systems other
|
||||
than threads). When Flask starts its internal request handling it
|
||||
figures out that the current thread is the active context and binds the
|
||||
current application and the WSGI environments to that context (thread).
|
||||
It does that in an intelligent way so that one application can invoke another
|
||||
application without breaking.
|
||||
|
||||
So what does this mean to you? Basically you can completely ignore that
|
||||
this is the case unless you are doing something like unit testing. You
|
||||
will notice that code which depends on a request object will suddenly break
|
||||
because there is no request object. The solution is creating a request
|
||||
object yourself and binding it to the context. The easiest solution for
|
||||
unit testing is to use the :meth:`~flask.Flask.test_request_context`
|
||||
context manager. In combination with the ``with`` statement it will bind a
|
||||
test request so that you can interact with it. Here is an example::
|
||||
.. code-block:: python
|
||||
|
||||
from flask import request
|
||||
|
||||
with app.test_request_context('/hello', method='POST'):
|
||||
# now you can do something with the request until the
|
||||
# end of the with block, such as basic assertions:
|
||||
assert request.path == '/hello'
|
||||
assert request.method == 'POST'
|
||||
If you have some experience with Python you might be wondering how that object
|
||||
can be global when Flask handles multiple requests at a time. The answer is
|
||||
that :data:`.request` is actually a proxy, pointing at whatever request is
|
||||
currently being handled by a given worker, which is managed internally by Flask
|
||||
and Python. See :doc:`/appcontext` for much more information.
|
||||
|
||||
The other possibility is passing a whole WSGI environment to the
|
||||
:meth:`~flask.Flask.request_context` method::
|
||||
The current request method is available in the :attr:`~.Request.method`
|
||||
attribute. To access form data (data transmitted in a ``POST`` or ``PUT``
|
||||
request), use the :attr:`~flask.Request.form` attribute, which behaves like a
|
||||
dict.
|
||||
|
||||
with app.request_context(environ):
|
||||
assert request.method == 'POST'
|
||||
.. code-block:: python
|
||||
|
||||
The Request Object
|
||||
``````````````````
|
||||
|
||||
The request object is documented in the API section and we will not cover
|
||||
it here in detail (see :class:`~flask.Request`). Here is a broad overview of
|
||||
some of the most common operations. First of all you have to import it from
|
||||
the ``flask`` module::
|
||||
|
||||
from flask import request
|
||||
|
||||
The current request method is available by using the
|
||||
:attr:`~flask.Request.method` attribute. To access form data (data
|
||||
transmitted in a ``POST`` or ``PUT`` request) you can use the
|
||||
:attr:`~flask.Request.form` attribute. Here is a full example of the two
|
||||
attributes mentioned above::
|
||||
|
||||
@app.route('/login', methods=['POST', 'GET'])
|
||||
@app.route("/login", methods=["GET", "POST"])
|
||||
def login():
|
||||
error = None
|
||||
if request.method == 'POST':
|
||||
if valid_login(request.form['username'],
|
||||
request.form['password']):
|
||||
return log_the_user_in(request.form['username'])
|
||||
|
||||
if request.method == "POST":
|
||||
if valid_login(request.form["username"], request.form["password"]):
|
||||
return store_login(request.form["username"])
|
||||
else:
|
||||
error = 'Invalid username/password'
|
||||
# the code below is executed if the request method
|
||||
# was GET or the credentials were invalid
|
||||
return render_template('login.html', error=error)
|
||||
error = "Invalid username or password"
|
||||
|
||||
What happens if the key does not exist in the ``form`` attribute? In that
|
||||
case a special :exc:`KeyError` is raised. You can catch it like a
|
||||
standard :exc:`KeyError` but if you don't do that, a HTTP 400 Bad Request
|
||||
error page is shown instead. So for many situations you don't have to
|
||||
deal with that problem.
|
||||
# Executed if the request method was GET or the credentials were invalid.
|
||||
return render_template("login.html", error=error)
|
||||
|
||||
To access parameters submitted in the URL (``?key=value``) you can use the
|
||||
:attr:`~flask.Request.args` attribute::
|
||||
If the key does not exist in ``form``, a special :exc:`KeyError` is raised. You
|
||||
can catch it like a normal ``KeyError``, otherwise it will return a HTTP 400
|
||||
Bad Request error page. You can also use the
|
||||
:meth:`~werkzeug.datastructures.MultiDict.get` method to get a default
|
||||
instead of an error.
|
||||
|
||||
To access parameters submitted in the URL (``?key=value``), use the
|
||||
:attr:`~.Request.args` attribute. Key errors behave the same as ``form``,
|
||||
returning a 400 response if not caught.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
searchword = request.args.get('key', '')
|
||||
|
||||
We recommend accessing URL parameters with `get` or by catching the
|
||||
:exc:`KeyError` because users might change the URL and presenting them a 400
|
||||
bad request page in that case is not user friendly.
|
||||
|
||||
For a full list of methods and attributes of the request object, head over
|
||||
to the :class:`~flask.Request` documentation.
|
||||
For a full list of methods and attributes of the request object, see the
|
||||
:class:`~.Request` documentation.
|
||||
|
||||
|
||||
File Uploads
|
||||
|
|
@ -740,22 +637,25 @@ The return value from a view function is automatically converted into
|
|||
a response object for you. If the return value is a string it's
|
||||
converted into a response object with the string as response body, a
|
||||
``200 OK`` status code and a :mimetype:`text/html` mimetype. If the
|
||||
return value is a dict, :func:`jsonify` is called to produce a response.
|
||||
The logic that Flask applies to converting return values into response
|
||||
objects is as follows:
|
||||
return value is a dict or list, :func:`jsonify` is called to produce a
|
||||
response. The logic that Flask applies to converting return values into
|
||||
response objects is as follows:
|
||||
|
||||
1. If a response object of the correct type is returned it's directly
|
||||
returned from the view.
|
||||
2. If it's a string, a response object is created with that data and
|
||||
the default parameters.
|
||||
3. If it's a dict, a response object is created using ``jsonify``.
|
||||
4. If a tuple is returned the items in the tuple can provide extra
|
||||
3. If it's an iterator or generator returning strings or bytes, it is
|
||||
treated as a streaming response.
|
||||
4. If it's a dict or list, a response object is created using
|
||||
:func:`~flask.json.jsonify`.
|
||||
5. If a tuple is returned the items in the tuple can provide extra
|
||||
information. Such tuples have to be in the form
|
||||
``(response, status)``, ``(response, headers)``, or
|
||||
``(response, status, headers)``. The ``status`` value will override
|
||||
the status code and ``headers`` can be a list or dictionary of
|
||||
additional header values.
|
||||
5. If none of that works, Flask will assume the return value is a
|
||||
6. If none of that works, Flask will assume the return value is a
|
||||
valid WSGI application and convert that into a response object.
|
||||
|
||||
If you want to get hold of the resulting response object inside the view
|
||||
|
|
@ -786,8 +686,8 @@ APIs with JSON
|
|||
``````````````
|
||||
|
||||
A common response format when writing an API is JSON. It's easy to get
|
||||
started writing such an API with Flask. If you return a ``dict`` from a
|
||||
view, it will be converted to a JSON response.
|
||||
started writing such an API with Flask. If you return a ``dict`` or
|
||||
``list`` from a view, it will be converted to a JSON response.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
|
|
@ -800,20 +700,20 @@ view, it will be converted to a JSON response.
|
|||
"image": url_for("user_image", filename=user.image),
|
||||
}
|
||||
|
||||
Depending on your API design, you may want to create JSON responses for
|
||||
types other than ``dict``. In that case, use the
|
||||
:func:`~flask.json.jsonify` function, which will serialize any supported
|
||||
JSON data type. Or look into Flask community extensions that support
|
||||
more complex applications.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from flask import jsonify
|
||||
|
||||
@app.route("/users")
|
||||
def users_api():
|
||||
users = get_all_users()
|
||||
return jsonify([user.to_json() for user in users])
|
||||
return [user.to_json() for user in users]
|
||||
|
||||
This is a shortcut to passing the data to the
|
||||
:func:`~flask.json.jsonify` function, which will serialize any supported
|
||||
JSON data type. That means that all the data in the dict or list must be
|
||||
JSON serializable.
|
||||
|
||||
For complex types such as database models, you'll want to use a
|
||||
serialization library to convert the data to valid JSON types first.
|
||||
There are many serialization libraries and Flask API extensions
|
||||
maintained by the community that support more complex applications.
|
||||
|
||||
|
||||
.. _sessions:
|
||||
|
|
|
|||
|
|
@ -1,265 +1,6 @@
|
|||
.. currentmodule:: flask
|
||||
:orphan:
|
||||
|
||||
The Request Context
|
||||
===================
|
||||
|
||||
The request context keeps track of the request-level data during a
|
||||
request. Rather than passing the request object to each function that
|
||||
runs during a request, the :data:`request` and :data:`session` proxies
|
||||
are accessed instead.
|
||||
|
||||
This is similar to :doc:`/appcontext`, which keeps track of the
|
||||
application-level data independent of a request. A corresponding
|
||||
application context is pushed when a request context is pushed.
|
||||
|
||||
|
||||
Purpose of the Context
|
||||
----------------------
|
||||
|
||||
When the :class:`Flask` application handles a request, it creates a
|
||||
:class:`Request` object based on the environment it received from the
|
||||
WSGI server. Because a *worker* (thread, process, or coroutine depending
|
||||
on the server) handles only one request at a time, the request data can
|
||||
be considered global to that worker during that request. Flask uses the
|
||||
term *context local* for this.
|
||||
|
||||
Flask automatically *pushes* a request context when handling a request.
|
||||
View functions, error handlers, and other functions that run during a
|
||||
request will have access to the :data:`request` proxy, which points to
|
||||
the request object for the current request.
|
||||
|
||||
|
||||
Lifetime of the Context
|
||||
-----------------------
|
||||
|
||||
When a Flask application begins handling a request, it pushes a request
|
||||
context, which also pushes an :doc:`app context </appcontext>`. When the
|
||||
request ends it pops the request context then the application context.
|
||||
|
||||
The context is unique to each thread (or other worker type).
|
||||
:data:`request` cannot be passed to another thread, the other thread
|
||||
will have a different context stack and will not know about the request
|
||||
the parent thread was pointing to.
|
||||
|
||||
Context locals are implemented in Werkzeug. See :doc:`werkzeug:local`
|
||||
for more information on how this works internally.
|
||||
|
||||
|
||||
Manually Push a Context
|
||||
-----------------------
|
||||
|
||||
If you try to access :data:`request`, or anything that uses it, outside
|
||||
a request context, you'll get this error message:
|
||||
|
||||
.. code-block:: pytb
|
||||
|
||||
RuntimeError: Working outside of request context.
|
||||
|
||||
This typically means that you attempted to use functionality that
|
||||
needed an active HTTP request. Consult the documentation on testing
|
||||
for information about how to avoid this problem.
|
||||
|
||||
This should typically only happen when testing code that expects an
|
||||
active request. One option is to use the
|
||||
:meth:`test client <Flask.test_client>` to simulate a full request. Or
|
||||
you can use :meth:`~Flask.test_request_context` in a ``with`` block, and
|
||||
everything that runs in the block will have access to :data:`request`,
|
||||
populated with your test data. ::
|
||||
|
||||
def generate_report(year):
|
||||
format = request.args.get('format')
|
||||
...
|
||||
|
||||
with app.test_request_context(
|
||||
'/make_report/2017', data={'format': 'short'}):
|
||||
generate_report()
|
||||
|
||||
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.
|
||||
|
||||
For information on how to use the request context from the interactive
|
||||
Python shell, see :doc:`/shell`.
|
||||
|
||||
|
||||
How the Context Works
|
||||
---------------------
|
||||
|
||||
The :meth:`Flask.wsgi_app` method is called to handle each request. It
|
||||
manages the contexts during the request. Internally, the request and
|
||||
application contexts work as stacks, :data:`_request_ctx_stack` and
|
||||
:data:`_app_ctx_stack`. When contexts are pushed onto the stack, the
|
||||
proxies that depend on them are available and point at information from
|
||||
the top context on the stack.
|
||||
|
||||
When the request starts, a :class:`~ctx.RequestContext` is created and
|
||||
pushed, which creates and pushes an :class:`~ctx.AppContext` first if
|
||||
a context for that application is not already the top context. While
|
||||
these contexts are pushed, the :data:`current_app`, :data:`g`,
|
||||
:data:`request`, and :data:`session` proxies are available to the
|
||||
original thread handling the request.
|
||||
|
||||
Because the contexts are stacks, other contexts may be pushed to change
|
||||
the proxies during a request. While this is not a common pattern, it
|
||||
can be used in advanced applications to, for example, do internal
|
||||
redirects or chain different applications together.
|
||||
|
||||
After the request is dispatched and a response is generated and sent,
|
||||
the request context is popped, which then pops the application context.
|
||||
Immediately before they are popped, the :meth:`~Flask.teardown_request`
|
||||
and :meth:`~Flask.teardown_appcontext` functions are executed. These
|
||||
execute even if an unhandled exception occurred during dispatch.
|
||||
|
||||
|
||||
.. _callbacks-and-errors:
|
||||
|
||||
Callbacks and Errors
|
||||
--------------------
|
||||
|
||||
Flask dispatches a request in multiple stages which can affect the
|
||||
request, response, and how errors are handled. The contexts are active
|
||||
during all of these stages.
|
||||
|
||||
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.
|
||||
|
||||
#. Before each request, :meth:`~Flask.before_request` functions are
|
||||
called. If one of these functions return a value, the other
|
||||
functions are skipped. The return value is treated as the response
|
||||
and the view function is not called.
|
||||
|
||||
#. If the :meth:`~Flask.before_request` functions did not return a
|
||||
response, the view function for the matched route is called and
|
||||
returns a response.
|
||||
|
||||
#. The return value of the view is converted into an actual response
|
||||
object and passed to the :meth:`~Flask.after_request`
|
||||
functions. Each function returns a modified or new response object.
|
||||
|
||||
#. After the response is returned, the contexts are popped, which calls
|
||||
the :meth:`~Flask.teardown_request` and
|
||||
:meth:`~Flask.teardown_appcontext` functions. These functions are
|
||||
called even if an unhandled exception was raised at any point above.
|
||||
|
||||
If an exception is raised before the teardown functions, Flask tries to
|
||||
match it with an :meth:`~Flask.errorhandler` function to handle the
|
||||
exception and return a response. If no error handler is found, or the
|
||||
handler itself raises an exception, Flask returns a generic
|
||||
``500 Internal Server Error`` response. The teardown functions are still
|
||||
called, and are passed the exception object.
|
||||
|
||||
If debug mode is enabled, unhandled exceptions are not converted to a
|
||||
``500`` response and instead are propagated to the WSGI server. This
|
||||
allows the development server to present the interactive debugger with
|
||||
the traceback.
|
||||
|
||||
|
||||
Teardown Callbacks
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The teardown callbacks are independent of the request dispatch, and are
|
||||
instead called by the contexts when they are popped. The functions are
|
||||
called even if there is an unhandled exception during dispatch, and for
|
||||
manually pushed contexts. This means there is no guarantee that any
|
||||
other parts of the request dispatch have run first. Be sure to write
|
||||
these functions in a way that does not depend on other callbacks and
|
||||
will not fail.
|
||||
|
||||
During testing, it can be useful to defer popping the contexts after the
|
||||
request ends, so that their data can be accessed in the test function.
|
||||
Use the :meth:`~Flask.test_client` as a ``with`` block to preserve the
|
||||
contexts until the ``with`` block exits.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from flask import Flask, request
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
@app.route('/')
|
||||
def hello():
|
||||
print('during view')
|
||||
return 'Hello, World!'
|
||||
|
||||
@app.teardown_request
|
||||
def show_teardown(exception):
|
||||
print('after with block')
|
||||
|
||||
with app.test_request_context():
|
||||
print('during with block')
|
||||
|
||||
# teardown functions are called after the context with block exits
|
||||
|
||||
with app.test_client() as client:
|
||||
client.get('/')
|
||||
# the contexts are not popped even though the request ended
|
||||
print(request.path)
|
||||
|
||||
# the contexts are popped and teardown functions are called after
|
||||
# the client with block exits
|
||||
|
||||
Signals
|
||||
~~~~~~~
|
||||
|
||||
If :data:`~signals.signals_available` is true, the following signals are
|
||||
sent:
|
||||
|
||||
#. :data:`request_started` is sent before the
|
||||
:meth:`~Flask.before_request` functions are called.
|
||||
|
||||
#. :data:`request_finished` is sent after the
|
||||
:meth:`~Flask.after_request` functions are called.
|
||||
|
||||
#. :data:`got_request_exception` is sent when an exception begins to
|
||||
be handled, but before an :meth:`~Flask.errorhandler` is looked up or
|
||||
called.
|
||||
|
||||
#. :data:`request_tearing_down` is sent after the
|
||||
:meth:`~Flask.teardown_request` functions are called.
|
||||
|
||||
|
||||
Context Preservation on Error
|
||||
-----------------------------
|
||||
|
||||
At the end of a request, the request context is popped and all data
|
||||
associated with it is destroyed. If an error occurs during development,
|
||||
it is useful to delay destroying the data for debugging purposes.
|
||||
|
||||
When the development server is running in development mode (the
|
||||
``FLASK_ENV`` environment variable is set to ``'development'``), the
|
||||
error and data will be preserved and shown in the interactive debugger.
|
||||
|
||||
This behavior can be controlled with the
|
||||
:data:`PRESERVE_CONTEXT_ON_EXCEPTION` config. As described above, it
|
||||
defaults to ``True`` in the development environment.
|
||||
|
||||
Do not enable :data:`PRESERVE_CONTEXT_ON_EXCEPTION` in production, as it
|
||||
will cause your application to leak memory on exceptions.
|
||||
|
||||
|
||||
.. _notes-on-proxies:
|
||||
|
||||
Notes On Proxies
|
||||
----------------
|
||||
|
||||
Some of the objects provided by Flask are proxies to other objects. The
|
||||
proxies are accessed in the same way for each worker thread, but
|
||||
point to the unique object bound to each worker behind the scenes as
|
||||
described on this page.
|
||||
|
||||
Most of the time you don't have to care about that, but there are some
|
||||
exceptions where it is good to know that this object is actually a proxy:
|
||||
|
||||
- The proxy objects cannot fake their type as the actual object types.
|
||||
If you want to perform instance checks, you have to do that on the
|
||||
object being proxied.
|
||||
- The reference to the proxied object is needed in some situations,
|
||||
such as sending :doc:`signals` or passing data to a background
|
||||
thread.
|
||||
|
||||
If you need to access the underlying object that is proxied, use the
|
||||
:meth:`~werkzeug.local.LocalProxy._get_current_object` method::
|
||||
|
||||
app = current_app._get_current_object()
|
||||
my_signal.send(app)
|
||||
Obsolete, see :doc:`/appcontext` instead.
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@
|
|||
Development Server
|
||||
==================
|
||||
|
||||
Flask provides a ``run`` command to run the application with a
|
||||
development server. In development mode, this server provides an
|
||||
interactive debugger and will reload when code is changed.
|
||||
Flask provides a ``run`` command to run the application with a development server. In
|
||||
debug mode, this server provides an interactive debugger and will reload when code is
|
||||
changed.
|
||||
|
||||
.. warning::
|
||||
|
||||
|
|
@ -18,58 +18,18 @@ interactive debugger and will reload when code is changed.
|
|||
Command Line
|
||||
------------
|
||||
|
||||
The ``flask run`` command line script is the recommended way to run the
|
||||
development server. It requires setting the ``FLASK_APP`` environment
|
||||
variable to point to your application, and ``FLASK_ENV=development`` to
|
||||
fully enable development mode.
|
||||
The ``flask run`` CLI command is the recommended way to run the development server. Use
|
||||
the ``--app`` option to point to your application, and the ``--debug`` option to enable
|
||||
debug mode.
|
||||
|
||||
.. tabs::
|
||||
.. code-block:: text
|
||||
|
||||
.. group-tab:: Bash
|
||||
$ flask --app hello run --debug
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ export FLASK_APP=hello
|
||||
$ export FLASK_ENV=development
|
||||
$ flask run
|
||||
|
||||
.. group-tab:: Fish
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ set -x FLASK_APP hello
|
||||
$ export FLASK_ENV=development
|
||||
$ flask run
|
||||
|
||||
.. group-tab:: CMD
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
> set FLASK_APP=hello
|
||||
> set FLASK_ENV=development
|
||||
> flask run
|
||||
|
||||
.. group-tab:: Powershell
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
> $env:FLASK_APP = "hello"
|
||||
> $env:FLASK_ENV = "development"
|
||||
> flask run
|
||||
|
||||
This enables the development environment, including the interactive
|
||||
debugger and reloader, and then starts the server on
|
||||
http://localhost:5000/. Use ``flask run --help`` to see the available
|
||||
options, and :doc:`/cli` for detailed instructions about configuring
|
||||
and using the CLI.
|
||||
|
||||
.. note::
|
||||
|
||||
Prior to Flask 1.0 the ``FLASK_ENV`` environment variable was not
|
||||
supported and you needed to enable debug mode by exporting
|
||||
``FLASK_DEBUG=1``. This can still be used to control debug mode, but
|
||||
you should prefer setting the development environment as shown
|
||||
above.
|
||||
This enables debug mode, including the interactive debugger and reloader, and then
|
||||
starts the server on http://localhost:5000/. Use ``flask run --help`` to see the
|
||||
available options, and :doc:`/cli` for detailed instructions about configuring and using
|
||||
the CLI.
|
||||
|
||||
|
||||
.. _address-already-in-use:
|
||||
|
|
@ -116,44 +76,34 @@ following example shows that process id 6847 is using port 5000.
|
|||
TCP 127.0.0.1:5000 0.0.0.0:0 LISTENING 6847
|
||||
|
||||
macOS Monterey and later automatically starts a service that uses port
|
||||
5000. To disable the service, go to System Preferences, Sharing, and
|
||||
disable "AirPlay Receiver".
|
||||
5000. You can choose to disable this service instead of using a different port by
|
||||
searching for "AirPlay Receiver" in System Settings and toggling it off.
|
||||
|
||||
|
||||
Lazy or Eager Loading
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
Deferred Errors on Reload
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
When using the ``flask run`` command with the reloader, the server will
|
||||
continue to run even if you introduce syntax errors or other
|
||||
initialization errors into the code. Accessing the site will show the
|
||||
interactive debugger for the error, rather than crashing the server.
|
||||
This feature is called "lazy loading".
|
||||
|
||||
If a syntax error is already present when calling ``flask run``, it will
|
||||
fail immediately and show the traceback rather than waiting until the
|
||||
site is accessed. This is intended to make errors more visible initially
|
||||
while still allowing the server to handle errors on reload.
|
||||
|
||||
To override this behavior and always fail immediately, even on reload,
|
||||
pass the ``--eager-loading`` option. To always keep the server running,
|
||||
even on the initial call, pass ``--lazy-loading``.
|
||||
|
||||
|
||||
In Code
|
||||
-------
|
||||
|
||||
As an alternative to the ``flask run`` command, the development server
|
||||
can also be started from Python with the :meth:`Flask.run` method. This
|
||||
method takes arguments similar to the CLI options to control the server.
|
||||
The main difference from the CLI command is that the server will crash
|
||||
if there are errors when reloading.
|
||||
The development server can also be started from Python with the :meth:`Flask.run`
|
||||
method. This method takes arguments similar to the CLI options to control the server.
|
||||
The main difference from the CLI command is that the server will crash if there are
|
||||
errors when reloading. ``debug=True`` can be passed to enable debug mode.
|
||||
|
||||
``debug=True`` can be passed to enable the debugger and reloader, but
|
||||
the ``FLASK_ENV=development`` environment variable is still required to
|
||||
fully enable development mode.
|
||||
|
||||
Place the call in a main block, otherwise it will interfere when trying
|
||||
to import and run the application with a production server later.
|
||||
Place the call in a main block, otherwise it will interfere when trying to import and
|
||||
run the application with a production server later.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
|
|
|
|||
|
|
@ -1,56 +1,37 @@
|
|||
Working with the Shell
|
||||
======================
|
||||
|
||||
.. versionadded:: 0.3
|
||||
One of the reasons everybody loves Python is the interactive shell. It allows
|
||||
you to play around with code in real time and immediately get results back.
|
||||
Flask provides the ``flask shell`` CLI command to start an interactive Python
|
||||
shell with some setup done to make working with the Flask app easier.
|
||||
|
||||
One of the reasons everybody loves Python is the interactive shell. It
|
||||
basically allows you to execute Python commands in real time and
|
||||
immediately get results back. Flask itself does not come with an
|
||||
interactive shell, because it does not require any specific setup upfront,
|
||||
just import your application and start playing around.
|
||||
.. code-block:: text
|
||||
|
||||
There are however some handy helpers to make playing around in the shell a
|
||||
more pleasant experience. The main issue with interactive console
|
||||
sessions is that you're not triggering a request like a browser does which
|
||||
means that :data:`~flask.g`, :data:`~flask.request` and others are not
|
||||
available. But the code you want to test might depend on them, so what
|
||||
can you do?
|
||||
|
||||
This is where some helper functions come in handy. Keep in mind however
|
||||
that these functions are not only there for interactive shell usage, but
|
||||
also for unit testing and other situations that require a faked request
|
||||
context.
|
||||
|
||||
Generally it's recommended that you read :doc:`reqcontext` first.
|
||||
|
||||
Command Line Interface
|
||||
----------------------
|
||||
|
||||
Starting with Flask 0.11 the recommended way to work with the shell is the
|
||||
``flask shell`` command which does a lot of this automatically for you.
|
||||
For instance the shell is automatically initialized with a loaded
|
||||
application context.
|
||||
|
||||
For more information see :doc:`/cli`.
|
||||
$ flask shell
|
||||
|
||||
Creating a Request Context
|
||||
--------------------------
|
||||
|
||||
``flask shell`` pushes an app context automatically, so :data:`.current_app` and
|
||||
:data:`.g` are already available. However, there is no HTTP request being
|
||||
handled in the shell, so :data:`.request` and :data:`.session` are not yet
|
||||
available.
|
||||
|
||||
The easiest way to create a proper request context from the shell is by
|
||||
using the :attr:`~flask.Flask.test_request_context` method which creates
|
||||
us a :class:`~flask.ctx.RequestContext`:
|
||||
|
||||
>>> ctx = app.test_request_context()
|
||||
|
||||
Normally you would use the ``with`` statement to make this request object
|
||||
active, but in the shell it's easier to use the
|
||||
:meth:`~flask.ctx.RequestContext.push` and
|
||||
:meth:`~flask.ctx.RequestContext.pop` methods by hand:
|
||||
Normally you would use the ``with`` statement to make this context active, but
|
||||
in the shell it's easier to call :meth:`~.RequestContext.push` and
|
||||
:meth:`~.RequestContext.pop` manually:
|
||||
|
||||
>>> ctx.push()
|
||||
|
||||
From that point onwards you can work with the request object until you
|
||||
call `pop`:
|
||||
From that point onwards you can work with the request object until you call
|
||||
``pop``:
|
||||
|
||||
>>> ctx.pop()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,33 +1,28 @@
|
|||
Signals
|
||||
=======
|
||||
|
||||
.. versionadded:: 0.6
|
||||
Signals are a lightweight way to notify subscribers of certain events during the
|
||||
lifecycle of the application and each request. When an event occurs, it emits the
|
||||
signal, which calls each subscriber.
|
||||
|
||||
Starting with Flask 0.6, there is integrated support for signalling in
|
||||
Flask. This support is provided by the excellent `blinker`_ library and
|
||||
will gracefully fall back if it is not available.
|
||||
Signals are implemented by the `Blinker`_ library. See its documentation for detailed
|
||||
information. Flask provides some built-in signals. Extensions may provide their own.
|
||||
|
||||
What are signals? Signals help you decouple applications by sending
|
||||
notifications when actions occur elsewhere in the core framework or
|
||||
another Flask extensions. In short, signals allow certain senders to
|
||||
notify subscribers that something happened.
|
||||
Many signals mirror Flask's decorator-based callbacks with similar names. For example,
|
||||
the :data:`.request_started` signal is similar to the :meth:`~.Flask.before_request`
|
||||
decorator. The advantage of signals over handlers is that they can be subscribed to
|
||||
temporarily, and can't directly affect the application. This is useful for testing,
|
||||
metrics, auditing, and more. For example, if you want to know what templates were
|
||||
rendered at what parts of what requests, there is a signal that will notify you of that
|
||||
information.
|
||||
|
||||
Flask comes with a couple of signals and other extensions might provide
|
||||
more. Also keep in mind that signals are intended to notify subscribers
|
||||
and should not encourage subscribers to modify data. You will notice that
|
||||
there are signals that appear to do the same thing like some of the
|
||||
builtin decorators do (eg: :data:`~flask.request_started` is very similar
|
||||
to :meth:`~flask.Flask.before_request`). However, there are differences in
|
||||
how they work. The core :meth:`~flask.Flask.before_request` handler, for
|
||||
example, is executed in a specific order and is able to abort the request
|
||||
early by returning a response. In contrast all signal handlers are
|
||||
executed in undefined order and do not modify any data.
|
||||
|
||||
The big advantage of signals over handlers is that you can safely
|
||||
subscribe to them for just a split second. These temporary
|
||||
subscriptions are helpful for unit testing for example. Say you want to
|
||||
know what templates were rendered as part of a request: signals allow you
|
||||
to do exactly that.
|
||||
Core Signals
|
||||
------------
|
||||
|
||||
See :ref:`core-signals-list` for a list of all built-in signals. The :doc:`lifecycle`
|
||||
page also describes the order that signals and decorators execute.
|
||||
|
||||
|
||||
Subscribing to Signals
|
||||
----------------------
|
||||
|
|
@ -99,17 +94,12 @@ The example above would then look like this::
|
|||
...
|
||||
template, context = templates[0]
|
||||
|
||||
.. admonition:: Blinker API Changes
|
||||
|
||||
The :meth:`~blinker.base.Signal.connected_to` method arrived in Blinker
|
||||
with version 1.1.
|
||||
|
||||
Creating Signals
|
||||
----------------
|
||||
|
||||
If you want to use signals in your own application, you can use the
|
||||
blinker library directly. The most common use case are named signals in a
|
||||
custom :class:`~blinker.base.Namespace`.. This is what is recommended
|
||||
custom :class:`~blinker.base.Namespace`. This is what is recommended
|
||||
most of the time::
|
||||
|
||||
from blinker import Namespace
|
||||
|
|
@ -123,12 +113,6 @@ The name for the signal here makes it unique and also simplifies
|
|||
debugging. You can access the name of the signal with the
|
||||
:attr:`~blinker.base.NamedSignal.name` attribute.
|
||||
|
||||
.. admonition:: For Extension Developers
|
||||
|
||||
If you are writing a Flask extension and you want to gracefully degrade for
|
||||
missing blinker installations, you can do so by using the
|
||||
:class:`flask.signals.Namespace` class.
|
||||
|
||||
.. _signals-sending:
|
||||
|
||||
Sending Signals
|
||||
|
|
@ -160,17 +144,16 @@ function, you can pass ``current_app._get_current_object()`` as sender.
|
|||
Signals and Flask's Request Context
|
||||
-----------------------------------
|
||||
|
||||
Signals fully support :doc:`reqcontext` when receiving signals.
|
||||
Context-local variables are consistently available between
|
||||
:data:`~flask.request_started` and :data:`~flask.request_finished`, so you can
|
||||
rely on :class:`flask.g` and others as needed. Note the limitations described
|
||||
in :ref:`signals-sending` and the :data:`~flask.request_tearing_down` signal.
|
||||
Context-local proxies are available between :data:`~flask.request_started` and
|
||||
:data:`~flask.request_finished`, so you can rely on :class:`flask.g` and others
|
||||
as needed. Note the limitations described in :ref:`signals-sending` and the
|
||||
:data:`~flask.request_tearing_down` signal.
|
||||
|
||||
|
||||
Decorator Based Signal Subscriptions
|
||||
------------------------------------
|
||||
|
||||
With Blinker 1.1 you can also easily subscribe to signals by using the new
|
||||
You can also easily subscribe to signals by using the
|
||||
:meth:`~blinker.base.NamedSignal.connect_via` decorator::
|
||||
|
||||
from flask import template_rendered
|
||||
|
|
@ -179,10 +162,5 @@ With Blinker 1.1 you can also easily subscribe to signals by using the new
|
|||
def when_template_rendered(sender, template, context, **extra):
|
||||
print(f'Template {template.name} is rendered with {context}')
|
||||
|
||||
Core Signals
|
||||
------------
|
||||
|
||||
Take a look at :ref:`core-signals-list` for a list of all builtin signals.
|
||||
|
||||
|
||||
.. _blinker: https://pypi.org/project/blinker/
|
||||
|
|
|
|||
|
|
@ -1,37 +1,37 @@
|
|||
Templates
|
||||
=========
|
||||
|
||||
Flask leverages Jinja2 as its template engine. You are obviously free to use
|
||||
a different template engine, but you still have to install Jinja2 to run
|
||||
Flask leverages Jinja as its template engine. You are obviously free to use
|
||||
a different template engine, but you still have to install Jinja to run
|
||||
Flask itself. This requirement is necessary to enable rich extensions.
|
||||
An extension can depend on Jinja2 being present.
|
||||
An extension can depend on Jinja being present.
|
||||
|
||||
This section only gives a very quick introduction into how Jinja2
|
||||
This section only gives a very quick introduction into how Jinja
|
||||
is integrated into Flask. If you want information on the template
|
||||
engine's syntax itself, head over to the official `Jinja2 Template
|
||||
engine's syntax itself, head over to the official `Jinja Template
|
||||
Documentation <https://jinja.palletsprojects.com/templates/>`_ for
|
||||
more information.
|
||||
|
||||
Jinja Setup
|
||||
-----------
|
||||
|
||||
Unless customized, Jinja2 is configured by Flask as follows:
|
||||
Unless customized, Jinja is configured by Flask as follows:
|
||||
|
||||
- autoescaping is enabled for all templates ending in ``.html``,
|
||||
``.htm``, ``.xml`` as well as ``.xhtml`` when using
|
||||
``.htm``, ``.xml``, ``.xhtml``, as well as ``.svg`` when using
|
||||
:func:`~flask.templating.render_template`.
|
||||
- autoescaping is enabled for all strings when using
|
||||
:func:`~flask.templating.render_template_string`.
|
||||
- a template has the ability to opt in/out autoescaping with the
|
||||
``{% autoescape %}`` tag.
|
||||
- Flask inserts a couple of global functions and helpers into the
|
||||
Jinja2 context, additionally to the values that are present by
|
||||
Jinja context, additionally to the values that are present by
|
||||
default.
|
||||
|
||||
Standard Context
|
||||
----------------
|
||||
|
||||
The following global variables are available within Jinja2 templates
|
||||
The following global variables are available within Jinja templates
|
||||
by default:
|
||||
|
||||
.. data:: config
|
||||
|
|
@ -115,7 +115,7 @@ markdown to HTML converter.
|
|||
|
||||
There are three ways to accomplish that:
|
||||
|
||||
- In the Python code, wrap the HTML string in a :class:`~flask.Markup`
|
||||
- In the Python code, wrap the HTML string in a :class:`~markupsafe.Markup`
|
||||
object before passing it to the template. This is in general the
|
||||
recommended way.
|
||||
- Inside the template, use the ``|safe`` filter to explicitly mark a
|
||||
|
|
@ -137,32 +137,58 @@ using in this block.
|
|||
|
||||
.. _registering-filters:
|
||||
|
||||
Registering Filters
|
||||
-------------------
|
||||
Registering Filters, Tests, and Globals
|
||||
---------------------------------------
|
||||
|
||||
If you want to register your own filters in Jinja2 you have two ways to do
|
||||
that. You can either put them by hand into the
|
||||
:attr:`~flask.Flask.jinja_env` of the application or use the
|
||||
:meth:`~flask.Flask.template_filter` decorator.
|
||||
The Flask app and blueprints provide decorators and methods to register your own
|
||||
filters, tests, and global functions for use in Jinja templates. They all follow
|
||||
the same pattern, so the following examples only discuss filters.
|
||||
|
||||
The two following examples work the same and both reverse an object::
|
||||
Decorate a function with :meth:`~.Flask.template_filter` to register it as a
|
||||
template filter.
|
||||
|
||||
@app.template_filter('reverse')
|
||||
def reverse_filter(s):
|
||||
return s[::-1]
|
||||
.. code-block:: python
|
||||
|
||||
def reverse_filter(s):
|
||||
return s[::-1]
|
||||
app.jinja_env.filters['reverse'] = reverse_filter
|
||||
@app.template_filter
|
||||
def reverse(s):
|
||||
return reversed(s)
|
||||
|
||||
In case of the decorator the argument is optional if you want to use the
|
||||
function name as name of the filter. Once registered, you can use the filter
|
||||
in your templates in the same way as Jinja2's builtin filters, for example if
|
||||
you have a Python list in context called `mylist`::
|
||||
.. code-block:: jinja
|
||||
|
||||
{% for x in mylist | reverse %}
|
||||
{% for item in data | reverse %}
|
||||
{% endfor %}
|
||||
|
||||
By default it will use the name of the function as the name of the filter, but
|
||||
that can be changed by passing a name to the decorator.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@app.template_filter("reverse")
|
||||
def reverse_filter(s):
|
||||
return reversed(s)
|
||||
|
||||
A filter can be registered separately using :meth:`~.Flask.add_template_filter`.
|
||||
The name is optional and will use the function name if not given.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def reverse_filter(s):
|
||||
return reversed(s)
|
||||
|
||||
app.add_template_filter(reverse_filter, "reverse")
|
||||
|
||||
For template tests, use the :meth:`~.Flask.template_test` decorator or
|
||||
:meth:`~.Flask.add_template_test` method. For template global functions, use the
|
||||
:meth:`~.Flask.template_global` decorator or :meth:`~.Flask.add_template_global`
|
||||
method.
|
||||
|
||||
The same methods also exist on :class:`.Blueprint`, prefixed with ``app_`` to
|
||||
indicate that the registered functions will be available to all templates, not
|
||||
only when rendering from within the blueprint.
|
||||
|
||||
The Jinja environment is also available as :attr:`~.Flask.jinja_env`. It may be
|
||||
modified directly, as you would when using Jinja outside Flask.
|
||||
|
||||
|
||||
Context Processors
|
||||
------------------
|
||||
|
|
@ -201,3 +227,35 @@ templates::
|
|||
You could also build `format_price` as a template filter (see
|
||||
:ref:`registering-filters`), but this demonstrates how to pass functions in a
|
||||
context processor.
|
||||
|
||||
Streaming
|
||||
---------
|
||||
|
||||
It can be useful to not render the whole template as one complete
|
||||
string, instead render it as a stream, yielding smaller incremental
|
||||
strings. This can be used for streaming HTML in chunks to speed up
|
||||
initial page load, or to save memory when rendering a very large
|
||||
template.
|
||||
|
||||
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.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from flask import stream_template
|
||||
|
||||
@app.get("/timeline")
|
||||
def timeline():
|
||||
return stream_template("timeline.html")
|
||||
|
||||
These functions automatically apply the
|
||||
:func:`~flask.stream_with_context` wrapper if a request is active, so that
|
||||
:data:`.request`, :data:`.session`, and :data:`.g` remain available in the
|
||||
template.
|
||||
|
||||
More headers cannot be sent after the body has begun. Therefore, you must
|
||||
make sure all headers are set before starting the response. In particular,
|
||||
if the template will access ``session``, be sure to do so in the view as
|
||||
well so that the ``Vary: cookie`` header will be set.
|
||||
|
|
|
|||
|
|
@ -109,10 +109,8 @@ provides ``response.text``, or use ``response.get_data(as_text=True)``.
|
|||
|
||||
|
||||
Pass a dict ``query_string={"key": "value", ...}`` to set arguments in
|
||||
the query string (after the ``?`` in the URL). You can also pass a
|
||||
string if you want to set a specific value directly.
|
||||
|
||||
Pass a dict to ``headers={}`` to set request headers.
|
||||
the query string (after the ``?`` in the URL). Pass a dict
|
||||
``headers={}`` to set request headers.
|
||||
|
||||
To send a request body in a POST or PUT request, pass a value to
|
||||
``data``. If raw bytes are passed, that exact body is used. Usually,
|
||||
|
|
@ -194,7 +192,7 @@ which records the request that produced that response.
|
|||
.. code-block:: python
|
||||
|
||||
def test_logout_redirect(client):
|
||||
response = client.get("/logout")
|
||||
response = client.get("/logout", follow_redirects=True)
|
||||
# Check that there was one redirect response.
|
||||
assert len(response.history) == 1
|
||||
# Check that the second request was to the index page.
|
||||
|
|
@ -267,21 +265,20 @@ command from the command line.
|
|||
click.echo(f"Hello, {name}!")
|
||||
|
||||
def test_hello_command(runner):
|
||||
result = runner.invoke(["hello"])
|
||||
result = runner.invoke(args="hello")
|
||||
assert "World" in result.output
|
||||
|
||||
result = runner.invoke(["hello", "--name", "Flask"])
|
||||
result = runner.invoke(args=["hello", "--name", "Flask"])
|
||||
assert "Flask" in result.output
|
||||
|
||||
|
||||
Tests that depend on an Active Context
|
||||
--------------------------------------
|
||||
|
||||
You may have functions that are called from views or commands, that
|
||||
expect an active :doc:`application context </appcontext>` or
|
||||
:doc:`request context </reqcontext>` because they access ``request``,
|
||||
``session``, or ``current_app``. Rather than testing them by making a
|
||||
request or invoking the command, you can create and activate a context
|
||||
You may have functions that are called from views or commands, that expect an
|
||||
active :doc:`app context </appcontext>` because they access :data:`.request`,
|
||||
:data:`.session`, :data:`.g`, or :data:`.current_app`. Rather than testing them by
|
||||
making a request or invoking the command, you can create and activate a context
|
||||
directly.
|
||||
|
||||
Use ``with app.app_context()`` to push an application context. For
|
||||
|
|
|
|||
|
|
@ -305,7 +305,7 @@ The pattern ``{{ request.form['title'] or post['title'] }}`` is used to
|
|||
choose what data appears in the form. When the form hasn't been
|
||||
submitted, the original ``post`` data appears, but if invalid form data
|
||||
was posted you want to display that so the user can fix the error, so
|
||||
``request.form`` is used instead. :data:`request` is another variable
|
||||
``request.form`` is used instead. :data:`.request` is another variable
|
||||
that's automatically available in templates.
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -37,10 +37,10 @@ response is sent.
|
|||
:caption: ``flaskr/db.py``
|
||||
|
||||
import sqlite3
|
||||
from datetime import datetime
|
||||
|
||||
import click
|
||||
from flask import current_app, g
|
||||
from flask.cli import with_appcontext
|
||||
|
||||
|
||||
def get_db():
|
||||
|
|
@ -60,17 +60,17 @@ response is sent.
|
|||
if db is not None:
|
||||
db.close()
|
||||
|
||||
:data:`g` is a special object that is unique for each request. It is
|
||||
:data:`.g` is a special object that is unique for each request. It is
|
||||
used to store data that might be accessed by multiple functions during
|
||||
the request. The connection is stored and reused instead of creating a
|
||||
new connection if ``get_db`` is called a second time in the same
|
||||
request.
|
||||
|
||||
:data:`current_app` is another special object that points to the Flask
|
||||
:data:`.current_app` is another special object that points to the Flask
|
||||
application handling the request. Since you used an application factory,
|
||||
there is no application object when writing the rest of your code.
|
||||
``get_db`` will be called when the application has been created and is
|
||||
handling a request, so :data:`current_app` can be used.
|
||||
handling a request, so :data:`.current_app` can be used.
|
||||
|
||||
:func:`sqlite3.connect` establishes a connection to the file pointed at
|
||||
by the ``DATABASE`` configuration key. This file doesn't have to exist
|
||||
|
|
@ -128,12 +128,16 @@ Add the Python functions that will run these SQL commands to the
|
|||
|
||||
|
||||
@click.command('init-db')
|
||||
@with_appcontext
|
||||
def init_db_command():
|
||||
"""Clear the existing data and create new tables."""
|
||||
init_db()
|
||||
click.echo('Initialized the database.')
|
||||
|
||||
|
||||
sqlite3.register_converter(
|
||||
"timestamp", lambda v: datetime.fromisoformat(v.decode())
|
||||
)
|
||||
|
||||
:meth:`open_resource() <Flask.open_resource>` opens a file relative to
|
||||
the ``flaskr`` package, which is useful since you won't necessarily know
|
||||
where that location is when deploying the application later. ``get_db``
|
||||
|
|
@ -144,6 +148,10 @@ read from the file.
|
|||
that calls the ``init_db`` function and shows a success message to the
|
||||
user. You can read :doc:`/cli` to learn more about writing commands.
|
||||
|
||||
The call to :func:`sqlite3.register_converter` tells Python how to
|
||||
interpret timestamp values in the database. We convert the value to a
|
||||
:class:`datetime.datetime`.
|
||||
|
||||
|
||||
Register with the Application
|
||||
-----------------------------
|
||||
|
|
@ -196,15 +204,13 @@ previous page.
|
|||
If you're still running the server from the previous page, you can
|
||||
either stop the server, or run this command in a new terminal. If
|
||||
you use a new terminal, remember to change to your project directory
|
||||
and activate the env as described in :doc:`/installation`. You'll
|
||||
also need to set ``FLASK_APP`` and ``FLASK_ENV`` as shown on the
|
||||
previous page.
|
||||
and activate the env as described in :doc:`/installation`.
|
||||
|
||||
Run the ``init-db`` command:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
$ flask init-db
|
||||
$ flask --app flaskr init-db
|
||||
Initialized the database.
|
||||
|
||||
There will now be a ``flaskr.sqlite`` file in the ``instance`` folder in
|
||||
|
|
|
|||
|
|
@ -14,22 +14,13 @@ application.
|
|||
Build and Install
|
||||
-----------------
|
||||
|
||||
When you want to deploy your application elsewhere, you build a
|
||||
distribution file. The current standard for Python distribution is the
|
||||
*wheel* format, with the ``.whl`` extension. Make sure the wheel library
|
||||
is installed first:
|
||||
When you want to deploy your application elsewhere, you build a *wheel*
|
||||
(``.whl``) file. Install and use the ``build`` tool to do this.
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
$ pip install wheel
|
||||
|
||||
Running ``setup.py`` with Python gives you a command line tool to issue
|
||||
build-related commands. The ``bdist_wheel`` command will build a wheel
|
||||
distribution file.
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
$ python setup.py bdist_wheel
|
||||
$ pip install build
|
||||
$ python -m build --wheel
|
||||
|
||||
You can find the file in ``dist/flaskr-1.0.0-py3-none-any.whl``. The
|
||||
file name is in the format of {project name}-{version}-{python tag}
|
||||
|
|
@ -48,39 +39,13 @@ Pip will install your project along with its dependencies.
|
|||
Since this is a different machine, you need to run ``init-db`` again to
|
||||
create the database in the instance folder.
|
||||
|
||||
.. tabs::
|
||||
.. code-block:: text
|
||||
|
||||
.. group-tab:: Bash
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ export FLASK_APP=flaskr
|
||||
$ flask init-db
|
||||
|
||||
.. group-tab:: Fish
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ set -x FLASK_APP flaskr
|
||||
$ flask init-db
|
||||
|
||||
.. group-tab:: CMD
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
> set FLASK_APP=flaskr
|
||||
> flask init-db
|
||||
|
||||
.. group-tab:: Powershell
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
> $env:FLASK_APP = "flaskr"
|
||||
> flask init-db
|
||||
$ flask --app flaskr init-db
|
||||
|
||||
When Flask detects that it's installed (not in editable mode), it uses
|
||||
a different directory for the instance folder. You can find it at
|
||||
``venv/var/flaskr-instance`` instead.
|
||||
``.venv/var/flaskr-instance`` instead.
|
||||
|
||||
|
||||
Configure the Secret Key
|
||||
|
|
@ -103,7 +68,7 @@ Create the ``config.py`` file in the instance folder, which the factory
|
|||
will read from if it exists. Copy the generated value into it.
|
||||
|
||||
.. code-block:: python
|
||||
:caption: ``venv/var/flaskr-instance/config.py``
|
||||
:caption: ``.venv/var/flaskr-instance/config.py``
|
||||
|
||||
SECRET_KEY = '192b9bdd22ab9ed4d12e236c78afcb9a393ec15f71bbf5dc987d54727823bcbf'
|
||||
|
||||
|
|
@ -127,7 +92,7 @@ first install it in the virtual environment:
|
|||
$ pip install waitress
|
||||
|
||||
You need to tell Waitress about your application, but it doesn't use
|
||||
``FLASK_APP`` like ``flask run`` does. You need to tell it to import and
|
||||
``--app`` like ``flask run`` does. You need to tell it to import and
|
||||
call the application factory to get an application object.
|
||||
|
||||
.. code-block:: none
|
||||
|
|
|
|||
|
|
@ -56,10 +56,7 @@ directory should be treated as a package.
|
|||
app.config.from_mapping(test_config)
|
||||
|
||||
# ensure the instance folder exists
|
||||
try:
|
||||
os.makedirs(app.instance_path)
|
||||
except OSError:
|
||||
pass
|
||||
os.makedirs(app.instance_path, exist_ok=True)
|
||||
|
||||
# a simple page that says hello
|
||||
@app.route('/hello')
|
||||
|
|
@ -127,59 +124,28 @@ Run The Application
|
|||
|
||||
Now you can run your application using the ``flask`` command. From the
|
||||
terminal, tell Flask where to find your application, then run it in
|
||||
development mode. Remember, you should still be in the top-level
|
||||
debug mode. Remember, you should still be in the top-level
|
||||
``flask-tutorial`` directory, not the ``flaskr`` package.
|
||||
|
||||
Development mode shows an interactive debugger whenever a page raises an
|
||||
Debug mode shows an interactive debugger whenever a page raises an
|
||||
exception, and restarts the server whenever you make changes to the
|
||||
code. You can leave it running and just reload the browser page as you
|
||||
follow the tutorial.
|
||||
|
||||
.. tabs::
|
||||
.. code-block:: text
|
||||
|
||||
.. group-tab:: Bash
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ export FLASK_APP=flaskr
|
||||
$ export FLASK_ENV=development
|
||||
$ flask run
|
||||
|
||||
.. group-tab:: Fish
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ set -x FLASK_APP flaskr
|
||||
$ set -x FLASK_ENV development
|
||||
$ flask run
|
||||
|
||||
.. group-tab:: CMD
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
> set FLASK_APP=flaskr
|
||||
> set FLASK_ENV=development
|
||||
> flask run
|
||||
|
||||
.. group-tab:: Powershell
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
> $env:FLASK_APP = "flaskr"
|
||||
> $env:FLASK_ENV = "development"
|
||||
> flask run
|
||||
$ flask --app flaskr run --debug
|
||||
|
||||
You'll see output similar to this:
|
||||
|
||||
.. code-block:: none
|
||||
.. code-block:: text
|
||||
|
||||
* Serving Flask app "flaskr"
|
||||
* Environment: development
|
||||
* Debug mode: on
|
||||
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
|
||||
* Restarting with stat
|
||||
* Debugger is active!
|
||||
* Debugger PIN: 855-212-761
|
||||
* Debugger PIN: nnn-nnn-nnn
|
||||
|
||||
Visit http://127.0.0.1:5000/hello in a browser and you should see the
|
||||
"Hello, World!" message. Congratulations, you're now running your Flask
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
Make the Project Installable
|
||||
============================
|
||||
|
||||
Making your project installable means that you can build a
|
||||
*distribution* file and install that in another environment, just like
|
||||
you installed Flask in your project's environment. This makes deploying
|
||||
your project the same as installing any other library, so you're using
|
||||
all the standard Python tools to manage everything.
|
||||
Making your project installable means that you can build a *wheel* file and install that
|
||||
in another environment, just like you installed Flask in your project's environment.
|
||||
This makes deploying your project the same as installing any other library, so you're
|
||||
using all the standard Python tools to manage everything.
|
||||
|
||||
Installing also comes with other benefits that might not be obvious from
|
||||
the tutorial or as a new Python user, including:
|
||||
|
|
@ -28,49 +27,27 @@ the tutorial or as a new Python user, including:
|
|||
Describe the Project
|
||||
--------------------
|
||||
|
||||
The ``setup.py`` file describes your project and the files that belong
|
||||
to it.
|
||||
The ``pyproject.toml`` file describes your project and how to build it.
|
||||
|
||||
.. code-block:: python
|
||||
:caption: ``setup.py``
|
||||
.. code-block:: toml
|
||||
:caption: ``pyproject.toml``
|
||||
|
||||
from setuptools import find_packages, setup
|
||||
[project]
|
||||
name = "flaskr"
|
||||
version = "1.0.0"
|
||||
description = "The basic blog app built in the Flask tutorial."
|
||||
dependencies = [
|
||||
"flask",
|
||||
]
|
||||
|
||||
setup(
|
||||
name='flaskr',
|
||||
version='1.0.0',
|
||||
packages=find_packages(),
|
||||
include_package_data=True,
|
||||
zip_safe=False,
|
||||
install_requires=[
|
||||
'flask',
|
||||
],
|
||||
)
|
||||
[build-system]
|
||||
requires = ["flit_core<4"]
|
||||
build-backend = "flit_core.buildapi"
|
||||
|
||||
See the official `Packaging tutorial <packaging tutorial_>`_ for more
|
||||
explanation of the files and options used.
|
||||
|
||||
``packages`` tells Python what package directories (and the Python files
|
||||
they contain) to include. ``find_packages()`` finds these directories
|
||||
automatically so you don't have to type them out. To include other
|
||||
files, such as the static and templates directories,
|
||||
``include_package_data`` is set. Python needs another file named
|
||||
``MANIFEST.in`` to tell what this other data is.
|
||||
|
||||
.. code-block:: none
|
||||
:caption: ``MANIFEST.in``
|
||||
|
||||
include flaskr/schema.sql
|
||||
graft flaskr/static
|
||||
graft flaskr/templates
|
||||
global-exclude *.pyc
|
||||
|
||||
This tells Python to copy everything in the ``static`` and ``templates``
|
||||
directories, and the ``schema.sql`` file, but to exclude all bytecode
|
||||
files.
|
||||
|
||||
See the `official packaging guide`_ for another explanation of the files
|
||||
and options used.
|
||||
|
||||
.. _official packaging guide: https://packaging.python.org/tutorials/packaging-projects/
|
||||
.. _packaging tutorial: https://packaging.python.org/tutorials/packaging-projects/
|
||||
|
||||
|
||||
Install the Project
|
||||
|
|
@ -82,10 +59,10 @@ Use ``pip`` to install your project in the virtual environment.
|
|||
|
||||
$ pip install -e .
|
||||
|
||||
This tells pip to find ``setup.py`` in the current directory and install
|
||||
it in *editable* or *development* mode. Editable mode means that as you
|
||||
make changes to your local code, you'll only need to re-install if you
|
||||
change the metadata about the project, such as its dependencies.
|
||||
This tells pip to find ``pyproject.toml`` in the current directory and install the
|
||||
project in *editable* or *development* mode. Editable mode means that as you make
|
||||
changes to your local code, you'll only need to re-install if you change the metadata
|
||||
about the project, such as its dependencies.
|
||||
|
||||
You can observe that the project is now installed with ``pip list``.
|
||||
|
||||
|
|
@ -102,12 +79,10 @@ You can observe that the project is now installed with ``pip list``.
|
|||
Jinja2 2.10
|
||||
MarkupSafe 1.0
|
||||
pip 9.0.3
|
||||
setuptools 39.0.1
|
||||
Werkzeug 0.14.1
|
||||
wheel 0.30.0
|
||||
|
||||
Nothing changes from how you've been running your project so far.
|
||||
``FLASK_APP`` is still set to ``flaskr`` and ``flask run`` still runs
|
||||
``--app`` is still set to ``flaskr`` and ``flask run`` still runs
|
||||
the application, but you can call it from anywhere, not just the
|
||||
``flask-tutorial`` directory.
|
||||
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ The project directory will contain:
|
|||
* ``flaskr/``, a Python package containing your application code and
|
||||
files.
|
||||
* ``tests/``, a directory containing test modules.
|
||||
* ``venv/``, a Python virtual environment where Flask and other
|
||||
* ``.venv/``, a Python virtual environment where Flask and other
|
||||
dependencies are installed.
|
||||
* Installation files telling Python how to install your project.
|
||||
* Version control config, such as `git`_. You should make a habit of
|
||||
|
|
@ -80,9 +80,8 @@ By the end, your project layout will look like this:
|
|||
│ ├── test_db.py
|
||||
│ ├── test_auth.py
|
||||
│ └── test_blog.py
|
||||
├── venv/
|
||||
├── setup.py
|
||||
└── MANIFEST.in
|
||||
├── .venv/
|
||||
└── pyproject.toml
|
||||
|
||||
If you're using version control, the following files that are generated
|
||||
while running your project should be ignored. There may be other files
|
||||
|
|
@ -92,7 +91,7 @@ write. For example, with git:
|
|||
.. code-block:: none
|
||||
:caption: ``.gitignore``
|
||||
|
||||
venv/
|
||||
.venv/
|
||||
|
||||
*.pyc
|
||||
__pycache__/
|
||||
|
|
@ -103,8 +102,4 @@ write. For example, with git:
|
|||
.coverage
|
||||
htmlcov/
|
||||
|
||||
dist/
|
||||
build/
|
||||
*.egg-info/
|
||||
|
||||
Continue to :doc:`factory`.
|
||||
|
|
|
|||