mirror of
https://github.com/fastapi/fastapi.git
synced 2025-12-25 07:08:11 -05:00
Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5a6e47bd49 | ||
|
|
58872dca74 | ||
|
|
9b04593260 | ||
|
|
6d77e2ac5f | ||
|
|
b16ca54c30 | ||
|
|
834723cf2c | ||
|
|
9778542ba6 | ||
|
|
34c34c68d2 | ||
|
|
c64f8346ae | ||
|
|
4f852878d6 | ||
|
|
59bc4b7d69 | ||
|
|
3cae2ccbae | ||
|
|
e21ba7646a | ||
|
|
10498fcfbd | ||
|
|
3f7b7837fb | ||
|
|
1c26e77a66 | ||
|
|
108c2f3c0e | ||
|
|
f2fd948ce3 | ||
|
|
b269655b7f | ||
|
|
a174f01901 | ||
|
|
9b76ad1870 | ||
|
|
f1c367aead | ||
|
|
8291c664b9 | ||
|
|
e8472ebbd1 | ||
|
|
f4391e2a87 | ||
|
|
11c755bee3 | ||
|
|
35054a450c | ||
|
|
da60de33c1 | ||
|
|
c0758dfe71 | ||
|
|
1112ac7538 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -11,3 +11,4 @@ site
|
||||
coverage.xml
|
||||
.netlify
|
||||
test.db
|
||||
log.txt
|
||||
|
||||
3
Pipfile
3
Pipfile
@@ -22,10 +22,11 @@ ujson = "*"
|
||||
flake8 = "*"
|
||||
python-multipart = "*"
|
||||
sqlalchemy = "*"
|
||||
uvicorn = "*"
|
||||
|
||||
[packages]
|
||||
starlette = "==0.11.1"
|
||||
pydantic = "==0.18.2"
|
||||
pydantic = "==0.21.0"
|
||||
|
||||
[requires]
|
||||
python_version = "3.6"
|
||||
|
||||
302
Pipfile.lock
generated
302
Pipfile.lock
generated
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "6b55a2dcce8b6bd5a1be8f170acb18478149218a01d1b026981a6297800cd3fa"
|
||||
"sha256": "676c6ae13691eef64abe6638f833cb8a330612521d3fad08718b240328b4877a"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
@@ -26,11 +26,11 @@
|
||||
},
|
||||
"pydantic": {
|
||||
"hashes": [
|
||||
"sha256:9f023811b6cefd203c5fd8fd15a4152f04e79e531b8f676ab1244dfe06ce8024",
|
||||
"sha256:edbb08b561feda505374c0f25e4b54466a0a0c702ed6b2efaabdc3890d1a82e7"
|
||||
"sha256:93fa585402e7c8c01623ea8af6ca23363e8b4c6a020b7a2de9e99fa29d642d50",
|
||||
"sha256:eb441dd50779347a450494c437db3ecbb13c1f3854497df879662782af516c5c"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.18.2"
|
||||
"version": "==0.21.0"
|
||||
},
|
||||
"starlette": {
|
||||
"hashes": [
|
||||
@@ -57,10 +57,10 @@
|
||||
},
|
||||
"attrs": {
|
||||
"hashes": [
|
||||
"sha256:10cbf6e27dbce8c30807caf056c8eb50917e0eaafe86347671b57254006c3e69",
|
||||
"sha256:ca4be454458f9dec299268d472aaa5a11f67a4ff70093396e1ceae9c76cf4bbb"
|
||||
"sha256:69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79",
|
||||
"sha256:f0b870f674851ecbfbbbd364d6b5cbdff9dcedbc7f3f5e18a6891057f21fe399"
|
||||
],
|
||||
"version": "==18.2.0"
|
||||
"version": "==19.1.0"
|
||||
},
|
||||
"autoflake": {
|
||||
"hashes": [
|
||||
@@ -86,11 +86,11 @@
|
||||
},
|
||||
"black": {
|
||||
"hashes": [
|
||||
"sha256:817243426042db1d36617910df579a54f1afd659adb96fc5032fcf4b36209739",
|
||||
"sha256:e030a9a28f542debc08acceb273f228ac422798e5215ba2a791a6ddeaaca22a5"
|
||||
"sha256:09a9dcb7c46ed496a9850b76e4e825d6049ecd38b611f1224857a79bd985a8cf",
|
||||
"sha256:68950ffd4d9169716bcb8719a56c07a2f4485354fec061cdd5910aa07369731c"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==18.9b0"
|
||||
"version": "==19.3b0"
|
||||
},
|
||||
"bleach": {
|
||||
"hashes": [
|
||||
@@ -101,10 +101,10 @@
|
||||
},
|
||||
"certifi": {
|
||||
"hashes": [
|
||||
"sha256:47f9c83ef4c0c621eaef743f133f09fa8a74a9b75f037e8624f83bd1b6626cb7",
|
||||
"sha256:993f830721089fef441cdfeb4b2c8c9df86f0c63239f06bd025a76a7daddb033"
|
||||
"sha256:59b7658e26ca9c7339e00f8f4636cdfe59d34fa37b9b04f6f9e9926b3cece1a5",
|
||||
"sha256:b26104d6835d1f5e49452a26eb2ff87fe7090b89dfcaee5ea2212697e1e1d7ae"
|
||||
],
|
||||
"version": "==2018.11.29"
|
||||
"version": "==2019.3.9"
|
||||
},
|
||||
"chardet": {
|
||||
"hashes": [
|
||||
@@ -156,10 +156,10 @@
|
||||
},
|
||||
"decorator": {
|
||||
"hashes": [
|
||||
"sha256:33cd704aea07b4c28b3eb2c97d288a06918275dac0ecebdaf1bc8a48d98adb9e",
|
||||
"sha256:cabb249f4710888a2fc0e13e9a16c343d932033718ff62e1e9bc93a9d3a9122b"
|
||||
"sha256:86156361c50488b84a3f148056ea716ca587df2f0de1d34750d35c21312725de",
|
||||
"sha256:f069f3a01830ca754ba5258fde2278454a0b5b79e0d7f5c13b3b97e57d4acff6"
|
||||
],
|
||||
"version": "==4.3.2"
|
||||
"version": "==4.4.0"
|
||||
},
|
||||
"defusedxml": {
|
||||
"hashes": [
|
||||
@@ -199,11 +199,11 @@
|
||||
},
|
||||
"flake8": {
|
||||
"hashes": [
|
||||
"sha256:6d8c66a65635d46d54de59b027a1dda40abbe2275b3164b634835ac9c13fd048",
|
||||
"sha256:6eab21c6e34df2c05416faa40d0c59963008fff29b6f0ccfe8fa28152ab3e383"
|
||||
"sha256:859996073f341f2670741b51ec1e67a01da142831aa1fdc6242dbf88dffbe661",
|
||||
"sha256:a796a115208f5c03b18f332f7c11729812c8c3ded6c46319c59b53efd3819da8"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==3.7.6"
|
||||
"version": "==3.7.7"
|
||||
},
|
||||
"flit": {
|
||||
"hashes": [
|
||||
@@ -213,6 +213,19 @@
|
||||
"index": "pypi",
|
||||
"version": "==1.3"
|
||||
},
|
||||
"h11": {
|
||||
"hashes": [
|
||||
"sha256:acca6a44cb52a32ab442b1779adf0875c443c689e9e028f8d831a3769f9c5208",
|
||||
"sha256:f2b1ca39bfed357d1f19ac732913d5f9faa54a5062eca7d2ec3a916cfb7ae4c7"
|
||||
],
|
||||
"version": "==0.8.1"
|
||||
},
|
||||
"httptools": {
|
||||
"hashes": [
|
||||
"sha256:e00cbd7ba01ff748e494248183abc6e153f49181169d8a3d41bb49132ca01dfc"
|
||||
],
|
||||
"version": "==0.0.13"
|
||||
},
|
||||
"idna": {
|
||||
"hashes": [
|
||||
"sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407",
|
||||
@@ -251,19 +264,18 @@
|
||||
},
|
||||
"isort": {
|
||||
"hashes": [
|
||||
"sha256:1153601da39a25b14ddc54955dbbacbb6b2d19135386699e2ad58517953b34af",
|
||||
"sha256:b9c40e9750f3d77e6e4d441d8b0266cf555e7cdabdcff33c4fd06366ca761ef8",
|
||||
"sha256:ec9ef8f4a9bc6f71eec99e1806bfa2de401650d996c59330782b89a5555c1497"
|
||||
"sha256:18c796c2cd35eb1a1d3f012a214a542790a1aed95e29768bdcb9f2197eccbd0b",
|
||||
"sha256:96151fca2c6e736503981896495d344781b60d18bfda78dc11b290c6125ebdb6"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==4.3.4"
|
||||
"version": "==4.3.15"
|
||||
},
|
||||
"jedi": {
|
||||
"hashes": [
|
||||
"sha256:571702b5bd167911fe9036e5039ba67f820d6502832285cde8c881ab2b2149fd",
|
||||
"sha256:c8481b5e59d34a5c7c42e98f6625e633f6ef59353abea6437472c7ec2093f191"
|
||||
"sha256:2bb0603e3506f708e792c7f4ad8fc2a7a9d9c2d292a358fbbd58da531695595b",
|
||||
"sha256:2c6bcd9545c7d6440951b12b44d373479bf18123a401a52025cf98563fbd826c"
|
||||
],
|
||||
"version": "==0.13.2"
|
||||
"version": "==0.13.3"
|
||||
},
|
||||
"jinja2": {
|
||||
"hashes": [
|
||||
@@ -274,10 +286,10 @@
|
||||
},
|
||||
"jsonschema": {
|
||||
"hashes": [
|
||||
"sha256:683fe7ed58763ea0be572de5aad47cd3cc1297640916f9a8ccd222b287da7d2f",
|
||||
"sha256:b42d7a292addb57370e6260bcbadb77e00a899fe6ec998c453f45893c41c658b"
|
||||
"sha256:0c0a81564f181de3212efa2d17de1910f8732fa1b71c42266d983cd74304e20d",
|
||||
"sha256:a5f6559964a3851f59040d3b961de5e68e70971afb88ba519d27e6a039efff1a"
|
||||
],
|
||||
"version": "==3.0.0b3"
|
||||
"version": "==3.0.1"
|
||||
},
|
||||
"jupyter": {
|
||||
"hashes": [
|
||||
@@ -332,36 +344,36 @@
|
||||
},
|
||||
"markupsafe": {
|
||||
"hashes": [
|
||||
"sha256:048ef924c1623740e70204aa7143ec592504045ae4429b59c30054cb31e3c432",
|
||||
"sha256:130f844e7f5bdd8e9f3f42e7102ef1d49b2e6fdf0d7526df3f87281a532d8c8b",
|
||||
"sha256:19f637c2ac5ae9da8bfd98cef74d64b7e1bb8a63038a3505cd182c3fac5eb4d9",
|
||||
"sha256:1b8a7a87ad1b92bd887568ce54b23565f3fd7018c4180136e1cf412b405a47af",
|
||||
"sha256:1c25694ca680b6919de53a4bb3bdd0602beafc63ff001fea2f2fc16ec3a11834",
|
||||
"sha256:1f19ef5d3908110e1e891deefb5586aae1b49a7440db952454b4e281b41620cd",
|
||||
"sha256:1fa6058938190ebe8290e5cae6c351e14e7bb44505c4a7624555ce57fbbeba0d",
|
||||
"sha256:31cbb1359e8c25f9f48e156e59e2eaad51cd5242c05ed18a8de6dbe85184e4b7",
|
||||
"sha256:3e835d8841ae7863f64e40e19477f7eb398674da6a47f09871673742531e6f4b",
|
||||
"sha256:4e97332c9ce444b0c2c38dd22ddc61c743eb208d916e4265a2a3b575bdccb1d3",
|
||||
"sha256:525396ee324ee2da82919f2ee9c9e73b012f23e7640131dd1b53a90206a0f09c",
|
||||
"sha256:52b07fbc32032c21ad4ab060fec137b76eb804c4b9a1c7c7dc562549306afad2",
|
||||
"sha256:52ccb45e77a1085ec5461cde794e1aa037df79f473cbc69b974e73940655c8d7",
|
||||
"sha256:5c3fbebd7de20ce93103cb3183b47671f2885307df4a17a0ad56a1dd51273d36",
|
||||
"sha256:5e5851969aea17660e55f6a3be00037a25b96a9b44d2083651812c99d53b14d1",
|
||||
"sha256:5edfa27b2d3eefa2210fb2f5d539fbed81722b49f083b2c6566455eb7422fd7e",
|
||||
"sha256:7d263e5770efddf465a9e31b78362d84d015cc894ca2c131901a4445eaa61ee1",
|
||||
"sha256:83381342bfc22b3c8c06f2dd93a505413888694302de25add756254beee8449c",
|
||||
"sha256:857eebb2c1dc60e4219ec8e98dfa19553dae33608237e107db9c6078b1167856",
|
||||
"sha256:98e439297f78fca3a6169fd330fbe88d78b3bb72f967ad9961bcac0d7fdd1550",
|
||||
"sha256:bf54103892a83c64db58125b3f2a43df6d2cb2d28889f14c78519394feb41492",
|
||||
"sha256:d9ac82be533394d341b41d78aca7ed0e0f4ba5a2231602e2f05aa87f25c51672",
|
||||
"sha256:e982fe07ede9fada6ff6705af70514a52beb1b2c3d25d4e873e82114cf3c5401",
|
||||
"sha256:edce2ea7f3dfc981c4ddc97add8a61381d9642dc3273737e756517cc03e84dd6",
|
||||
"sha256:efdc45ef1afc238db84cb4963aa689c0408912a0239b0721cb172b4016eb31d6",
|
||||
"sha256:f137c02498f8b935892d5c0172560d7ab54bc45039de8805075e19079c639a9c",
|
||||
"sha256:f82e347a72f955b7017a39708a3667f106e6ad4d10b25f237396a7115d8ed5fd",
|
||||
"sha256:fb7c206e01ad85ce57feeaaa0bf784b97fa3cad0d4a5737bc5295785f5c613a1"
|
||||
"sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473",
|
||||
"sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161",
|
||||
"sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235",
|
||||
"sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5",
|
||||
"sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff",
|
||||
"sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b",
|
||||
"sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1",
|
||||
"sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e",
|
||||
"sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183",
|
||||
"sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66",
|
||||
"sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1",
|
||||
"sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1",
|
||||
"sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e",
|
||||
"sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b",
|
||||
"sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905",
|
||||
"sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735",
|
||||
"sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d",
|
||||
"sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e",
|
||||
"sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d",
|
||||
"sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c",
|
||||
"sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21",
|
||||
"sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2",
|
||||
"sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5",
|
||||
"sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b",
|
||||
"sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6",
|
||||
"sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f",
|
||||
"sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f",
|
||||
"sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"
|
||||
],
|
||||
"version": "==1.1.0"
|
||||
"version": "==1.1.1"
|
||||
},
|
||||
"mccabe": {
|
||||
"hashes": [
|
||||
@@ -387,11 +399,11 @@
|
||||
},
|
||||
"mkdocs-material": {
|
||||
"hashes": [
|
||||
"sha256:63c49a7020e5d187d5adcd441b259e0b81ad418599b22e2c2574b419ed833851",
|
||||
"sha256:90a240f268f182a96098490d35bb75d5efc86b2f67d63a82b8750da20a72ef60"
|
||||
"sha256:762a71f82c1e291c3ff067cecd9d581557da777332fd98bc0af20fd5ab4a2dd0",
|
||||
"sha256:b2c7174ecaa81fb1d62a5f4906f99fa0e7062ced8f9a14ec4f60b1bef9feebbf"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==4.0.1"
|
||||
"version": "==4.0.2"
|
||||
},
|
||||
"more-itertools": {
|
||||
"hashes": [
|
||||
@@ -432,10 +444,10 @@
|
||||
},
|
||||
"notebook": {
|
||||
"hashes": [
|
||||
"sha256:3ab2db8bc10e6edbd264c3c4b800bee276c99818386ee0c146d98d7e6bcf0a67",
|
||||
"sha256:d908673a4010787625c8952e91a22adf737db031f2aa0793ad92f6558918a74a"
|
||||
"sha256:18a98858c0331fb65a60f2ebb6439f8c0c4defd14ca363731b6cabc7f61624b4",
|
||||
"sha256:cc027a62be0f7756e0ef3d2d98458c4d7f4b3566449fb1a05891207f5bd9a1bf"
|
||||
],
|
||||
"version": "==5.7.4"
|
||||
"version": "==5.7.6"
|
||||
},
|
||||
"pandocfilters": {
|
||||
"hashes": [
|
||||
@@ -467,24 +479,24 @@
|
||||
},
|
||||
"pluggy": {
|
||||
"hashes": [
|
||||
"sha256:8ddc32f03971bfdf900a81961a48ccf2fb677cf7715108f85295c67405798616",
|
||||
"sha256:980710797ff6a041e9a73a5787804f848996ecaa6f8a1b1e08224a5894f2074a"
|
||||
"sha256:19ecf9ce9db2fce065a7a0586e07cfb4ac8614fe96edf628a264b1c70116cf8f",
|
||||
"sha256:84d306a647cc805219916e62aab89caa97a33a1dd8c342e87a37f91073cd4746"
|
||||
],
|
||||
"version": "==0.8.1"
|
||||
"version": "==0.9.0"
|
||||
},
|
||||
"prometheus-client": {
|
||||
"hashes": [
|
||||
"sha256:e8c11ff5ca53de6c3d91e1510500611cafd1d247a937ec6c588a0a7cc3bef93c"
|
||||
"sha256:1b38b958750f66f208bcd9ab92a633c0c994d8859c831f7abc1f46724fcee490"
|
||||
],
|
||||
"version": "==0.5.0"
|
||||
"version": "==0.6.0"
|
||||
},
|
||||
"prompt-toolkit": {
|
||||
"hashes": [
|
||||
"sha256:88002cc618cacfda8760c4539e76c3b3f148ecdb7035a3d422c7ecdc90c2a3ba",
|
||||
"sha256:c6655a12e9b08edb8cf5aeab4815fd1e1bdea4ad73d3bbf269cf2e0c4eb75d5e",
|
||||
"sha256:df5835fb8f417aa55e5cafadbaeb0cf630a1e824aad16989f9f0493e679ec010"
|
||||
"sha256:11adf3389a996a6d45cc277580d0d53e8a5afd281d0c9ec71b28e6f121463780",
|
||||
"sha256:2519ad1d8038fd5fc8e770362237ad0364d16a7650fb5724af6997ed5515e3c1",
|
||||
"sha256:977c6583ae813a37dc1c2e1b715892461fcbdaa57f6fc62f33a528c4886c8f55"
|
||||
],
|
||||
"version": "==2.0.8"
|
||||
"version": "==2.0.9"
|
||||
},
|
||||
"ptyprocess": {
|
||||
"hashes": [
|
||||
@@ -496,10 +508,10 @@
|
||||
},
|
||||
"py": {
|
||||
"hashes": [
|
||||
"sha256:bf92637198836372b520efcba9e020c330123be8ce527e535d185ed4b6f45694",
|
||||
"sha256:e76826342cefe3c3d5f7e8ee4316b80d1dd8a300781612ddbc765c17ba25a6c6"
|
||||
"sha256:64f65755aee5b381cea27766a3a147c3f15b9b6b9ac88676de66ba2ae36793fa",
|
||||
"sha256:dc639b046a6e2cff5bbe40194ad65936d6ba360b52b3c3fe1d08a82dd50b5e53"
|
||||
],
|
||||
"version": "==1.7.0"
|
||||
"version": "==1.8.0"
|
||||
},
|
||||
"pycodestyle": {
|
||||
"hashes": [
|
||||
@@ -510,10 +522,10 @@
|
||||
},
|
||||
"pyflakes": {
|
||||
"hashes": [
|
||||
"sha256:5e8c00e30c464c99e0b501dc160b13a14af7f27d4dffb529c556e30a159e231d",
|
||||
"sha256:f277f9ca3e55de669fba45b7393a1449009cff5a37d1af10ebb76c52765269cd"
|
||||
"sha256:17dbeb2e3f4d772725c777fabc446d5634d1038f234e77343108ce445ea69ce0",
|
||||
"sha256:d976835886f8c5b31d47970ed689944a0262b5f3afa00a5a7b4dc81e5449f8a2"
|
||||
],
|
||||
"version": "==2.1.0"
|
||||
"version": "==2.1.1"
|
||||
},
|
||||
"pygments": {
|
||||
"hashes": [
|
||||
@@ -531,17 +543,17 @@
|
||||
},
|
||||
"pyrsistent": {
|
||||
"hashes": [
|
||||
"sha256:07f7ae71291af8b0dbad8c2ab630d8223e4a8c4e10fc37badda158c02e753acf"
|
||||
"sha256:3ca82748918eb65e2d89f222b702277099aca77e34843c5eb9d52451173970e2"
|
||||
],
|
||||
"version": "==0.14.10"
|
||||
"version": "==0.14.11"
|
||||
},
|
||||
"pytest": {
|
||||
"hashes": [
|
||||
"sha256:067a1d4bf827ffdd56ad21bd46674703fce77c5957f6c1eef731f6146bfcef1c",
|
||||
"sha256:9687049d53695ad45cf5fdc7bbd51f0c49f1ea3ecfc4b7f3fde7501b541f17f4"
|
||||
"sha256:592eaa2c33fae68c7d75aacf042efc9f77b27c08a6224a4f59beab8d9a420523",
|
||||
"sha256:ad3ad5c450284819ecde191a654c09b0ec72257a2c711b9633d677c71c9850c4"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==4.3.0"
|
||||
"version": "==4.3.1"
|
||||
},
|
||||
"pytest-cov": {
|
||||
"hashes": [
|
||||
@@ -573,33 +585,49 @@
|
||||
},
|
||||
"pyyaml": {
|
||||
"hashes": [
|
||||
"sha256:254bf6fda2b7c651837acb2c718e213df29d531eebf00edb54743d10bcb694eb",
|
||||
"sha256:3108529b78577327d15eec243f0ff348a0640b0c3478d67ad7f5648f93bac3e2",
|
||||
"sha256:3c17fb92c8ba2f525e4b5f7941d850e7a48c3a59b32d331e2502a3cdc6648e76",
|
||||
"sha256:8d6d96001aa7f0a6a4a95e8143225b5d06e41b1131044913fecb8f85a125714b",
|
||||
"sha256:c8a88edd93ee29ede719080b2be6cb2333dfee1dccba213b422a9c8e97f2967b"
|
||||
"sha256:1adecc22f88d38052fb787d959f003811ca858b799590a5eaa70e63dca50308c",
|
||||
"sha256:436bc774ecf7c103814098159fbb84c2715d25980175292c648f2da143909f95",
|
||||
"sha256:460a5a4248763f6f37ea225d19d5c205677d8d525f6a83357ca622ed541830c2",
|
||||
"sha256:5a22a9c84653debfbf198d02fe592c176ea548cccce47553f35f466e15cf2fd4",
|
||||
"sha256:7a5d3f26b89d688db27822343dfa25c599627bc92093e788956372285c6298ad",
|
||||
"sha256:9372b04a02080752d9e6f990179a4ab840227c6e2ce15b95e1278456664cf2ba",
|
||||
"sha256:a5dcbebee834eaddf3fa7366316b880ff4062e4bcc9787b78c7fbb4a26ff2dd1",
|
||||
"sha256:aee5bab92a176e7cd034e57f46e9df9a9862a71f8f37cad167c6fc74c65f5b4e",
|
||||
"sha256:c51f642898c0bacd335fc119da60baae0824f2cde95b0330b56c0553439f0673",
|
||||
"sha256:c68ea4d3ba1705da1e0d85da6684ac657912679a649e8868bd850d2c299cce13",
|
||||
"sha256:e23d0cc5299223dcc37885dae624f382297717e459ea24053709675a976a3e19"
|
||||
],
|
||||
"version": "==4.2b4"
|
||||
"version": "==5.1"
|
||||
},
|
||||
"pyzmq": {
|
||||
"hashes": [
|
||||
"sha256:15f0bf7cd80020f165635595e197603aedb37fddf4164ad5ae226afc43242f7b",
|
||||
"sha256:1756dc72e192c670490e38c788c3a35f901adc74ee436e5131d5a3e85fdd7dc6",
|
||||
"sha256:1d1eb490da54679d724b08ef3ee04530849023670c4ba7e400ed2cdf906720c4",
|
||||
"sha256:228402625796821f08706f58cc42a3c51c9897d723550babaefe4feec2b6dacc",
|
||||
"sha256:3928534fa00a2aabfcfdb439c08ba37fbe99ab0cf57776c8db8d2b73a51693ba",
|
||||
"sha256:3d2a295b1086d450981f73d3561ac204a0cc9c8ded386a4a34327d918f3b1d0a",
|
||||
"sha256:4fd8621a309db6ec23ef1369f43cdf7a9b0dc217d8ff9ca4095a6e932b379bda",
|
||||
"sha256:54fe55a1694ffe608c8e4c5183e83cab7a91f3e5c84bd6f188868d6676c12aba",
|
||||
"sha256:618887be4ad754228c0cbba7631f6574608b4430fe93974e6322324f1304fdac",
|
||||
"sha256:69130efb6efa936de601cb135a8a4eec1caccd4ea2b784237145ff4075c2d3ae",
|
||||
"sha256:6e7f78eeac82140bde7e60e975c6e6b1b678a4dd377782ab63319c1c78bf3aa1",
|
||||
"sha256:6ee760cdb84e43574da6b3f2f1fc1251e8acf87253900d28a06451c5f5de39e9",
|
||||
"sha256:97cb1b7cd2c46e87b0a26651eccd2bbb8c758035efd1635ebb81ac36aa76a88c",
|
||||
"sha256:abfa774dbadacc849121ed92eae05189d226daab583388b499472e1bbb17ef69",
|
||||
"sha256:b30c339eb58355f51f4f54dd61d785f1ff58c86bca1c3a5916977631d121867b"
|
||||
"sha256:1651e52ed91f0736afd6d94ef9f3259b5534ce8beddb054f3d5ca989c4ef7c4f",
|
||||
"sha256:5ccb9b3d4cd20c000a9b75689d5add8cd3bce67fcbd0f8ae1b59345247d803af",
|
||||
"sha256:5e120c4cd3872e332fb35d255ad5998ebcee32ace4387b1b337416b6b90436c7",
|
||||
"sha256:5e2a3707c69a7281a9957f83718815fd74698cba31f6d69f9ed359921f662221",
|
||||
"sha256:63d51add9af8d0442dc90f916baf98fdc04e3b0a32afec4bfc83f8d85e72959f",
|
||||
"sha256:65c5a0bdc49e20f7d6b03a661f71e2fda7a99c51270cafe71598146d09810d0d",
|
||||
"sha256:66828fabe911aa545d919028441a585edb7c9c77969a5fea6722ef6e6ece38ab",
|
||||
"sha256:7d79427e82d9dad6e9b47c0b3e7ae5f9d489b1601e3a36ea629bb49501a4daf3",
|
||||
"sha256:824ee5d3078c4eae737ffc500fbf32f2b14e6ec89b26b435b7834febd70120cf",
|
||||
"sha256:89dc0a83cccec19ff3c62c091e43e66e0183d1e6b4658c16ee4e659518131494",
|
||||
"sha256:8b319805f6f7c907b101c864c3ca6cefc9db8ce0791356f180b1b644c7347e4c",
|
||||
"sha256:90facfb379ab47f94b19519c1ecc8ec8d10813b69d9c163117944948bdec5d15",
|
||||
"sha256:a0a178c7420021fc0730180a914a4b4b3092ce9696ceb8e72d0f60f8ce1655dd",
|
||||
"sha256:a7a89591ae315baccb8072f216614b3e59aed7385aef4393a6c741783d6ee9cf",
|
||||
"sha256:ba2578f0ae582452c02ed9fac2dc477b08e80ce05d2c0885becf5fff6651ccb0",
|
||||
"sha256:c69b0055c55702f5b0b6b354133e8325b9a56dbc80e1be2d240bead253fb9825",
|
||||
"sha256:ca434e1858fe222380221ddeb81e86f45522773344c9da63c311d17161df5e06",
|
||||
"sha256:d4b8ecfc3d92f114f04d5c40f60a65e5196198b827503341521dda12d8b14939",
|
||||
"sha256:d706025c47b09a54f005953ebe206f6d07a22516776faa4f509aaff681cc5468",
|
||||
"sha256:d8f27e958f8a2c0c8ffd4d8855c3ce8ac3fa1e105f0491ce31729aa2b3229740",
|
||||
"sha256:dbd264298f76b9060ce537008eb989317ca787c857e23cbd1b3ddf89f190a9b1",
|
||||
"sha256:e926d66f0df8fdbf03ba20583af0f215e475c667fb033d45fd031c66c63e34c9",
|
||||
"sha256:efc3bd48237f973a749f7312f68062f1b4ca5c2032a0673ca3ea8e46aa77187b",
|
||||
"sha256:f59bc782228777cbfe04555707a9c56d269c787ed25d6d28ed9d0fbb41cb1ad2",
|
||||
"sha256:f8da5322f4ff5f667a0d5a27e871b560c6637153c81e318b35cb012b2a98835c"
|
||||
],
|
||||
"version": "==18.0.0"
|
||||
"version": "==18.0.1"
|
||||
},
|
||||
"qtconsole": {
|
||||
"hashes": [
|
||||
@@ -632,10 +660,10 @@
|
||||
},
|
||||
"sqlalchemy": {
|
||||
"hashes": [
|
||||
"sha256:7dede29f121071da9873e7b8c98091874617858e790dc364ffaab4b09d81216c"
|
||||
"sha256:781fb7b9d194ed3fc596b8f0dd4623ff160e3e825dd8c15472376a438c19598b"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.3.0b3"
|
||||
"version": "==1.3.1"
|
||||
},
|
||||
"terminado": {
|
||||
"hashes": [
|
||||
@@ -660,9 +688,15 @@
|
||||
},
|
||||
"tornado": {
|
||||
"hashes": [
|
||||
"sha256:d3b719a0cb7094e2b1ca94b31f4b601639fa7ad01a548a1a2ccdd6cbdfd56671"
|
||||
"sha256:1a58f2d603476d5e462f7c28ca1dbb5ac7e51348b27a9cac849cdec3471101f8",
|
||||
"sha256:33f93243cd46dd398e5d2bbdd75539564d1f13f25d704cfc7541db74066d6695",
|
||||
"sha256:34e59401afcecf0381a28228daad8ed3275bcb726810654612d5e9c001f421b7",
|
||||
"sha256:35817031611d2c296c69e5023ea1f9b5720be803e3bb119464bb2a0405d5cd70",
|
||||
"sha256:666b335cef5cc2759c21b7394cff881f71559aaf7cb8c4458af5bb6cb7275b47",
|
||||
"sha256:81203efb26debaaef7158187af45bc440796de9fb1df12a75b65fae11600a255",
|
||||
"sha256:de274c65f45f6656c375cdf1759dbf0bc52902a1e999d12a35eb13020a641a53"
|
||||
],
|
||||
"version": "==6.0b1"
|
||||
"version": "==6.0.1"
|
||||
},
|
||||
"traitlets": {
|
||||
"hashes": [
|
||||
@@ -709,6 +743,28 @@
|
||||
],
|
||||
"version": "==1.24.1"
|
||||
},
|
||||
"uvicorn": {
|
||||
"hashes": [
|
||||
"sha256:d700b65169820fc260f39402b7f966c178691daaa40cb376cad99d7cd737f772"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.7.0b1"
|
||||
},
|
||||
"uvloop": {
|
||||
"hashes": [
|
||||
"sha256:0fcd894f6fc3226a962ee7ad895c4f52e3f5c3c55098e21efb17c071849a0573",
|
||||
"sha256:2f31de1742c059c96cb76b91c5275b22b22b965c886ee1fced093fa27dde9e64",
|
||||
"sha256:459e4649fcd5ff719523de33964aa284898e55df62761e7773d088823ccbd3e0",
|
||||
"sha256:67867aafd6e0bc2c30a079603a85d83b94f23c5593b3cc08ec7e58ac18bf48e5",
|
||||
"sha256:8c200457e6847f28d8bb91c5e5039d301716f5f2fce25646f5fb3fd65eda4a26",
|
||||
"sha256:958906b9ca39eb158414fbb7d6b8ef1b7aee4db5c8e8e5d00fcbb69a1ce9dca7",
|
||||
"sha256:ac1dca3d8f3ef52806059e81042ee397ac939e5a86c8a3cea55d6b087db66115",
|
||||
"sha256:b284c22d8938866318e3b9d178142b8be316c52d16fcfe1560685a686718a021",
|
||||
"sha256:c48692bf4587ce281d641087658eca275a5ad3b63c78297bbded96570ae9ce8f",
|
||||
"sha256:fefc3b2b947c99737c348887db2c32e539160dcbeb7af9aa6b53db7a283538fe"
|
||||
],
|
||||
"version": "==0.12.2"
|
||||
},
|
||||
"wcwidth": {
|
||||
"hashes": [
|
||||
"sha256:3df37372226d6e63e1b1e1eda15c594bca98a22d33a23832a90998faa96bc65e",
|
||||
@@ -723,6 +779,32 @@
|
||||
],
|
||||
"version": "==0.5.1"
|
||||
},
|
||||
"websockets": {
|
||||
"hashes": [
|
||||
"sha256:04b42a1b57096ffa5627d6a78ea1ff7fad3bc2c0331ffc17bc32a4024da7fea0",
|
||||
"sha256:08e3c3e0535befa4f0c4443824496c03ecc25062debbcf895874f8a0b4c97c9f",
|
||||
"sha256:10d89d4326045bf5e15e83e9867c85d686b612822e4d8f149cf4840aab5f46e0",
|
||||
"sha256:232fac8a1978fc1dead4b1c2fa27c7756750fb393eb4ac52f6bc87ba7242b2fa",
|
||||
"sha256:4bf4c8097440eff22bc78ec76fe2a865a6e658b6977a504679aaf08f02c121da",
|
||||
"sha256:51642ea3a00772d1e48fb0c492f0d3ae3b6474f34d20eca005a83f8c9c06c561",
|
||||
"sha256:55d86102282a636e195dad68aaaf85b81d0bef449d7e2ef2ff79ac450bb25d53",
|
||||
"sha256:564d2675682bd497b59907d2205031acbf7d3fadf8c763b689b9ede20300b215",
|
||||
"sha256:5d13bf5197a92149dc0badcc2b699267ff65a867029f465accfca8abab95f412",
|
||||
"sha256:5eda665f6789edb9b57b57a159b9c55482cbe5b046d7db458948370554b16439",
|
||||
"sha256:5edb2524d4032be4564c65dc4f9d01e79fe8fad5f966e5b552f4e5164fef0885",
|
||||
"sha256:79691794288bc51e2a3b8de2bc0272ca8355d0b8503077ea57c0716e840ebaef",
|
||||
"sha256:7fcc8681e9981b9b511cdee7c580d5b005f3bb86b65bde2188e04a29f1d63317",
|
||||
"sha256:8e447e05ec88b1b408a4c9cde85aa6f4b04f06aa874b9f0b8e8319faf51b1fee",
|
||||
"sha256:90ea6b3e7787620bb295a4ae050d2811c807d65b1486749414f78cfd6fb61489",
|
||||
"sha256:9e13239952694b8b831088431d15f771beace10edfcf9ef230cefea14f18508f",
|
||||
"sha256:d40f081187f7b54d7a99d8a5c782eaa4edc335a057aa54c85059272ed826dc09",
|
||||
"sha256:e1df1a58ed2468c7b7ce9a2f9752a32ad08eac2bcd56318625c3647c2cd2da6f",
|
||||
"sha256:e98d0cec437097f09c7834a11c69d79fe6241729b23f656cfc227e93294fc242",
|
||||
"sha256:f8d59627702d2ff27cb495ca1abdea8bd8d581de425c56e93bff6517134e0a9b",
|
||||
"sha256:fc30cdf2e949a2225b012a7911d1d031df3d23e99b7eda7dfc982dc4a860dae9"
|
||||
],
|
||||
"version": "==7.0"
|
||||
},
|
||||
"widgetsnbextension": {
|
||||
"hashes": [
|
||||
"sha256:14b2c65f9940c9a7d3b70adbe713dbd38b5ec69724eebaba034d1036cf3d4740",
|
||||
|
||||
12
README.md
12
README.md
@@ -31,10 +31,10 @@ The key features are:
|
||||
* **Fast**: Very high performance, on par with **NodeJS** and **Go** (thanks to Starlette and Pydantic). [One of the fastest Python frameworks available](#performance).
|
||||
|
||||
* **Fast to code**: Increase the speed to develop features by about 200% to 300% *.
|
||||
* **Less bugs**: Reduce about 40% of human (developer) induced errors. *
|
||||
* **Fewer bugs**: Reduce about 40% of human (developer) induced errors. *
|
||||
* **Intuitive**: Great editor support. <abbr title="also known as auto-complete, autocompletion, IntelliSense">Completion</abbr> everywhere. Less time debugging.
|
||||
* **Easy**: Designed to be easy to use and learn. Less time reading docs.
|
||||
* **Short**: Minimize code duplication. Multiple features from each parameter declaration. Less bugs.
|
||||
* **Short**: Minimize code duplication. Multiple features from each parameter declaration. Fewer bugs.
|
||||
* **Robust**: Get production-ready code. With automatic interactive documentation.
|
||||
* **Standards-based**: Based on (and fully compatible with) the open standards for APIs: <a href="https://github.com/OAI/OpenAPI-Specification" target="_blank">OpenAPI</a> (previously known as Swagger) and <a href="http://json-schema.org/" target="_blank">JSON Schema</a>.
|
||||
|
||||
@@ -116,17 +116,17 @@ If you don't know, check the _"In a hurry?"_ section about <a href="https://fast
|
||||
Run the server with:
|
||||
|
||||
```bash
|
||||
uvicorn main:app --debug
|
||||
uvicorn main:app --reload
|
||||
```
|
||||
|
||||
<details markdown="1">
|
||||
<summary>About the command <code>uvicorn main:app --debug</code>...</summary>
|
||||
<summary>About the command <code>uvicorn main:app --reload</code>...</summary>
|
||||
|
||||
The command `uvicorn main:app` refers to:
|
||||
|
||||
* `main`: the file `main.py` (the Python "module").
|
||||
* `app`: the object created inside of `main.py` with the line `app = FastAPI()`.
|
||||
* `--debug`: make the server restart after code changes. Only do this for development.
|
||||
* `--reload`: make the server restart after code changes. Only do this for development.
|
||||
|
||||
</details>
|
||||
|
||||
@@ -199,7 +199,7 @@ def create_item(item_id: int, item: Item):
|
||||
return {"item_name": item.name, "item_id": item_id}
|
||||
```
|
||||
|
||||
The server should reload automatically (because you added `--debug` to the `uvicorn` command above).
|
||||
The server should reload automatically (because you added `--reload` to the `uvicorn` command above).
|
||||
|
||||
### Interactive API docs upgrade
|
||||
|
||||
|
||||
@@ -106,7 +106,7 @@ That way, you can edit the documentation/source files and see the changes live.
|
||||
And if you run the examples with, e.g.:
|
||||
|
||||
```bash
|
||||
uvicorn tutorial001:app --debug
|
||||
uvicorn tutorial001:app --reload
|
||||
```
|
||||
|
||||
as Uvicorn by default will use the port `8000`, the documentation on port `8008` won't clash.
|
||||
|
||||
@@ -39,7 +39,7 @@ COPY ./app /app
|
||||
|
||||
#### Bigger Applications
|
||||
|
||||
If you followed the section about creating <a href="" target="_blank">Bigger Applications with Multiple Files
|
||||
If you followed the section about creating <a href="https://fastapi.tiangolo.com/tutorial/bigger-applications/" target="_blank">Bigger Applications with Multiple Files
|
||||
</a>, your `Dockerfile` might instead look like:
|
||||
|
||||
```Dockerfile
|
||||
@@ -239,7 +239,7 @@ You can deploy **FastAPI** directly without Docker too.
|
||||
|
||||
You just need to install <a href="https://www.uvicorn.org/" target="_blank">Uvicorn</a> (or any other ASGI server).
|
||||
|
||||
And run your application the same way you have done in the tutorials, but without the `--debug` option, e.g.:
|
||||
And run your application the same way you have done in the tutorials, but without the `--reload` option, e.g.:
|
||||
|
||||
```bash
|
||||
uvicorn main:app --host 0.0.0.0 --port 80
|
||||
|
||||
@@ -27,7 +27,7 @@ Doing it, you will receive notifications (in your email) whenever there's a new
|
||||
|
||||
## Connect with the author
|
||||
|
||||
You can connect with me (Sebastián Ramírez / `tiangolo`), the author.
|
||||
You can connect with <a href="https://tiangolo.com" target="_blank">me (Sebastián Ramírez / `tiangolo`)</a>, the author.
|
||||
|
||||
You can:
|
||||
|
||||
|
||||
BIN
docs/img/tutorial/application-configuration/image01.png
Normal file
BIN
docs/img/tutorial/application-configuration/image01.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 37 KiB |
BIN
docs/img/tutorial/query-params-str-validations/image02.png
Normal file
BIN
docs/img/tutorial/query-params-str-validations/image02.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 71 KiB |
@@ -31,10 +31,10 @@ The key features are:
|
||||
* **Fast**: Very high performance, on par with **NodeJS** and **Go** (thanks to Starlette and Pydantic). [One of the fastest Python frameworks available](#performance).
|
||||
|
||||
* **Fast to code**: Increase the speed to develop features by about 200% to 300% *.
|
||||
* **Less bugs**: Reduce about 40% of human (developer) induced errors. *
|
||||
* **Fewer bugs**: Reduce about 40% of human (developer) induced errors. *
|
||||
* **Intuitive**: Great editor support. <abbr title="also known as auto-complete, autocompletion, IntelliSense">Completion</abbr> everywhere. Less time debugging.
|
||||
* **Easy**: Designed to be easy to use and learn. Less time reading docs.
|
||||
* **Short**: Minimize code duplication. Multiple features from each parameter declaration. Less bugs.
|
||||
* **Short**: Minimize code duplication. Multiple features from each parameter declaration. Fewer bugs.
|
||||
* **Robust**: Get production-ready code. With automatic interactive documentation.
|
||||
* **Standards-based**: Based on (and fully compatible with) the open standards for APIs: <a href="https://github.com/OAI/OpenAPI-Specification" target="_blank">OpenAPI</a> (previously known as Swagger) and <a href="http://json-schema.org/" target="_blank">JSON Schema</a>.
|
||||
|
||||
@@ -116,17 +116,17 @@ If you don't know, check the _"In a hurry?"_ section about <a href="https://fast
|
||||
Run the server with:
|
||||
|
||||
```bash
|
||||
uvicorn main:app --debug
|
||||
uvicorn main:app --reload
|
||||
```
|
||||
|
||||
<details markdown="1">
|
||||
<summary>About the command <code>uvicorn main:app --debug</code>...</summary>
|
||||
<summary>About the command <code>uvicorn main:app --reload</code>...</summary>
|
||||
|
||||
The command `uvicorn main:app` refers to:
|
||||
|
||||
* `main`: the file `main.py` (the Python "module").
|
||||
* `app`: the object created inside of `main.py` with the line `app = FastAPI()`.
|
||||
* `--debug`: make the server restart after code changes. Only do this for development.
|
||||
* `--reload`: make the server restart after code changes. Only do this for development.
|
||||
|
||||
</details>
|
||||
|
||||
@@ -199,7 +199,7 @@ def create_item(item_id: int, item: Item):
|
||||
return {"item_name": item.name, "item_id": item_id}
|
||||
```
|
||||
|
||||
The server should reload automatically (because you added `--debug` to the `uvicorn` command above).
|
||||
The server should reload automatically (because you added `--reload` to the `uvicorn` command above).
|
||||
|
||||
### Interactive API docs upgrade
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ John Doe
|
||||
|
||||
The function does the following:
|
||||
|
||||
* Takes a `fist_name` and `last_name`.
|
||||
* Takes a `first_name` and `last_name`.
|
||||
* Converts the first letter of each one to upper case with `title()`.
|
||||
* <abbr title="Puts them together, as one. With the contents of one after the other.">Concatenates</abbr> them with a space in the middle.
|
||||
|
||||
|
||||
@@ -1,4 +1,36 @@
|
||||
## Next
|
||||
## Next release
|
||||
|
||||
## 0.10.0
|
||||
|
||||
* Add support for Background Tasks in *path operation functions* and dependencies. New documentation about <a href="https://fastapi.tiangolo.com/tutorial/background-tasks/" target="_blank">Background Tasks is here</a>. PR <a href="https://github.com/tiangolo/fastapi/pull/103" target="_blank">#103</a>.
|
||||
|
||||
* Add support for `.websocket_route()` in `APIRouter`. PR <a href="https://github.com/tiangolo/fastapi/pull/100" target="_blank">#100</a> by <a href="https://github.com/euri10" target="_blank">@euri10</a>.
|
||||
|
||||
* New docs section about <a href="https://fastapi.tiangolo.com/tutorial/events/" target="_blank">Events: startup - shutdown</a>. PR <a href="https://github.com/tiangolo/fastapi/pull/99" target="_blank">#99</a>.
|
||||
|
||||
## 0.9.1
|
||||
|
||||
* Document receiving <a href="https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#query-parameter-list-multiple-values" target="_blank">Multiple values with the same query parameter</a> and <a href="https://fastapi.tiangolo.com/tutorial/header-params/#duplicate-headers" target="_blank">Duplicate headers</a>. PR <a href="https://github.com/tiangolo/fastapi/pull/95" target="_blank">#95</a>.
|
||||
|
||||
## 0.9.0
|
||||
|
||||
* Upgrade compatible Pydantic version to `0.21.0`. PR <a href="https://github.com/tiangolo/fastapi/pull/90" target="_blank">#90</a>.
|
||||
|
||||
* Add documentation for: <a href="https://fastapi.tiangolo.com/tutorial/application-configuration/" target="_blank">Application Configuration</a>.
|
||||
|
||||
* Fix typo in docs. PR <a href="https://github.com/tiangolo/fastapi/pull/76" target="_blank">#76</a> by <a href="https://github.com/matthewhegarty" target="_blank">@matthewhegarty</a>.
|
||||
|
||||
* Fix link in "Deployment" to "Bigger Applications".
|
||||
|
||||
## 0.8.0
|
||||
|
||||
* Make development scripts executable. PR <a href="https://github.com/tiangolo/fastapi/pull/76" target="_blank">#76</a> by <a href="https://github.com/euri10" target="_blank">@euri10</a>.
|
||||
|
||||
* Add support for adding `tags` in `app.include_router()`. PR <a href="https://github.com/tiangolo/fastapi/pull/55" target="_blank">#55</a> by <a href="https://github.com/euri10" target="_blank">@euri10</a>. Documentation updated in the section: <a href="https://fastapi.tiangolo.com/tutorial/bigger-applications/" target="_blank">Bigger Applications</a>.
|
||||
|
||||
* Update docs related to Uvicorn to use new `--reload` option from version `0.5.x`. PR <a href="https://github.com/tiangolo/fastapi/pull/74" target="_blank">#74</a>.
|
||||
|
||||
* Update `isort` imports and scripts to be compatible with newer versions. PR <a href="https://github.com/tiangolo/fastapi/pull/75" target="_blank">#75</a>.
|
||||
|
||||
## 0.7.1
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI(
|
||||
title="My Super Project", version="2.5.0", openapi_url="/api/v1/openapi.json"
|
||||
)
|
||||
app = FastAPI(openapi_url="/api/v1/openapi.json")
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
|
||||
@@ -1,12 +1,6 @@
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI(
|
||||
title="My Super Project",
|
||||
version="2.5.0",
|
||||
openapi_url="/api/v1/openapi.json",
|
||||
docs_url="/api/v1/docs",
|
||||
redoc_url=None,
|
||||
)
|
||||
app = FastAPI(docs_url="/documentation", redoc_url=None)
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
|
||||
15
docs/src/background_tasks/tutorial001.py
Normal file
15
docs/src/background_tasks/tutorial001.py
Normal file
@@ -0,0 +1,15 @@
|
||||
from fastapi import BackgroundTasks, FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
def write_notification(email: str, message=""):
|
||||
with open("log.txt", mode="w") as email_file:
|
||||
content = f"notification for {email}: {message}"
|
||||
email_file.write(content)
|
||||
|
||||
|
||||
@app.post("/send-notification/{email}")
|
||||
async def send_notification(email: str, background_tasks: BackgroundTasks):
|
||||
background_tasks.add_task(write_notification, email, message="some notification")
|
||||
return {"message": "Notification sent in the background"}
|
||||
24
docs/src/background_tasks/tutorial002.py
Normal file
24
docs/src/background_tasks/tutorial002.py
Normal file
@@ -0,0 +1,24 @@
|
||||
from fastapi import BackgroundTasks, Depends, FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
def write_log(message: str):
|
||||
with open("log.txt", mode="a") as log:
|
||||
log.write(message)
|
||||
|
||||
|
||||
def get_query(background_tasks: BackgroundTasks, q: str = None):
|
||||
if q:
|
||||
message = f"found query: {q}\n"
|
||||
background_tasks.add_task(write_log, message)
|
||||
return q
|
||||
|
||||
|
||||
@app.post("/send-notification/{email}")
|
||||
async def send_notification(
|
||||
email: str, background_tasks: BackgroundTasks, q: str = Depends(get_query)
|
||||
):
|
||||
message = f"message to {email}\n"
|
||||
background_tasks.add_task(write_log, message)
|
||||
return {"message": "Message sent"}
|
||||
@@ -1,9 +1,8 @@
|
||||
from fastapi import FastAPI
|
||||
|
||||
from .routers.items import router as items_router
|
||||
from .routers.users import router as users_router
|
||||
from .routers import items, users
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
app.include_router(users_router)
|
||||
app.include_router(items_router, prefix="/items")
|
||||
app.include_router(users.router)
|
||||
app.include_router(items.router, prefix="/items", tags=["items"])
|
||||
|
||||
@@ -3,11 +3,11 @@ from fastapi import APIRouter
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get("/", tags=["items"])
|
||||
@router.get("/")
|
||||
async def read_items():
|
||||
return [{"name": "Item Foo"}, {"name": "item Bar"}]
|
||||
|
||||
|
||||
@router.get("/{item_id}", tags=["items"])
|
||||
@router.get("/{item_id}")
|
||||
async def read_item(item_id: str):
|
||||
return {"name": "Fake Specific Item", "item_id": item_id}
|
||||
|
||||
16
docs/src/events/tutorial001.py
Normal file
16
docs/src/events/tutorial001.py
Normal file
@@ -0,0 +1,16 @@
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
items = {}
|
||||
|
||||
|
||||
@app.on_event("startup")
|
||||
async def startup_event():
|
||||
items["foo"] = {"name": "Fighters"}
|
||||
items["bar"] = {"name": "Tenders"}
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
async def read_item(item_id: str):
|
||||
return items[item_id]
|
||||
14
docs/src/events/tutorial002.py
Normal file
14
docs/src/events/tutorial002.py
Normal file
@@ -0,0 +1,14 @@
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.on_event("shutdown")
|
||||
def startup_event():
|
||||
with open("log.txt", mode="a") as log:
|
||||
log.write("Application shutdown")
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items():
|
||||
return [{"name": "Foo"}]
|
||||
10
docs/src/header_params/tutorial003.py
Normal file
10
docs/src/header_params/tutorial003.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from typing import List
|
||||
|
||||
from fastapi import FastAPI, Header
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(x_token: List[str] = Header(None)):
|
||||
return {"X-Token values": x_token}
|
||||
11
docs/src/query_params_str_validations/tutorial011.py
Normal file
11
docs/src/query_params_str_validations/tutorial011.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from typing import List
|
||||
|
||||
from fastapi import FastAPI, Query
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(q: List[str] = Query(None)):
|
||||
query_items = {"q": q}
|
||||
return query_items
|
||||
@@ -1,13 +1,51 @@
|
||||
Coming soon...
|
||||
There are several things that you can configure in your FastAPI application.
|
||||
|
||||
```Python
|
||||
## Title, description, and version
|
||||
|
||||
You can set the:
|
||||
|
||||
* Title: used as your API's title/name, in OpenAPI and the automatic API docs UIs.
|
||||
* Description: the description of your API, in OpenAPI and the automatic API docs UIs.
|
||||
* Version: the version of your API, e.g. `v2` or `2.5.0`.
|
||||
* Useful for example if you had a previous version of the application, also using OpenAPI.
|
||||
|
||||
To set them, use the parameters `title`, `description`, and `version`:
|
||||
|
||||
```Python hl_lines="4 5 6"
|
||||
{!./src/application_configuration/tutorial001.py!}
|
||||
```
|
||||
|
||||
```Python
|
||||
With this configuration, the automatic API docs would look like:
|
||||
|
||||
<img src="/img/tutorial/application-configuration/image01.png">
|
||||
|
||||
## OpenAPI URL
|
||||
|
||||
By default, the OpenAPI schema is served at `/openapi.json`.
|
||||
|
||||
But you can configure it with the parameter `openapi_url`.
|
||||
|
||||
For example, to set it to be served at `/api/v1/openapi.json`:
|
||||
|
||||
```Python hl_lines="3"
|
||||
{!./src/application_configuration/tutorial002.py!}
|
||||
```
|
||||
|
||||
```Python
|
||||
If you want to disable the OpenAPI schema completely you can set `openapi_url=None`.
|
||||
|
||||
## Docs URLs
|
||||
|
||||
You can configure the two documentation user interfaces included:
|
||||
|
||||
* **Swagger UI**: served at `/docs`.
|
||||
* You can set its URL with the parameter `docs_url`.
|
||||
* You can disable it by setting `docs_url=None`.
|
||||
* ReDoc: served at `/redoc`.
|
||||
* You can set its URL with the parameter `redoc_url`.
|
||||
* You can disable it by setting `redoc_url=None`.
|
||||
|
||||
For example, to set Swagger UI to be served at `/documentation` and disable ReDoc:
|
||||
|
||||
```Python hl_lines="3"
|
||||
{!./src/application_configuration/tutorial003.py!}
|
||||
```
|
||||
|
||||
86
docs/tutorial/background-tasks.md
Normal file
86
docs/tutorial/background-tasks.md
Normal file
@@ -0,0 +1,86 @@
|
||||
You can define background tasks to be run *after* returning a response.
|
||||
|
||||
This is useful for operations that need to happen after a request, but that the client doesn't really have to be waiting for the operation to complete before receiving his response.
|
||||
|
||||
This includes, for example:
|
||||
|
||||
* Email notifications sent after performing an action:
|
||||
* As connecting to an email server and sending an email tends to be "slow" (several seconds), you can return the response right away and send the email notification in the background.
|
||||
* Processing data:
|
||||
* For example, let's say you receive a file that must go through a slow process, you can return a response of "Accepted" (HTTP 202) and process it in the background.
|
||||
|
||||
## Using `BackgroundTasks`
|
||||
|
||||
First, import `BackgroundTasks` and define a parameter in your *path operation function* with a type declaration of `BackgroundTasks`:
|
||||
|
||||
```Python hl_lines="1 13"
|
||||
{!./src/background_tasks/tutorial001.py!}
|
||||
```
|
||||
|
||||
**FastAPI** will create the object of type `BackgroundTasks` for you and pass it as that parameter.
|
||||
|
||||
!!! tip
|
||||
You declare a parameter of `BackgroundTasks` and use it in a very similar way as to when <a href="/tutorial/using-request-directly/" target="_blank">using the `Request` directly</a>.
|
||||
|
||||
|
||||
## Create a task function
|
||||
|
||||
Create a function to be run as the background task.
|
||||
|
||||
It is just a standard function that can receive parameters.
|
||||
|
||||
It can be an `async def` or normal `def` function, **FastAPI** will know how to handle it correctly.
|
||||
|
||||
In this case, the task function will write to a file (simulating sending an email).
|
||||
|
||||
And as the write operation doesn't use `async` and `await`, we define the function with normal `def`:
|
||||
|
||||
```Python hl_lines="6 7 8 9"
|
||||
{!./src/background_tasks/tutorial001.py!}
|
||||
```
|
||||
|
||||
## Add the background task
|
||||
|
||||
Inside of your *path operation function*, pass your task function to the *background tasks* object with the method `.add_task()`:
|
||||
|
||||
```Python hl_lines="14"
|
||||
{!./src/background_tasks/tutorial001.py!}
|
||||
```
|
||||
|
||||
`.add_task()` receives as arguments:
|
||||
|
||||
* A task function to be run in the background (`write_notification`).
|
||||
* Any sequence of arguments that should be passed to the task function in order (`email`).
|
||||
* Any keyword arguments that should be passed to the task function (`message="some notification"`).
|
||||
|
||||
## Dependency Injection
|
||||
|
||||
Using `BackgroundTasks` also works with the dependency injection system, you can declare a parameter of type `BackgroundTasks` at multiple levels: in a *path operation function*, in a dependency (dependable), in a sub-dependency, etc.
|
||||
|
||||
**FastAPI** knows what to do in each case and how to re-use the same object, so that all the background tasks are merged together and are run in the background afterwards:
|
||||
|
||||
```Python hl_lines="11 14 20 23"
|
||||
{!./src/background_tasks/tutorial002.py!}
|
||||
```
|
||||
|
||||
In this example, the messages will be written to the `log.txt` file *after* the response is sent.
|
||||
|
||||
If there was a query in the request, it will be written to the log in a background task.
|
||||
|
||||
And then another background task generated at the *path operation function* will write a message using the `email` path parameter.
|
||||
|
||||
## Technical Details
|
||||
|
||||
The class `BackgroundTasks` comes directly from <a href="https://www.starlette.io/background/" target="_blank">`starlette.background`</a>.
|
||||
|
||||
It is imported/included directly into FastAPI so that you can import it from `fastapi` and avoid accidentally importing the alternative `BackgroundTask` (without the `s` at the end) from `starlette.background`.
|
||||
|
||||
By only using `BackgroundTasks` (and not `BackgroundTask`), it's then possible to use it as a *path operation function* parameter and have **FastAPI** handle the rest for you, just like when using the `Request` object directly.
|
||||
|
||||
It's still possible to use `BackgroundTask` alone in FastAPI, but you have to create the object in your code and return a Starlette `Response` including it.
|
||||
|
||||
You can see more details in <a href="https://www.starlette.io/background/" target="_blank">Starlette's official docs for Background Tasks</a>.
|
||||
|
||||
## Recap
|
||||
|
||||
Import and use `BackgroundTasks` with parameters in *path operation functions* and dependencies to add background tasks.
|
||||
@@ -2,6 +2,8 @@ If you are building an application or a web API, it's rarely the case that you c
|
||||
|
||||
**FastAPI** provides a convenience tool to structure your application while keeping all the flexibility.
|
||||
|
||||
!!! info
|
||||
If you come from Flask, this would be the equivalent of Flask's Blueprints.
|
||||
|
||||
## An example file structure
|
||||
|
||||
@@ -99,13 +101,12 @@ It's all the same structure as with `app/routers/users.py`.
|
||||
|
||||
But let's say that this time we are more lazy.
|
||||
|
||||
And we don't want to have to explicitly type `/items/` in every path operation, we can do it later:
|
||||
And we don't want to have to explicitly type `/items/` and `tags=["items"]` in every *path operation* (we will be able to do it later):
|
||||
|
||||
```Python hl_lines="6 11 16"
|
||||
{!./src/bigger_applications/app/routers/items.py!}
|
||||
```
|
||||
|
||||
|
||||
## The main `FastAPI`
|
||||
|
||||
Now, let's see the module at `app/main.py`.
|
||||
@@ -118,17 +119,17 @@ This will be the main file in your application that ties everything together.
|
||||
|
||||
You import and create a `FastAPI` class as normally:
|
||||
|
||||
```Python hl_lines="1 6"
|
||||
```Python hl_lines="1 5"
|
||||
{!./src/bigger_applications/app/main.py!}
|
||||
```
|
||||
|
||||
### Import the `APIRouter`
|
||||
|
||||
But this time we are not adding path operations directly with the `FastAPI` `app`.
|
||||
But this time we are not adding *path operations* directly with the `FastAPI` `app`.
|
||||
|
||||
We import the `APIRouter`s from the other files:
|
||||
We import the other submodules that have `APIRouter`s:
|
||||
|
||||
```Python hl_lines="3 4"
|
||||
```Python hl_lines="3"
|
||||
{!./src/bigger_applications/app/main.py!}
|
||||
```
|
||||
|
||||
@@ -140,22 +141,21 @@ As the file `app/routers/items.py` is part of the same Python package, we can im
|
||||
The section:
|
||||
|
||||
```Python
|
||||
from .routers.items import router
|
||||
from .routers import items, users
|
||||
```
|
||||
|
||||
Means:
|
||||
|
||||
* Starting in the same package that this module (the file `app/main.py`) lives in (the directory `app/`)...
|
||||
* look for the subpackage `routers` (the directory at `app/routers/`)...
|
||||
* and from it, the submodule `items` (the file at `app/routers/items.py`)...
|
||||
* and from that submodule, import the variable `router`.
|
||||
* and from it, import the submodule `items` (the file at `app/routers/items.py`) and `users` (the file at `app/routers/users.py`)...
|
||||
|
||||
The variable `router` is the same one we created in the file `app/routers/items.py`. It's an `APIRouter`.
|
||||
The module `items` will have a variable `router` (`items.router`). This is the same one we created in the file `app/routers/items.py`. It's an `APIRouter`. The same for the module `users`.
|
||||
|
||||
We could also import it like:
|
||||
We could also import them like:
|
||||
|
||||
```Python
|
||||
from app.routers.items import router
|
||||
from app.routers import items, users
|
||||
```
|
||||
|
||||
!!! info
|
||||
@@ -168,40 +168,43 @@ from app.routers.items import router
|
||||
|
||||
### Avoid name collisions
|
||||
|
||||
We are importing a variable named `router` from the submodule `items`.
|
||||
We are importing the submodule `items` directly, instead of importing just its variable `router`.
|
||||
|
||||
But we also have another variable named `router` in the submodule `users`.
|
||||
This is because we also have another variable named `router` in the submodule `users`.
|
||||
|
||||
If we import one after the other, like:
|
||||
If we had imported one after the other, like:
|
||||
|
||||
```Python
|
||||
from .routers.items import router
|
||||
from .routers.users import router
|
||||
```
|
||||
|
||||
The `router` from `users` will overwrite the one form `items` and we won't be able to use them at the same time.
|
||||
The `router` from `users` would overwrite the one from `items` and we wouldn't be able to use them at the same time.
|
||||
|
||||
So, to be able to use both of them in the same file, we rename them while importing them using `as`:
|
||||
So, to be able to use both of them in the same file, we import the submodules directly:
|
||||
|
||||
```Python hl_lines="3 4"
|
||||
```Python hl_lines="3"
|
||||
{!./src/bigger_applications/app/main.py!}
|
||||
```
|
||||
|
||||
|
||||
### Include an `APIRouter`
|
||||
|
||||
Now, let's include the router from the submodule `users`, now in the variable `users_router`:
|
||||
Now, let's include the `router` from the submodule `users`:
|
||||
|
||||
```Python hl_lines="8"
|
||||
{!./src/bigger_applications/app/main.py!}
|
||||
```
|
||||
|
||||
!!! info
|
||||
`users.router` contains the `APIRouter` inside of the file `app/routers/users.py`.
|
||||
|
||||
With `app.include_router()` we can add an `APIRouter` to the main `FastAPI` application.
|
||||
|
||||
It will include all the routes from that router as part of it.
|
||||
|
||||
!!! note "Technical Details"
|
||||
It will actually internally create a path operation for each path operation that was declared in the `APIRouter`.
|
||||
It will actually internally create a *path operation* for each *path operation* that was declared in the `APIRouter`.
|
||||
|
||||
So, behind the scenes, it will actually work as if everything was the same single app.
|
||||
|
||||
@@ -216,23 +219,25 @@ It will include all the routes from that router as part of it.
|
||||
|
||||
### Include an `APIRouter` with a prefix
|
||||
|
||||
Now, let's include the router form the `items` submodule, now in the variable `items_router`.
|
||||
Now, let's include the router form the `items` submodule.
|
||||
|
||||
But, remember that we were lazy and didn't add `/items/` to all the path operations?
|
||||
But, remember that we were lazy and didn't add `/items/` nor `tags` to all the *path operations*?
|
||||
|
||||
We can add a prefix to all the path operations using the parameter `prefix` of `app.include_router()`.
|
||||
|
||||
As the path of each path operation has to start with `/`, like in:
|
||||
|
||||
```Python hl_lines="1"
|
||||
@router.get("/{item_id}", tags=["items"])
|
||||
@router.get("/{item_id}")
|
||||
async def read_item(item_id: str):
|
||||
...
|
||||
```
|
||||
|
||||
...the prefix must not include a final `/`.
|
||||
|
||||
So, the prefix in this case would be `/items`:
|
||||
So, the prefix in this case would be `/items`.
|
||||
|
||||
And we can also add a list of `tags` that will be applied to all the *path operations* included in this router:
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!./src/bigger_applications/app/main.py!}
|
||||
@@ -245,8 +250,12 @@ The end result is that the item paths are now:
|
||||
|
||||
...as we intended.
|
||||
|
||||
And they are marked with a list of tags that contain a single string `"items"`.
|
||||
|
||||
These "tags" are especially useful for the automatic interactive documentation systems (using OpenAPI).
|
||||
|
||||
!!! check
|
||||
The `prefix` parameter is (as in many other cases) just a feature from **FastAPI** to help you avoid code duplication.
|
||||
The `prefix` and `tags` parameters are (as in many other cases) just a feature from **FastAPI** to help you avoid code duplication.
|
||||
|
||||
|
||||
!!! tip
|
||||
@@ -274,11 +283,11 @@ The end result is that the item paths are now:
|
||||
Now, run `uvicorn`, using the module `app.main` and the variable `app`:
|
||||
|
||||
```bash
|
||||
uvicorn app.main:app --debug
|
||||
uvicorn app.main:app --reload
|
||||
```
|
||||
|
||||
And open the docs at <a href="http://127.0.0.1:8000/docs" target="_blank">http://127.0.0.1:8000/docs</a>.
|
||||
|
||||
You will see the automatic API docs, including the paths from all the submodules:
|
||||
You will see the automatic API docs, including the paths from all the submodules, using the correct paths (and prefixes) and the correct tags:
|
||||
|
||||
<img src="/img/tutorial/bigger-applications/image01.png">
|
||||
|
||||
43
docs/tutorial/events.md
Normal file
43
docs/tutorial/events.md
Normal file
@@ -0,0 +1,43 @@
|
||||
|
||||
You can define event handlers (functions) that need to be executed before the application starts up, or when the application is shutting down.
|
||||
|
||||
These functions can be declared with `async def` or normal `def`.
|
||||
|
||||
## `startup` event
|
||||
|
||||
To add a function that should be run before the application starts, declare it with the event `"startup"`:
|
||||
|
||||
```Python hl_lines="8"
|
||||
{!./src/events/tutorial001.py!}
|
||||
```
|
||||
|
||||
In this case, the `startup` event handler function will initialize the items "database" (just a `dict`) with some values.
|
||||
|
||||
You can add more than one event handler function.
|
||||
|
||||
And your application won't start receiving requests until all the `startup` event handlers have completed.
|
||||
|
||||
## `shutdown` event
|
||||
|
||||
To add a function that should be run when the application is shutting down, declare it with the event `"shutdown"`:
|
||||
|
||||
```Python hl_lines="6"
|
||||
{!./src/events/tutorial002.py!}
|
||||
```
|
||||
|
||||
Here, the `shutdown` event handler function will write a text line `"Application shutdown"` to a file `log.txt`.
|
||||
|
||||
!!! info
|
||||
In the `open()` function, the `mode="a"` means "append", so, the line will be added after whatever is on that file, without overwriting the previous contents.
|
||||
|
||||
!!! tip
|
||||
Notice that in this case we are using a standard Python `open()` function that interacts with a file.
|
||||
|
||||
So, it involves I/O (input/output), that requires "waiting" for things to be written to disk.
|
||||
|
||||
But `open()` doesn't use `async` and `await`.
|
||||
|
||||
So, we declare the event handler function with standard `def` instead of `async def`.
|
||||
|
||||
!!! info
|
||||
You can read more about these event handlers in <a href="https://www.starlette.io/events/" target="_blank">Starlette's Events' docs</a>.
|
||||
@@ -9,7 +9,7 @@ Copy that to a file `main.py`.
|
||||
Run the live server:
|
||||
|
||||
```bash
|
||||
uvicorn main:app --debug
|
||||
uvicorn main:app --reload
|
||||
```
|
||||
|
||||
!!! note
|
||||
@@ -17,7 +17,7 @@ uvicorn main:app --debug
|
||||
|
||||
* `main`: the file `main.py` (the Python "module").
|
||||
* `app`: the object created inside of `main.py` with the line `app = FastAPI()`.
|
||||
* `--debug`: make the server restart after code changes. Only use for development.
|
||||
* `--reload`: make the server restart after code changes. Only use for development.
|
||||
|
||||
You will see an output like:
|
||||
|
||||
@@ -146,7 +146,7 @@ This will be the main point of interaction to create all your API.
|
||||
This `app` is the same one referred by `uvicorn` in the command:
|
||||
|
||||
```bash
|
||||
uvicorn main:app --debug
|
||||
uvicorn main:app --reload
|
||||
```
|
||||
|
||||
If you create your app like:
|
||||
@@ -158,7 +158,7 @@ If you create your app like:
|
||||
And put it in a file `main.py`, then you would call `uvicorn` like:
|
||||
|
||||
```bash
|
||||
uvicorn main:my_awesome_api --debug
|
||||
uvicorn main:my_awesome_api --reload
|
||||
```
|
||||
|
||||
### Step 3: create a path operation
|
||||
@@ -311,4 +311,4 @@ There are many other objects and models that will be automatically converted to
|
||||
* Create an `app` instance.
|
||||
* Write a **path operation decorator** (like `@app.get("/")`).
|
||||
* Write a **path operation function** (like `def root(): ...` above).
|
||||
* Run the debugging server (like `uvicorn main:app --debug`).
|
||||
* Run the development server (like `uvicorn main:app --reload`).
|
||||
@@ -47,6 +47,39 @@ If for some reason you need to disable automatic conversion of underscores to hy
|
||||
!!! warning
|
||||
Before setting `convert_underscores` to `False`, bear in mind that some HTTP proxies and servers disallow the usage of headers with underscores.
|
||||
|
||||
|
||||
## Duplicate headers
|
||||
|
||||
It is possible to receive duplicate headers. That means, the same header with multiple values.
|
||||
|
||||
You can define those cases using a list in the type declaration.
|
||||
|
||||
You will receive all the values from the duplicate header as a Python `list`.
|
||||
|
||||
For example, to declare a header of `X-Token` that can appear more than once, you can write:
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!./src/header_params/tutorial003.py!}
|
||||
```
|
||||
|
||||
If you communicate with that *path operation* sending two HTTP headers like:
|
||||
|
||||
```
|
||||
X-Token: foo
|
||||
X-Token: bar
|
||||
```
|
||||
|
||||
The response would be like:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"X-Token values": [
|
||||
"bar",
|
||||
"foo"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Recap
|
||||
|
||||
Declare headers with `Header`, using the same common pattern as `Query`, `Path` and `Cookie`.
|
||||
|
||||
@@ -13,7 +13,7 @@ All the code blocks can be copied and used directly (they are actually tested Py
|
||||
To run any of the examples, copy the code to a file `main.py`, and start `uvicorn` with:
|
||||
|
||||
```bash
|
||||
uvicorn main:app --debug
|
||||
uvicorn main:app --reload
|
||||
```
|
||||
|
||||
It is **HIGHLY encouraged** that you write or copy the code, edit it and run it locally.
|
||||
|
||||
@@ -124,6 +124,43 @@ So, when you need to declare a value as required while using `Query`, you can us
|
||||
|
||||
This will let **FastAPI** know that this parameter is required.
|
||||
|
||||
## Query parameter list / multiple values
|
||||
|
||||
When you define a query parameter explicitly with `Query` you can also declare it to receive a list of values, or said in other way, to receive multiple values.
|
||||
|
||||
For example, to declare a query parameter `q` that can appear multiple times in the URL, you can write:
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!./src/query_params_str_validations/tutorial011.py!}
|
||||
```
|
||||
|
||||
Then, with a URL like:
|
||||
|
||||
```
|
||||
http://localhost:8000/items/?q=foo&q=bar
|
||||
```
|
||||
|
||||
you would receive the multiple `q` *query parameters'* values (`foo` and `bar`) in a Python `list` inside your *path operation function*, in the *function parameter* `q`.
|
||||
|
||||
So, the response to that URL would be:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"q": [
|
||||
"foo",
|
||||
"bar"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
To declare a query parameter with a type of `list`, like in the example above, you need to explicitly use `Query`, otherwise it would be interpreted as a request body.
|
||||
|
||||
|
||||
The interactive API docs will update accordingly, to allow multiple values:
|
||||
|
||||
<img src="/img/tutorial/query-params-str-validations/image02.png">
|
||||
|
||||
## Declare more metadata
|
||||
|
||||
You can add more information about the parameter.
|
||||
|
||||
@@ -27,7 +27,7 @@ Copy the example in a file `main.py`:
|
||||
Run the example with:
|
||||
|
||||
```bash
|
||||
uvicorn main:app --debug
|
||||
uvicorn main:app --reload
|
||||
```
|
||||
|
||||
## Check it
|
||||
|
||||
@@ -270,7 +270,7 @@ You can copy it, let's say, to a file `main.py`.
|
||||
Then you can run it with Uvicorn:
|
||||
|
||||
```bash
|
||||
uvicorn main:app --debug
|
||||
uvicorn main:app --reload
|
||||
```
|
||||
|
||||
And then, you can open your browser at <a href="http://127.0.0.1:8000/docs" target="_blank">http://127.0.0.1:8000/docs</a>.
|
||||
|
||||
@@ -75,7 +75,7 @@ Here you need to make sure you use the same path that you used for the `openapi_
|
||||
Now, run `uvicorn`, if your file is at `main.py`, it would be:
|
||||
|
||||
```bash
|
||||
uvicorn main:app --debug
|
||||
uvicorn main:app --reload
|
||||
```
|
||||
|
||||
And open the docs at <a href="http://127.0.0.1:8000/docs" target="_blank">http://127.0.0.1:8000/docs</a>.
|
||||
|
||||
@@ -69,7 +69,7 @@ To learn more about the options, check Starlette's documentation for:
|
||||
If your file is named `main.py`, run your application with:
|
||||
|
||||
```bash
|
||||
uvicorn main:app --debug
|
||||
uvicorn main:app --reload
|
||||
```
|
||||
|
||||
Open your browser at <a href="http://127.0.0.1:8000" target="_blank">http://127.0.0.1:8000</a>.
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
"""FastAPI framework, high performance, easy to learn, fast to code, ready for production"""
|
||||
|
||||
__version__ = "0.7.1"
|
||||
__version__ = "0.10.0"
|
||||
|
||||
from starlette.background import BackgroundTasks
|
||||
|
||||
from .applications import FastAPI
|
||||
from .routing import APIRouter
|
||||
from .params import Body, Path, Query, Header, Cookie, Form, File, Security, Depends
|
||||
from .exceptions import HTTPException
|
||||
from .datastructures import UploadFile
|
||||
from .exceptions import HTTPException
|
||||
from .params import Body, Cookie, Depends, File, Form, Header, Path, Query, Security
|
||||
from .routing import APIRouter
|
||||
|
||||
@@ -176,8 +176,10 @@ class FastAPI(Starlette):
|
||||
|
||||
return decorator
|
||||
|
||||
def include_router(self, router: routing.APIRouter, *, prefix: str = "") -> None:
|
||||
self.router.include_router(router, prefix=prefix)
|
||||
def include_router(
|
||||
self, router: routing.APIRouter, *, prefix: str = "", tags: List[str] = None
|
||||
) -> None:
|
||||
self.router.include_router(router, prefix=prefix, tags=tags)
|
||||
|
||||
def get(
|
||||
self,
|
||||
|
||||
@@ -26,6 +26,7 @@ class Dependant:
|
||||
name: str = None,
|
||||
call: Callable = None,
|
||||
request_param_name: str = None,
|
||||
background_tasks_param_name: str = None,
|
||||
) -> None:
|
||||
self.path_params = path_params or []
|
||||
self.query_params = query_params or []
|
||||
@@ -35,5 +36,6 @@ class Dependant:
|
||||
self.dependencies = dependencies or []
|
||||
self.security_requirements = security_schemes or []
|
||||
self.request_param_name = request_param_name
|
||||
self.background_tasks_param_name = background_tasks_param_name
|
||||
self.name = name
|
||||
self.call = call
|
||||
|
||||
@@ -3,7 +3,18 @@ import inspect
|
||||
from copy import deepcopy
|
||||
from datetime import date, datetime, time, timedelta
|
||||
from decimal import Decimal
|
||||
from typing import Any, Callable, Dict, List, Mapping, Sequence, Tuple, Type, Union
|
||||
from typing import (
|
||||
Any,
|
||||
Callable,
|
||||
Dict,
|
||||
List,
|
||||
Mapping,
|
||||
Optional,
|
||||
Sequence,
|
||||
Tuple,
|
||||
Type,
|
||||
Union,
|
||||
)
|
||||
from uuid import UUID
|
||||
|
||||
from fastapi import params
|
||||
@@ -16,6 +27,7 @@ from pydantic.errors import MissingError
|
||||
from pydantic.fields import Field, Required, Shape
|
||||
from pydantic.schema import get_annotation_from_schema
|
||||
from pydantic.utils import lenient_issubclass
|
||||
from starlette.background import BackgroundTasks
|
||||
from starlette.concurrency import run_in_threadpool
|
||||
from starlette.datastructures import UploadFile
|
||||
from starlette.requests import Headers, QueryParams, Request
|
||||
@@ -125,6 +137,8 @@ def get_dependant(*, path: str, call: Callable, name: str = None) -> Dependant:
|
||||
)
|
||||
elif lenient_issubclass(param.annotation, Request):
|
||||
dependant.request_param_name = param_name
|
||||
elif lenient_issubclass(param.annotation, BackgroundTasks):
|
||||
dependant.background_tasks_param_name = param_name
|
||||
elif not isinstance(param.default, params.Depends):
|
||||
add_param_to_body_fields(param=param, dependant=dependant)
|
||||
return dependant
|
||||
@@ -215,13 +229,20 @@ def is_coroutine_callable(call: Callable) -> bool:
|
||||
|
||||
|
||||
async def solve_dependencies(
|
||||
*, request: Request, dependant: Dependant, body: Dict[str, Any] = None
|
||||
) -> Tuple[Dict[str, Any], List[ErrorWrapper]]:
|
||||
*,
|
||||
request: Request,
|
||||
dependant: Dependant,
|
||||
body: Dict[str, Any] = None,
|
||||
background_tasks: BackgroundTasks = None,
|
||||
) -> Tuple[Dict[str, Any], List[ErrorWrapper], Optional[BackgroundTasks]]:
|
||||
values: Dict[str, Any] = {}
|
||||
errors: List[ErrorWrapper] = []
|
||||
for sub_dependant in dependant.dependencies:
|
||||
sub_values, sub_errors = await solve_dependencies(
|
||||
request=request, dependant=sub_dependant, body=body
|
||||
sub_values, sub_errors, background_tasks = await solve_dependencies(
|
||||
request=request,
|
||||
dependant=sub_dependant,
|
||||
body=body,
|
||||
background_tasks=background_tasks,
|
||||
)
|
||||
if sub_errors:
|
||||
errors.extend(sub_errors)
|
||||
@@ -258,7 +279,11 @@ async def solve_dependencies(
|
||||
errors.extend(body_errors)
|
||||
if dependant.request_param_name:
|
||||
values[dependant.request_param_name] = request
|
||||
return values, errors
|
||||
if dependant.background_tasks_param_name:
|
||||
if background_tasks is None:
|
||||
background_tasks = BackgroundTasks()
|
||||
values[dependant.background_tasks_param_name] = background_tasks
|
||||
return values, errors, background_tasks
|
||||
|
||||
|
||||
def request_params_to_args(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from enum import Enum
|
||||
from types import GeneratorType
|
||||
from typing import Any, Set
|
||||
from typing import Any, List, Set
|
||||
|
||||
from pydantic import BaseModel
|
||||
from pydantic.json import ENCODERS_BY_TYPE
|
||||
@@ -70,7 +70,7 @@ def jsonable_encoder(
|
||||
)
|
||||
)
|
||||
return encoded_list
|
||||
errors = []
|
||||
errors: List[Exception] = []
|
||||
try:
|
||||
if custom_encoder and type(obj) in custom_encoder:
|
||||
encoder = custom_encoder[type(obj)]
|
||||
|
||||
@@ -68,7 +68,7 @@ def get_app(
|
||||
raise HTTPException(
|
||||
status_code=400, detail="There was an error parsing the body"
|
||||
)
|
||||
values, errors = await solve_dependencies(
|
||||
values, errors, background_tasks = await solve_dependencies(
|
||||
request=request, dependant=dependant, body=body
|
||||
)
|
||||
if errors:
|
||||
@@ -83,11 +83,17 @@ def get_app(
|
||||
else:
|
||||
raw_response = await run_in_threadpool(dependant.call, **values)
|
||||
if isinstance(raw_response, Response):
|
||||
if raw_response.background is None:
|
||||
raw_response.background = background_tasks
|
||||
return raw_response
|
||||
response_data = serialize_response(
|
||||
field=response_field, response=raw_response
|
||||
)
|
||||
return content_type(content=response_data, status_code=status_code)
|
||||
return content_type(
|
||||
content=response_data,
|
||||
status_code=status_code,
|
||||
background=background_tasks,
|
||||
)
|
||||
|
||||
return app
|
||||
|
||||
@@ -237,7 +243,9 @@ class APIRouter(routing.Router):
|
||||
|
||||
return decorator
|
||||
|
||||
def include_router(self, router: "APIRouter", *, prefix: str = "") -> None:
|
||||
def include_router(
|
||||
self, router: "APIRouter", *, prefix: str = "", tags: List[str] = None
|
||||
) -> None:
|
||||
if prefix:
|
||||
assert prefix.startswith("/"), "A path prefix must start with '/'"
|
||||
assert not prefix.endswith(
|
||||
@@ -250,7 +258,7 @@ class APIRouter(routing.Router):
|
||||
route.endpoint,
|
||||
response_model=route.response_model,
|
||||
status_code=route.status_code,
|
||||
tags=route.tags or [],
|
||||
tags=(route.tags or []) + (tags or []),
|
||||
summary=route.summary,
|
||||
description=route.description,
|
||||
response_description=route.response_description,
|
||||
@@ -269,6 +277,10 @@ class APIRouter(routing.Router):
|
||||
include_in_schema=route.include_in_schema,
|
||||
name=route.name,
|
||||
)
|
||||
elif isinstance(route, routing.WebSocketRoute):
|
||||
self.add_websocket_route(
|
||||
prefix + route.path, route.endpoint, name=route.name
|
||||
)
|
||||
|
||||
def get(
|
||||
self,
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
from .api_key import APIKeyQuery, APIKeyHeader, APIKeyCookie
|
||||
from .api_key import APIKeyCookie, APIKeyHeader, APIKeyQuery
|
||||
from .http import (
|
||||
HTTPAuthorizationCredentials,
|
||||
HTTPBasic,
|
||||
HTTPBasicCredentials,
|
||||
HTTPBearer,
|
||||
HTTPDigest,
|
||||
HTTPBasicCredentials,
|
||||
HTTPAuthorizationCredentials,
|
||||
)
|
||||
from .oauth2 import OAuth2PasswordRequestForm, OAuth2, OAuth2PasswordBearer
|
||||
from .oauth2 import OAuth2, OAuth2PasswordBearer, OAuth2PasswordRequestForm
|
||||
from .open_id_connect_url import OpenIdConnect
|
||||
|
||||
@@ -59,10 +59,12 @@ nav:
|
||||
- SQL (Relational) Databases: 'tutorial/sql-databases.md'
|
||||
- NoSQL (Distributed / Big Data) Databases: 'tutorial/nosql-databases.md'
|
||||
- Bigger Applications - Multiple Files: 'tutorial/bigger-applications.md'
|
||||
- Background Tasks: 'tutorial/background-tasks.md'
|
||||
- Sub Applications - Behind a Proxy: 'tutorial/sub-applications-proxy.md'
|
||||
- Application Configuration: 'tutorial/application-configuration.md'
|
||||
- GraphQL: 'tutorial/graphql.md'
|
||||
- WebSockets: 'tutorial/websockets.md'
|
||||
- 'Events: startup - shutdown': 'tutorial/events.md'
|
||||
- Debugging: 'tutorial/debugging.md'
|
||||
- Concurrency and async / await: 'async.md'
|
||||
- Deployment: 'deployment.md'
|
||||
|
||||
@@ -62,7 +62,7 @@ class FakeDB:
|
||||
"johndoe": {
|
||||
"username": "johndoe",
|
||||
"password": "shouldbehashed",
|
||||
"fist_name": "John",
|
||||
"first_name": "John",
|
||||
"last_name": "Doe",
|
||||
}
|
||||
}
|
||||
@@ -87,7 +87,7 @@ class TokenUserData(BaseModel):
|
||||
class UserInDB(BaseModel):
|
||||
username: str
|
||||
password: str
|
||||
fist_name: str
|
||||
first_name: str
|
||||
last_name: str
|
||||
|
||||
|
||||
@@ -109,7 +109,7 @@ def require_user(
|
||||
|
||||
class UserOut(BaseModel):
|
||||
username: str
|
||||
fist_name: str
|
||||
first_name: str
|
||||
last_name: str
|
||||
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ classifiers = [
|
||||
]
|
||||
requires = [
|
||||
"starlette ==0.11.1",
|
||||
"pydantic >=0.17,<=0.18.2"
|
||||
"pydantic >=0.17,<=0.21.0"
|
||||
]
|
||||
description-file = "README.md"
|
||||
requires-python = ">=3.6"
|
||||
|
||||
0
scripts/build-docs.sh
Normal file → Executable file
0
scripts/build-docs.sh
Normal file → Executable file
0
scripts/docs-live.sh
Normal file → Executable file
0
scripts/docs-live.sh
Normal file → Executable file
2
scripts/lint.sh
Normal file → Executable file
2
scripts/lint.sh
Normal file → Executable file
@@ -3,4 +3,4 @@ set -x
|
||||
|
||||
autoflake --remove-all-unused-imports --recursive --remove-unused-variables --in-place docs/src/ fastapi tests --exclude=__init__.py
|
||||
black fastapi tests docs/src
|
||||
isort --multi-line=3 --trailing-comma --force-grid-wrap=0 --combine-as --line-width 88 --recursive --apply fastapi tests docs/src
|
||||
isort --multi-line=3 --trailing-comma --force-grid-wrap=0 --combine-as --line-width 88 --recursive --thirdparty fastapi --apply fastapi tests docs/src
|
||||
|
||||
0
scripts/netlify-docs.sh
Normal file → Executable file
0
scripts/netlify-docs.sh
Normal file → Executable file
0
scripts/test-cov-html.sh
Normal file → Executable file
0
scripts/test-cov-html.sh
Normal file → Executable file
@@ -13,10 +13,10 @@ fi
|
||||
|
||||
export PYTHONPATH=./docs/src
|
||||
pytest --cov=fastapi --cov=tests --cov=docs/src --cov-report=term-missing ${@}
|
||||
mypy fastapi --disallow-untyped-defs
|
||||
mypy fastapi --disallow-untyped-defs --follow-imports=skip
|
||||
if [ "${PYTHON_VERSION}" = '3.7' ]; then
|
||||
echo "Skipping 'black' on 3.7. See issue https://github.com/ambv/black/issues/494"
|
||||
else
|
||||
black fastapi tests --check
|
||||
fi
|
||||
isort --multi-line=3 --trailing-comma --force-grid-wrap=0 --combine-as --line-width 88 --recursive --check-only fastapi tests
|
||||
isort --multi-line=3 --trailing-comma --force-grid-wrap=0 --combine-as --line-width 88 --recursive --check-only --thirdparty fastapi fastapi tests
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
from starlette.testclient import TestClient
|
||||
|
||||
from background_tasks.tutorial001 import app
|
||||
|
||||
client = TestClient(app)
|
||||
|
||||
|
||||
def test():
|
||||
log = Path("log.txt")
|
||||
if log.is_file():
|
||||
os.remove(log) # pragma: no cover
|
||||
response = client.post("/send-notification/foo@example.com")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {"message": "Notification sent in the background"}
|
||||
with open("./log.txt") as f:
|
||||
assert "notification for foo@example.com: some notification" in f.read()
|
||||
@@ -0,0 +1,19 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
from starlette.testclient import TestClient
|
||||
|
||||
from background_tasks.tutorial002 import app
|
||||
|
||||
client = TestClient(app)
|
||||
|
||||
|
||||
def test():
|
||||
log = Path("log.txt")
|
||||
if log.is_file():
|
||||
os.remove(log) # pragma: no cover
|
||||
response = client.post("/send-notification/foo@example.com?q=some-query")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {"message": "Message sent"}
|
||||
with open("./log.txt") as f:
|
||||
assert "found query: some-query\nmessage to foo@example.com" in f.read()
|
||||
0
tests/test_tutorial/test_events/__init__.py
Normal file
0
tests/test_tutorial/test_events/__init__.py
Normal file
79
tests/test_tutorial/test_events/test_tutorial001.py
Normal file
79
tests/test_tutorial/test_events/test_tutorial001.py
Normal file
@@ -0,0 +1,79 @@
|
||||
from starlette.testclient import TestClient
|
||||
|
||||
from events.tutorial001 import app
|
||||
|
||||
openapi_schema = {
|
||||
"openapi": "3.0.2",
|
||||
"info": {"title": "Fast API", "version": "0.1.0"},
|
||||
"paths": {
|
||||
"/items/{item_id}": {
|
||||
"get": {
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {"application/json": {"schema": {}}},
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
"summary": "Read Item Get",
|
||||
"operationId": "read_item_items__item_id__get",
|
||||
"parameters": [
|
||||
{
|
||||
"required": True,
|
||||
"schema": {"title": "Item_Id", "type": "string"},
|
||||
"name": "item_id",
|
||||
"in": "path",
|
||||
}
|
||||
],
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"ValidationError": {
|
||||
"title": "ValidationError",
|
||||
"required": ["loc", "msg", "type"],
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"loc": {
|
||||
"title": "Location",
|
||||
"type": "array",
|
||||
"items": {"type": "string"},
|
||||
},
|
||||
"msg": {"title": "Message", "type": "string"},
|
||||
"type": {"title": "Error Type", "type": "string"},
|
||||
},
|
||||
},
|
||||
"HTTPValidationError": {
|
||||
"title": "HTTPValidationError",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"detail": {
|
||||
"title": "Detail",
|
||||
"type": "array",
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"},
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def test_events():
|
||||
with TestClient(app) as client:
|
||||
response = client.get("/openapi.json")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == openapi_schema
|
||||
response = client.get("/items/foo")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {"name": "Fighters"}
|
||||
34
tests/test_tutorial/test_events/test_tutorial002.py
Normal file
34
tests/test_tutorial/test_events/test_tutorial002.py
Normal file
@@ -0,0 +1,34 @@
|
||||
from starlette.testclient import TestClient
|
||||
|
||||
from events.tutorial002 import app
|
||||
|
||||
openapi_schema = {
|
||||
"openapi": "3.0.2",
|
||||
"info": {"title": "Fast API", "version": "0.1.0"},
|
||||
"paths": {
|
||||
"/items/": {
|
||||
"get": {
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {"application/json": {"schema": {}}},
|
||||
}
|
||||
},
|
||||
"summary": "Read Items Get",
|
||||
"operationId": "read_items_items__get",
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def test_events():
|
||||
with TestClient(app) as client:
|
||||
response = client.get("/openapi.json")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == openapi_schema
|
||||
response = client.get("/items/")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == [{"name": "Foo"}]
|
||||
with open("log.txt") as log:
|
||||
assert "Application shutdown" in log.read()
|
||||
@@ -0,0 +1,88 @@
|
||||
from starlette.testclient import TestClient
|
||||
|
||||
from query_params_str_validations.tutorial011 import app
|
||||
|
||||
client = TestClient(app)
|
||||
|
||||
openapi_schema = {
|
||||
"openapi": "3.0.2",
|
||||
"info": {"title": "Fast API", "version": "0.1.0"},
|
||||
"paths": {
|
||||
"/items/": {
|
||||
"get": {
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {"application/json": {"schema": {}}},
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
"summary": "Read Items Get",
|
||||
"operationId": "read_items_items__get",
|
||||
"parameters": [
|
||||
{
|
||||
"required": False,
|
||||
"schema": {
|
||||
"title": "Q",
|
||||
"type": "array",
|
||||
"items": {"type": "string"},
|
||||
},
|
||||
"name": "q",
|
||||
"in": "query",
|
||||
}
|
||||
],
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
"schemas": {
|
||||
"ValidationError": {
|
||||
"title": "ValidationError",
|
||||
"required": ["loc", "msg", "type"],
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"loc": {
|
||||
"title": "Location",
|
||||
"type": "array",
|
||||
"items": {"type": "string"},
|
||||
},
|
||||
"msg": {"title": "Message", "type": "string"},
|
||||
"type": {"title": "Error Type", "type": "string"},
|
||||
},
|
||||
},
|
||||
"HTTPValidationError": {
|
||||
"title": "HTTPValidationError",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"detail": {
|
||||
"title": "Detail",
|
||||
"type": "array",
|
||||
"items": {"$ref": "#/components/schemas/ValidationError"},
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def test_openapi_schema():
|
||||
response = client.get("/openapi.json")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == openapi_schema
|
||||
|
||||
|
||||
def test_multi_query_values():
|
||||
url = "/items/?q=foo&q=bar"
|
||||
response = client.get(url)
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {"q": ["foo", "bar"]}
|
||||
53
tests/test_ws_router.py
Normal file
53
tests/test_ws_router.py
Normal file
@@ -0,0 +1,53 @@
|
||||
from fastapi import APIRouter, FastAPI
|
||||
from starlette.testclient import TestClient
|
||||
from starlette.websockets import WebSocket
|
||||
|
||||
router = APIRouter()
|
||||
prefix_router = APIRouter()
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.websocket_route("/")
|
||||
async def index(websocket: WebSocket):
|
||||
await websocket.accept()
|
||||
await websocket.send_text("Hello, world!")
|
||||
await websocket.close()
|
||||
|
||||
|
||||
@router.websocket_route("/router")
|
||||
async def routerindex(websocket: WebSocket):
|
||||
await websocket.accept()
|
||||
await websocket.send_text("Hello, router!")
|
||||
await websocket.close()
|
||||
|
||||
|
||||
@prefix_router.websocket_route("/")
|
||||
async def routerprefixindex(websocket: WebSocket):
|
||||
await websocket.accept()
|
||||
await websocket.send_text("Hello, router with prefix!")
|
||||
await websocket.close()
|
||||
|
||||
|
||||
app.include_router(router)
|
||||
app.include_router(prefix_router, prefix="/prefix")
|
||||
|
||||
|
||||
def test_app():
|
||||
client = TestClient(app)
|
||||
with client.websocket_connect("/") as websocket:
|
||||
data = websocket.receive_text()
|
||||
assert data == "Hello, world!"
|
||||
|
||||
|
||||
def test_router():
|
||||
client = TestClient(app)
|
||||
with client.websocket_connect("/router") as websocket:
|
||||
data = websocket.receive_text()
|
||||
assert data == "Hello, router!"
|
||||
|
||||
|
||||
def test_prefix_router():
|
||||
client = TestClient(app)
|
||||
with client.websocket_connect("/prefix/") as websocket:
|
||||
data = websocket.receive_text()
|
||||
assert data == "Hello, router with prefix!"
|
||||
Reference in New Issue
Block a user