Compare commits
220 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bf229ad5d8 | ||
|
|
d0319001be | ||
|
|
c4682af13d | ||
|
|
6ca3ce80e4 | ||
|
|
25e85c8522 | ||
|
|
6bf3ab3b7a | ||
|
|
f5ea5eef2a | ||
|
|
46a986cacf | ||
|
|
e620aeb46d | ||
|
|
d1e2e46b80 | ||
|
|
b1c4a8acd5 | ||
|
|
362e2cdc79 | ||
|
|
93e6a08acd | ||
|
|
3ec4342282 | ||
|
|
dc483478eb | ||
|
|
bdd251a05b | ||
|
|
195559ccba | ||
|
|
9a71672a95 | ||
|
|
7e48be1561 | ||
|
|
508f9ce954 | ||
|
|
afbdf2546f | ||
|
|
62df417807 | ||
|
|
09d2747a70 | ||
|
|
d3ea6f7514 | ||
|
|
02187636ea | ||
|
|
687065509b | ||
|
|
b30cca8e9e | ||
|
|
3906777065 | ||
|
|
d60a10fa59 | ||
|
|
54368e7b22 | ||
|
|
acc556e416 | ||
|
|
700585f99d | ||
|
|
4c2993f353 | ||
|
|
ea9277aab4 | ||
|
|
8d86fca027 | ||
|
|
fc0716a7dd | ||
|
|
1e593dc4d4 | ||
|
|
dcc1e1bcf8 | ||
|
|
06eb775c63 | ||
|
|
ab77c069d4 | ||
|
|
46fffc0e94 | ||
|
|
1c2cdb97e9 | ||
|
|
76b6fd5c18 | ||
|
|
a2fb716035 | ||
|
|
aa84ac8e3e | ||
|
|
4ed2bd1fea | ||
|
|
87b7a63ff2 | ||
|
|
06d0918c3d | ||
|
|
5b3adfe449 | ||
|
|
bdd794a0e6 | ||
|
|
f0df79aa91 | ||
|
|
c26f1760d4 | ||
|
|
e5fa4b0af6 | ||
|
|
a33c299fd7 | ||
|
|
6939621730 | ||
|
|
120ab08360 | ||
|
|
3f5521fdfb | ||
|
|
7244e4b612 | ||
|
|
d329745064 | ||
|
|
5f7fe926ab | ||
|
|
c8eea09664 | ||
|
|
5700d65188 | ||
|
|
46178a5347 | ||
|
|
bff5dbbf5d | ||
|
|
09cd7c47a1 | ||
|
|
e2fadcbc90 | ||
|
|
b3bb29afa8 | ||
|
|
c7db2ff858 | ||
|
|
2a7ef5504a | ||
|
|
27964c5ffd | ||
|
|
d262f6e929 | ||
|
|
d61f5e4b55 | ||
|
|
3ed112e8a9 | ||
|
|
9da626eb2c | ||
|
|
6f74c7327b | ||
|
|
360a2797c1 | ||
|
|
0552977cd6 | ||
|
|
bd407cc4ed | ||
|
|
83b1a117cc | ||
|
|
2a1ff213a0 | ||
|
|
62af6e0eeb | ||
|
|
15da01af5c | ||
|
|
d544bdf092 | ||
|
|
703ade7967 | ||
|
|
58f135ba2f | ||
|
|
713d374484 | ||
|
|
24e9ea28d3 | ||
|
|
cae53138b2 | ||
|
|
a49d45eaa9 | ||
|
|
3986f79029 | ||
|
|
7379fde5ee | ||
|
|
7b63bc5551 | ||
|
|
747ae8210f | ||
|
|
c651416e05 | ||
|
|
814f95e2bf | ||
|
|
d8716f94ae | ||
|
|
67f8cb3b4f | ||
|
|
5c2828bd13 | ||
|
|
b087246f26 | ||
|
|
219d299426 | ||
|
|
31da760729 | ||
|
|
fc89eb8f81 | ||
|
|
6fca1041e9 | ||
|
|
9db1f5641b | ||
|
|
c3beb56e63 | ||
|
|
325edd5f00 | ||
|
|
08322ef359 | ||
|
|
01b43e6e25 | ||
|
|
3cf92a156c | ||
|
|
f54d8d57a4 | ||
|
|
56ab106bbb | ||
|
|
e92b43b5c8 | ||
|
|
7c50025c47 | ||
|
|
adfbd27100 | ||
|
|
eada8bf7db | ||
|
|
440b7a8efa | ||
|
|
fcaff64646 | ||
|
|
d240421378 | ||
|
|
ca27317b65 | ||
|
|
ce02d3cb83 | ||
|
|
95475aaa9c | ||
|
|
7a8b054a12 | ||
|
|
7b2993682f | ||
|
|
73fad03b46 | ||
|
|
b0b88f9d5b | ||
|
|
49d33f9f70 | ||
|
|
1f27981045 | ||
|
|
f541d2c200 | ||
|
|
0e99b23ebc | ||
|
|
de341abe66 | ||
|
|
4a1648b04e | ||
|
|
5f13b53ea5 | ||
|
|
5189c93d85 | ||
|
|
e612989313 | ||
|
|
d675991a34 | ||
|
|
d03678dfbb | ||
|
|
6ff89284c5 | ||
|
|
7f673cf4e9 | ||
|
|
bac7230027 | ||
|
|
866af5bca6 | ||
|
|
f4be79be51 | ||
|
|
3797c04946 | ||
|
|
9a9bfd7f93 | ||
|
|
ada1ecdb00 | ||
|
|
3bbd38313b | ||
|
|
c1df0f6b84 | ||
|
|
0cd5485597 | ||
|
|
528ef7e079 | ||
|
|
8e3a7699a3 | ||
|
|
8998ccaffb | ||
|
|
2b7f201a44 | ||
|
|
192ebba2a2 | ||
|
|
8880c4cb03 | ||
|
|
6324be684f | ||
|
|
c705685394 | ||
|
|
945f401d8e | ||
|
|
f216d340ec | ||
|
|
a4558e7053 | ||
|
|
298f8478e2 | ||
|
|
b86d163470 | ||
|
|
9e2d37b89c | ||
|
|
97adadd9e1 | ||
|
|
26e3dffb37 | ||
|
|
aa7b4bd101 | ||
|
|
ffc4c716c0 | ||
|
|
ef7b6e8eaf | ||
|
|
596243f4a5 | ||
|
|
766bf1c5aa | ||
|
|
9e748dbca4 | ||
|
|
cefe6cf92c | ||
|
|
be3953499f | ||
|
|
546d233dec | ||
|
|
61dd36a945 | ||
|
|
27f9d55c3e | ||
|
|
906cc60f65 | ||
|
|
69afaf256f | ||
|
|
4ab349a2a8 | ||
|
|
9c258107b4 | ||
|
|
29a4f90bcd | ||
|
|
361fd00777 | ||
|
|
4c3cf31730 | ||
|
|
aad6b123f7 | ||
|
|
e40e87c662 | ||
|
|
84de980977 | ||
|
|
08484603ee | ||
|
|
ab6dd60997 | ||
|
|
c9ef7bd6dc | ||
|
|
88ece95a30 | ||
|
|
366c5db0bb | ||
|
|
46e3811f8d | ||
|
|
613e211d20 | ||
|
|
500f2b2ad4 | ||
|
|
5cf7718657 | ||
|
|
727b656c8d | ||
|
|
cc7102e9b8 | ||
|
|
5123915fe4 | ||
|
|
1b8bbd51d8 | ||
|
|
1e4f86db6d | ||
|
|
7391056daf | ||
|
|
6a274d18b4 | ||
|
|
62626b0175 | ||
|
|
c8df3ae57c | ||
|
|
6f7f9268f6 | ||
|
|
50653e205f | ||
|
|
50a280b17b | ||
|
|
c1da3b38a3 | ||
|
|
e68a68c97c | ||
|
|
907e613ff2 | ||
|
|
f0fc2fad2c | ||
|
|
ad471307e2 | ||
|
|
2bd775988f | ||
|
|
eda9b28338 | ||
|
|
7514ac6fb0 | ||
|
|
25fb4239cc | ||
|
|
65568065e0 | ||
|
|
95679ca5e6 | ||
|
|
84a300ef84 | ||
|
|
c6d28c8209 | ||
|
|
3984e9b8ac | ||
|
|
aa0bca7bb2 |
1
.gitignore
vendored
@@ -12,3 +12,4 @@ coverage.xml
|
||||
.netlify
|
||||
test.db
|
||||
log.txt
|
||||
Pipfile.lock
|
||||
|
||||
@@ -10,7 +10,7 @@ python:
|
||||
|
||||
install:
|
||||
- pip install flit
|
||||
- flit install
|
||||
- flit install --symlink
|
||||
|
||||
script:
|
||||
- bash scripts/test.sh
|
||||
@@ -20,6 +20,7 @@ after_script:
|
||||
|
||||
deploy:
|
||||
provider: script
|
||||
script: bash scripts/trigger-docker.sh
|
||||
script: bash scripts/deploy.sh
|
||||
on:
|
||||
branch: master
|
||||
tags: true
|
||||
python: "3.6"
|
||||
|
||||
1
CONTRIBUTING.md
Normal file
@@ -0,0 +1 @@
|
||||
Please read the [Development - Contributing](https://fastapi.tiangolo.com/contributing/) guidelines in the documentation site.
|
||||
5
Pipfile
@@ -25,9 +25,10 @@ sqlalchemy = "*"
|
||||
uvicorn = "*"
|
||||
|
||||
[packages]
|
||||
starlette = "==0.11.1"
|
||||
pydantic = "==0.21.0"
|
||||
starlette = "==0.12.7"
|
||||
pydantic = "==0.30.0"
|
||||
databases = {extras = ["sqlite"],version = "*"}
|
||||
hypercorn = "*"
|
||||
|
||||
[requires]
|
||||
python_version = "3.6"
|
||||
|
||||
868
Pipfile.lock
generated
@@ -1,868 +0,0 @@
|
||||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "24b3b7b88d3cbe671ddbe296e64c15f8558f0e5d5df977200119872a363aac13"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
"python_version": "3.6"
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"name": "pypi",
|
||||
"url": "https://pypi.org/simple",
|
||||
"verify_ssl": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"default": {
|
||||
"aiocontextvars": {
|
||||
"hashes": [
|
||||
"sha256:1e0ff5837c8b01c36a1107acdd0baf7853ebdf6c9fc43e8e311f4be37ac2038a",
|
||||
"sha256:6ff7aee14f549d52f0446cbb84d0deddcd3fc677bcf8fbc2ce13f5756d2064dc"
|
||||
],
|
||||
"markers": "python_version < '3.7'",
|
||||
"version": "==0.2.1"
|
||||
},
|
||||
"aiosqlite": {
|
||||
"hashes": [
|
||||
"sha256:af4fed9e778756fa0ffffc7a8b14c4d7b1a57155dc5669f18e45107313f6019e"
|
||||
],
|
||||
"version": "==0.9.0"
|
||||
},
|
||||
"contextvars": {
|
||||
"hashes": [
|
||||
"sha256:2341042e1c03a271813e07dba29b6b60fa85c1005ea5ed1638a076cf50b4d625"
|
||||
],
|
||||
"markers": "python_version < '3.7'",
|
||||
"version": "==2.3"
|
||||
},
|
||||
"databases": {
|
||||
"extras": [
|
||||
"sqlite"
|
||||
],
|
||||
"hashes": [
|
||||
"sha256:4a0f15669c390a04b439972426350c0ae921ddc08c42bd54f125eb2fb86ee728"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.2.0"
|
||||
},
|
||||
"dataclasses": {
|
||||
"hashes": [
|
||||
"sha256:454a69d788c7fda44efd71e259be79577822f5e3f53f029a22d08004e951dc9f",
|
||||
"sha256:6988bd2b895eef432d562370bb707d540f32f7360ab13da45340101bc2307d84"
|
||||
],
|
||||
"markers": "python_version < '3.7'",
|
||||
"version": "==0.6"
|
||||
},
|
||||
"immutables": {
|
||||
"hashes": [
|
||||
"sha256:1e4f4513254ef11e0230a558ee0dcb4551b914993c330005d15338da595d3750",
|
||||
"sha256:228e38dc7a810ba4ff88909908ac47f840e5dc6c4c0da6b25009c626a9ae771c",
|
||||
"sha256:2ae88fbfe1d04f4e5859c924e97313edf70e72b4f19871bf329b96a67ede9ba0",
|
||||
"sha256:2d32b61c222cba1dd11f0faff67c7fb6204ef1982454e1b5b001d4b79966ef17",
|
||||
"sha256:35af186bfac5b62522fdf2cab11120d7b0547f405aa399b6a1e443cf5f5e318c",
|
||||
"sha256:63023fa0cceedc62e0d1535cd4ca7a1f6df3120a6d8e5c34e89037402a6fd809",
|
||||
"sha256:6bf5857f42a96331fd0929c357dc0b36a72f339f3b6acaf870b149c96b141f69",
|
||||
"sha256:7bb1590024a032c7a57f79faf8c8ff5e91340662550d2980e0177f67e66e9c9c",
|
||||
"sha256:7c090687d7e623d4eca22962635b5e1a1ee2d6f9a9aca2f3fb5a184a1ffef1f2",
|
||||
"sha256:bc36a0a8749881eebd753f696b081bd51145e4d77291d671d2e2f622e5b65d2f",
|
||||
"sha256:d9fc6a236018d99af6453ead945a6bb55f98d14b1801a2c229dd993edc753a00"
|
||||
],
|
||||
"version": "==0.6"
|
||||
},
|
||||
"pydantic": {
|
||||
"hashes": [
|
||||
"sha256:93fa585402e7c8c01623ea8af6ca23363e8b4c6a020b7a2de9e99fa29d642d50",
|
||||
"sha256:eb441dd50779347a450494c437db3ecbb13c1f3854497df879662782af516c5c"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.21.0"
|
||||
},
|
||||
"sqlalchemy": {
|
||||
"hashes": [
|
||||
"sha256:781fb7b9d194ed3fc596b8f0dd4623ff160e3e825dd8c15472376a438c19598b"
|
||||
],
|
||||
"version": "==1.3.1"
|
||||
},
|
||||
"starlette": {
|
||||
"hashes": [
|
||||
"sha256:9d48b35d1fc7521d59ae53c421297ab3878d3c7cd4b75266d77f6c73cccb78bb"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.11.1"
|
||||
}
|
||||
},
|
||||
"develop": {
|
||||
"appdirs": {
|
||||
"hashes": [
|
||||
"sha256:9e5896d1372858f8dd3344faf4e5014d21849c756c8d5701f78f8a103b372d92",
|
||||
"sha256:d8b24664561d0d34ddfaec54636d502d7cea6e29c3eaf68f3df6180863e2166e"
|
||||
],
|
||||
"version": "==1.4.3"
|
||||
},
|
||||
"atomicwrites": {
|
||||
"hashes": [
|
||||
"sha256:03472c30eb2c5d1ba9227e4c2ca66ab8287fbfbbda3888aa93dc2e28fc6811b4",
|
||||
"sha256:75a9445bac02d8d058d5e1fe689654ba5a6556a1dfd8ce6ec55a0ed79866cfa6"
|
||||
],
|
||||
"version": "==1.3.0"
|
||||
},
|
||||
"attrs": {
|
||||
"hashes": [
|
||||
"sha256:69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79",
|
||||
"sha256:f0b870f674851ecbfbbbd364d6b5cbdff9dcedbc7f3f5e18a6891057f21fe399"
|
||||
],
|
||||
"version": "==19.1.0"
|
||||
},
|
||||
"autoflake": {
|
||||
"hashes": [
|
||||
"sha256:c103e63466f11db3617167a2c68ff6a0cda35b940222920631c6eeec6b67e807"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.2"
|
||||
},
|
||||
"backcall": {
|
||||
"hashes": [
|
||||
"sha256:38ecd85be2c1e78f77fd91700c76e14667dc21e2713b63876c0eb901196e01e4",
|
||||
"sha256:bbbf4b1e5cd2bdb08f915895b51081c041bac22394fdfcfdfbe9f14b77c08bf2"
|
||||
],
|
||||
"version": "==0.1.0"
|
||||
},
|
||||
"better-exceptions": {
|
||||
"hashes": [
|
||||
"sha256:bf79c87659bc849989d726bf0e4a2100edefe7eded112d201f54fe08467fdf63",
|
||||
"sha256:c196cad849de615abb9f6eb67ca1b83f33b938818f0e2fe8fa157b22aeb7b992"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.2.2"
|
||||
},
|
||||
"black": {
|
||||
"hashes": [
|
||||
"sha256:09a9dcb7c46ed496a9850b76e4e825d6049ecd38b611f1224857a79bd985a8cf",
|
||||
"sha256:68950ffd4d9169716bcb8719a56c07a2f4485354fec061cdd5910aa07369731c"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==19.3b0"
|
||||
},
|
||||
"bleach": {
|
||||
"hashes": [
|
||||
"sha256:213336e49e102af26d9cde77dd2d0397afabc5a6bf2fed985dc35b5d1e285a16",
|
||||
"sha256:3fdf7f77adcf649c9911387df51254b813185e32b2c6619f690b593a617e19fa"
|
||||
],
|
||||
"version": "==3.1.0"
|
||||
},
|
||||
"certifi": {
|
||||
"hashes": [
|
||||
"sha256:59b7658e26ca9c7339e00f8f4636cdfe59d34fa37b9b04f6f9e9926b3cece1a5",
|
||||
"sha256:b26104d6835d1f5e49452a26eb2ff87fe7090b89dfcaee5ea2212697e1e1d7ae"
|
||||
],
|
||||
"version": "==2019.3.9"
|
||||
},
|
||||
"chardet": {
|
||||
"hashes": [
|
||||
"sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
|
||||
"sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
|
||||
],
|
||||
"version": "==3.0.4"
|
||||
},
|
||||
"click": {
|
||||
"hashes": [
|
||||
"sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13",
|
||||
"sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7"
|
||||
],
|
||||
"version": "==7.0"
|
||||
},
|
||||
"coverage": {
|
||||
"hashes": [
|
||||
"sha256:029c69deaeeeae1b15bc6c59f0ffa28aa8473721c614a23f2c2976dec245cd12",
|
||||
"sha256:02abbbebc6e9d5abe13cd28b5e963dedb6ffb51c146c916d17b18f141acd9947",
|
||||
"sha256:1bbfe5b82a3921d285e999c6d256c1e16b31c554c29da62d326f86c173d30337",
|
||||
"sha256:210c02f923df33a8d0e461c86fdcbbb17228ff4f6d92609fc06370a98d283c2d",
|
||||
"sha256:2d0807ba935f540d20b49d5bf1c0237b90ce81e133402feda906e540003f2f7a",
|
||||
"sha256:35d7a013874a7c927ce997350d314144ffc5465faf787bb4e46e6c4f381ef562",
|
||||
"sha256:3636f9d0dcb01aed4180ef2e57a4e34bb4cac3ecd203c2a23db8526d86ab2fb4",
|
||||
"sha256:42f4be770af2455a75e4640f033a82c62f3fb0d7a074123266e143269d7010ef",
|
||||
"sha256:48440b25ba6cda72d4c638f3a9efa827b5b87b489c96ab5f4ff597d976413156",
|
||||
"sha256:4dac8dfd1acf6a3ac657475dfdc66c621f291b1b7422a939cc33c13ac5356473",
|
||||
"sha256:4e8474771c69c2991d5eab65764289a7dd450bbea050bc0ebb42b678d8222b42",
|
||||
"sha256:551f10ddfeff56a1325e5a34eff304c5892aa981fd810babb98bfee77ee2fb17",
|
||||
"sha256:5b104982f1809c1577912519eb249f17d9d7e66304ad026666cb60a5ef73309c",
|
||||
"sha256:5c62aef73dfc87bfcca32cee149a1a7a602bc74bac72223236b0023543511c88",
|
||||
"sha256:633151f8d1ad9467b9f7e90854a7f46ed8f2919e8bc7d98d737833e8938fc081",
|
||||
"sha256:772207b9e2d5bf3f9d283b88915723e4e92d9a62c83f44ec92b9bd0cd685541b",
|
||||
"sha256:7d5e02f647cd727afc2659ec14d4d1cc0508c47e6cfb07aea33d7aa9ca94d288",
|
||||
"sha256:a9798a4111abb0f94584000ba2a2c74841f2cfe5f9254709756367aabbae0541",
|
||||
"sha256:b38ea741ab9e35bfa7015c93c93bbd6a1623428f97a67083fc8ebd366238b91f",
|
||||
"sha256:b6a5478c904236543c0347db8a05fac6fc0bd574c870e7970faa88e1d9890044",
|
||||
"sha256:c6248bfc1de36a3844685a2e10ba17c18119ba6252547f921062a323fb31bff1",
|
||||
"sha256:c705ab445936457359b1424ef25ccc0098b0491b26064677c39f1d14a539f056",
|
||||
"sha256:d95a363d663ceee647291131dbd213af258df24f41350246842481ec3709bd33",
|
||||
"sha256:e27265eb80cdc5dab55a40ef6f890e04ecc618649ad3da5265f128b141f93f78",
|
||||
"sha256:ebc276c9cb5d917bd2ae959f84ffc279acafa9c9b50b0fa436ebb70bbe2166ea",
|
||||
"sha256:f4d229866d030863d0fe3bf297d6d11e6133ca15bbb41ed2534a8b9a3d6bd061",
|
||||
"sha256:f95675bd88b51474d4fe5165f3266f419ce754ffadfb97f10323931fa9ac95e5",
|
||||
"sha256:f95bc54fb6d61b9f9ff09c4ae8ff6a3f5edc937cda3ca36fc937302a7c152bf1",
|
||||
"sha256:fd0f6be53de40683584e5331c341e65a679dbe5ec489a0697cec7c2ef1a48cda"
|
||||
],
|
||||
"version": "==5.0a4"
|
||||
},
|
||||
"decorator": {
|
||||
"hashes": [
|
||||
"sha256:86156361c50488b84a3f148056ea716ca587df2f0de1d34750d35c21312725de",
|
||||
"sha256:f069f3a01830ca754ba5258fde2278454a0b5b79e0d7f5c13b3b97e57d4acff6"
|
||||
],
|
||||
"version": "==4.4.0"
|
||||
},
|
||||
"defusedxml": {
|
||||
"hashes": [
|
||||
"sha256:24d7f2f94f7f3cb6061acb215685e5125fbcdc40a857eff9de22518820b0a4f4",
|
||||
"sha256:702a91ade2968a82beb0db1e0766a6a273f33d4616a6ce8cde475d8e09853b20"
|
||||
],
|
||||
"version": "==0.5.0"
|
||||
},
|
||||
"dnspython": {
|
||||
"hashes": [
|
||||
"sha256:36c5e8e38d4369a08b6780b7f27d790a292b2b08eea01607865bf0936c558e01",
|
||||
"sha256:f69c21288a962f4da86e56c4905b49d11aba7938d3d740e80d9e366ee4f1632d"
|
||||
],
|
||||
"version": "==1.16.0"
|
||||
},
|
||||
"docutils": {
|
||||
"hashes": [
|
||||
"sha256:02aec4bd92ab067f6ff27a38a38a41173bf01bed8f89157768c1573f53e474a6",
|
||||
"sha256:51e64ef2ebfb29cae1faa133b3710143496eca21c530f3f71424d77687764274",
|
||||
"sha256:7a4bd47eaf6596e1295ecb11361139febe29b084a87bf005bf899f9a42edc3c6"
|
||||
],
|
||||
"version": "==0.14"
|
||||
},
|
||||
"email-validator": {
|
||||
"hashes": [
|
||||
"sha256:ddc4b5b59fa699bb10127adcf7ad4de78fde4ec539a072b104b8bb16da666ae5"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.0.3"
|
||||
},
|
||||
"entrypoints": {
|
||||
"hashes": [
|
||||
"sha256:589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19",
|
||||
"sha256:c70dd71abe5a8c85e55e12c19bd91ccfeec11a6e99044204511f9ed547d48451"
|
||||
],
|
||||
"version": "==0.3"
|
||||
},
|
||||
"flake8": {
|
||||
"hashes": [
|
||||
"sha256:859996073f341f2670741b51ec1e67a01da142831aa1fdc6242dbf88dffbe661",
|
||||
"sha256:a796a115208f5c03b18f332f7c11729812c8c3ded6c46319c59b53efd3819da8"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==3.7.7"
|
||||
},
|
||||
"flit": {
|
||||
"hashes": [
|
||||
"sha256:1d93f7a833ed8a6e120ddc40db5c4763bc39bccc75c05081ec8285ece718aefb",
|
||||
"sha256:6f6f0fb83c51ffa3a150fa41b5ac118df9ea4a87c2c06dff4ebf9adbe7b52b36"
|
||||
],
|
||||
"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",
|
||||
"sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c"
|
||||
],
|
||||
"version": "==2.8"
|
||||
},
|
||||
"ipykernel": {
|
||||
"hashes": [
|
||||
"sha256:0aeb7ec277ac42cc2b59ae3d08b10909b2ec161dc6908096210527162b53675d",
|
||||
"sha256:0fc0bf97920d454102168ec2008620066878848fcfca06c22b669696212e292f"
|
||||
],
|
||||
"version": "==5.1.0"
|
||||
},
|
||||
"ipython": {
|
||||
"hashes": [
|
||||
"sha256:b038baa489c38f6d853a3cfc4c635b0cda66f2864d136fe8f40c1a6e334e2a6b",
|
||||
"sha256:f5102c1cd67e399ec8ea66bcebe6e3968ea25a8977e53f012963e5affeb1fe38"
|
||||
],
|
||||
"markers": "python_version >= '3.3'",
|
||||
"version": "==7.4.0"
|
||||
},
|
||||
"ipython-genutils": {
|
||||
"hashes": [
|
||||
"sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8",
|
||||
"sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8"
|
||||
],
|
||||
"version": "==0.2.0"
|
||||
},
|
||||
"ipywidgets": {
|
||||
"hashes": [
|
||||
"sha256:0f2b5cde9f272cb49d52f3f0889fdd1a7ae1e74f37b48dac35a83152780d2b7b",
|
||||
"sha256:a3e224f430163f767047ab9a042fc55adbcab0c24bbe6cf9f306c4f89fdf0ba3"
|
||||
],
|
||||
"version": "==7.4.2"
|
||||
},
|
||||
"isort": {
|
||||
"hashes": [
|
||||
"sha256:08f8e3f0f0b7249e9fad7e5c41e2113aba44969798a26452ee790c06f155d4ec",
|
||||
"sha256:4e9e9c4bd1acd66cf6c36973f29b031ec752cbfd991c69695e4e259f9a756927"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==4.3.16"
|
||||
},
|
||||
"jedi": {
|
||||
"hashes": [
|
||||
"sha256:2bb0603e3506f708e792c7f4ad8fc2a7a9d9c2d292a358fbbd58da531695595b",
|
||||
"sha256:2c6bcd9545c7d6440951b12b44d373479bf18123a401a52025cf98563fbd826c"
|
||||
],
|
||||
"version": "==0.13.3"
|
||||
},
|
||||
"jinja2": {
|
||||
"hashes": [
|
||||
"sha256:74c935a1b8bb9a3947c50a54766a969d4846290e1e788ea44c1392163723c3bd",
|
||||
"sha256:f84be1bb0040caca4cea721fcbbbbd61f9be9464ca236387158b0feea01914a4"
|
||||
],
|
||||
"version": "==2.10"
|
||||
},
|
||||
"jsonschema": {
|
||||
"hashes": [
|
||||
"sha256:0c0a81564f181de3212efa2d17de1910f8732fa1b71c42266d983cd74304e20d",
|
||||
"sha256:a5f6559964a3851f59040d3b961de5e68e70971afb88ba519d27e6a039efff1a"
|
||||
],
|
||||
"version": "==3.0.1"
|
||||
},
|
||||
"jupyter": {
|
||||
"hashes": [
|
||||
"sha256:3e1f86076bbb7c8c207829390305a2b1fe836d471ed54be66a3b8c41e7f46cc7",
|
||||
"sha256:5b290f93b98ffbc21c0c7e749f054b3267782166d72fa5e3ed1ed4eaf34a2b78",
|
||||
"sha256:d9dc4b3318f310e34c82951ea5d6683f67bed7def4b259fafbfe4f1beb1d8e5f"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.0.0"
|
||||
},
|
||||
"jupyter-client": {
|
||||
"hashes": [
|
||||
"sha256:b5f9cb06105c1d2d30719db5ffb3ea67da60919fb68deaefa583deccd8813551",
|
||||
"sha256:c44411eb1463ed77548bc2d5ec0d744c9b81c4a542d9637c7a52824e2121b987"
|
||||
],
|
||||
"version": "==5.2.4"
|
||||
},
|
||||
"jupyter-console": {
|
||||
"hashes": [
|
||||
"sha256:308ce876354924fb6c540b41d5d6d08acfc946984bf0c97777c1ddcb42e0b2f5",
|
||||
"sha256:cc80a97a5c389cbd30252ffb5ce7cefd4b66bde98219edd16bf5cb6f84bb3568"
|
||||
],
|
||||
"version": "==6.0.0"
|
||||
},
|
||||
"jupyter-core": {
|
||||
"hashes": [
|
||||
"sha256:927d713ffa616ea11972534411544589976b2493fc7e09ad946e010aa7eb9970",
|
||||
"sha256:ba70754aa680300306c699790128f6fbd8c306ee5927976cbe48adacf240c0b7"
|
||||
],
|
||||
"version": "==4.4.0"
|
||||
},
|
||||
"livereload": {
|
||||
"hashes": [
|
||||
"sha256:29cadfabcedd12eed792e0131991235b9d4764d4474bed75cf525f57109ec0a2",
|
||||
"sha256:e632a6cd1d349155c1d7f13a65be873b38f43ef02961804a1bba8d817fa649a7"
|
||||
],
|
||||
"version": "==2.6.0"
|
||||
},
|
||||
"markdown": {
|
||||
"hashes": [
|
||||
"sha256:c00429bd503a47ec88d5e30a751e147dcb4c6889663cd3e2ba0afe858e009baa",
|
||||
"sha256:d02e0f9b04c500cde6637c11ad7c72671f359b87b9fe924b2383649d8841db7c"
|
||||
],
|
||||
"version": "==3.0.1"
|
||||
},
|
||||
"markdown-include": {
|
||||
"hashes": [
|
||||
"sha256:72a45461b589489a088753893bc95c5fa5909936186485f4ed55caa57d10250f"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.5.1"
|
||||
},
|
||||
"markupsafe": {
|
||||
"hashes": [
|
||||
"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.1"
|
||||
},
|
||||
"mccabe": {
|
||||
"hashes": [
|
||||
"sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42",
|
||||
"sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"
|
||||
],
|
||||
"version": "==0.6.1"
|
||||
},
|
||||
"mistune": {
|
||||
"hashes": [
|
||||
"sha256:59a3429db53c50b5c6bcc8a07f8848cb00d7dc8bdb431a4ab41920d201d4756e",
|
||||
"sha256:88a1051873018da288eee8538d476dffe1262495144b33ecb586c4ab266bb8d4"
|
||||
],
|
||||
"version": "==0.8.4"
|
||||
},
|
||||
"mkdocs": {
|
||||
"hashes": [
|
||||
"sha256:17d34329aad75d5de604b9ed4e31df3a4d235afefdc46ce7b1964fddb2e1e939",
|
||||
"sha256:8cc8b38325456b9e942c981a209eaeb1e9f3f77b493ad755bfef889b9c8d356a"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.0.4"
|
||||
},
|
||||
"mkdocs-material": {
|
||||
"hashes": [
|
||||
"sha256:0b394aa034b25a09a5874ae2a6ccc426fd81f5764e0991217b169e31cb0c1c0e",
|
||||
"sha256:f5bb80a2c16d045d380edb2c5b05636af1bb709cb859bfaa9d01063a11df803f"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==4.1.0"
|
||||
},
|
||||
"more-itertools": {
|
||||
"hashes": [
|
||||
"sha256:0125e8f60e9e031347105eb1682cef932f5e97d7b9a1a28d9bf00c22a5daef40",
|
||||
"sha256:590044e3942351a1bdb1de960b739ff4ce277960f2425ad4509446dbace8d9d1"
|
||||
],
|
||||
"markers": "python_version > '2.7'",
|
||||
"version": "==6.0.0"
|
||||
},
|
||||
"mypy": {
|
||||
"hashes": [
|
||||
"sha256:308c274eb8482fbf16006f549137ddc0d69e5a589465e37b99c4564414363ca7",
|
||||
"sha256:e80fd6af34614a0e898a57f14296d0dacb584648f0339c2e000ddbf0f4cc2f8d"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.670"
|
||||
},
|
||||
"mypy-extensions": {
|
||||
"hashes": [
|
||||
"sha256:37e0e956f41369209a3d5f34580150bcacfabaa57b33a15c0b25f4b5725e0812",
|
||||
"sha256:b16cabe759f55e3409a7d231ebd2841378fb0c27a5d1994719e340e4f429ac3e"
|
||||
],
|
||||
"version": "==0.4.1"
|
||||
},
|
||||
"nbconvert": {
|
||||
"hashes": [
|
||||
"sha256:302554a2e219bc0fc84f3edd3e79953f3767b46ab67626fdec16e38ba3f7efe4",
|
||||
"sha256:5de8fb2284422272a1d45abc77c07b888127550a6d602ce619592a2b08a474ff"
|
||||
],
|
||||
"version": "==5.4.1"
|
||||
},
|
||||
"nbformat": {
|
||||
"hashes": [
|
||||
"sha256:b9a0dbdbd45bb034f4f8893cafd6f652ea08c8c1674ba83f2dc55d3955743b0b",
|
||||
"sha256:f7494ef0df60766b7cabe0a3651556345a963b74dbc16bc7c18479041170d402"
|
||||
],
|
||||
"version": "==4.4.0"
|
||||
},
|
||||
"notebook": {
|
||||
"hashes": [
|
||||
"sha256:18a98858c0331fb65a60f2ebb6439f8c0c4defd14ca363731b6cabc7f61624b4",
|
||||
"sha256:cc027a62be0f7756e0ef3d2d98458c4d7f4b3566449fb1a05891207f5bd9a1bf"
|
||||
],
|
||||
"version": "==5.7.6"
|
||||
},
|
||||
"pandocfilters": {
|
||||
"hashes": [
|
||||
"sha256:b3dd70e169bb5449e6bc6ff96aea89c5eea8c5f6ab5e207fc2f521a2cf4a0da9"
|
||||
],
|
||||
"version": "==1.4.2"
|
||||
},
|
||||
"parso": {
|
||||
"hashes": [
|
||||
"sha256:4580328ae3f548b358f4901e38c0578229186835f0fa0846e47369796dd5bcc9",
|
||||
"sha256:68406ebd7eafe17f8e40e15a84b56848eccbf27d7c1feb89e93d8fca395706db"
|
||||
],
|
||||
"version": "==0.3.4"
|
||||
},
|
||||
"pexpect": {
|
||||
"hashes": [
|
||||
"sha256:2a8e88259839571d1251d278476f3eec5db26deb73a70be5ed5dc5435e418aba",
|
||||
"sha256:3fbd41d4caf27fa4a377bfd16fef87271099463e6fa73e92a52f92dfee5d425b"
|
||||
],
|
||||
"markers": "sys_platform != 'win32'",
|
||||
"version": "==4.6.0"
|
||||
},
|
||||
"pickleshare": {
|
||||
"hashes": [
|
||||
"sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca",
|
||||
"sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"
|
||||
],
|
||||
"version": "==0.7.5"
|
||||
},
|
||||
"pluggy": {
|
||||
"hashes": [
|
||||
"sha256:19ecf9ce9db2fce065a7a0586e07cfb4ac8614fe96edf628a264b1c70116cf8f",
|
||||
"sha256:84d306a647cc805219916e62aab89caa97a33a1dd8c342e87a37f91073cd4746"
|
||||
],
|
||||
"version": "==0.9.0"
|
||||
},
|
||||
"prometheus-client": {
|
||||
"hashes": [
|
||||
"sha256:1b38b958750f66f208bcd9ab92a633c0c994d8859c831f7abc1f46724fcee490"
|
||||
],
|
||||
"version": "==0.6.0"
|
||||
},
|
||||
"prompt-toolkit": {
|
||||
"hashes": [
|
||||
"sha256:11adf3389a996a6d45cc277580d0d53e8a5afd281d0c9ec71b28e6f121463780",
|
||||
"sha256:2519ad1d8038fd5fc8e770362237ad0364d16a7650fb5724af6997ed5515e3c1",
|
||||
"sha256:977c6583ae813a37dc1c2e1b715892461fcbdaa57f6fc62f33a528c4886c8f55"
|
||||
],
|
||||
"version": "==2.0.9"
|
||||
},
|
||||
"ptyprocess": {
|
||||
"hashes": [
|
||||
"sha256:923f299cc5ad920c68f2bc0bc98b75b9f838b93b599941a6b63ddbc2476394c0",
|
||||
"sha256:d7cc528d76e76342423ca640335bd3633420dc1366f258cb31d05e865ef5ca1f"
|
||||
],
|
||||
"markers": "os_name != 'nt'",
|
||||
"version": "==0.6.0"
|
||||
},
|
||||
"py": {
|
||||
"hashes": [
|
||||
"sha256:64f65755aee5b381cea27766a3a147c3f15b9b6b9ac88676de66ba2ae36793fa",
|
||||
"sha256:dc639b046a6e2cff5bbe40194ad65936d6ba360b52b3c3fe1d08a82dd50b5e53"
|
||||
],
|
||||
"version": "==1.8.0"
|
||||
},
|
||||
"pycodestyle": {
|
||||
"hashes": [
|
||||
"sha256:95a2219d12372f05704562a14ec30bc76b05a5b297b21a5dfe3f6fac3491ae56",
|
||||
"sha256:e40a936c9a450ad81df37f549d676d127b1b66000a6c500caa2b085bc0ca976c"
|
||||
],
|
||||
"version": "==2.5.0"
|
||||
},
|
||||
"pyflakes": {
|
||||
"hashes": [
|
||||
"sha256:17dbeb2e3f4d772725c777fabc446d5634d1038f234e77343108ce445ea69ce0",
|
||||
"sha256:d976835886f8c5b31d47970ed689944a0262b5f3afa00a5a7b4dc81e5449f8a2"
|
||||
],
|
||||
"version": "==2.1.1"
|
||||
},
|
||||
"pygments": {
|
||||
"hashes": [
|
||||
"sha256:5ffada19f6203563680669ee7f53b64dabbeb100eb51b61996085e99c03b284a",
|
||||
"sha256:e8218dd399a61674745138520d0d4cf2621d7e032439341bc3f647bff125818d"
|
||||
],
|
||||
"version": "==2.3.1"
|
||||
},
|
||||
"pymdown-extensions": {
|
||||
"hashes": [
|
||||
"sha256:25b0a7967fa697b5035e23340a48594e3e93acb10b06d74574218ace3347d1df",
|
||||
"sha256:6cf0cf36b5a03b291ace22dc2f320f4789ce56fbdb6635a3be5fadbf5d7694dd"
|
||||
],
|
||||
"version": "==6.0"
|
||||
},
|
||||
"pyrsistent": {
|
||||
"hashes": [
|
||||
"sha256:3ca82748918eb65e2d89f222b702277099aca77e34843c5eb9d52451173970e2"
|
||||
],
|
||||
"version": "==0.14.11"
|
||||
},
|
||||
"pytest": {
|
||||
"hashes": [
|
||||
"sha256:592eaa2c33fae68c7d75aacf042efc9f77b27c08a6224a4f59beab8d9a420523",
|
||||
"sha256:ad3ad5c450284819ecde191a654c09b0ec72257a2c711b9633d677c71c9850c4"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==4.3.1"
|
||||
},
|
||||
"pytest-cov": {
|
||||
"hashes": [
|
||||
"sha256:0ab664b25c6aa9716cbf203b17ddb301932383046082c081b9848a0edf5add33",
|
||||
"sha256:230ef817450ab0699c6cc3c9c8f7a829c34674456f2ed8df1fe1d39780f7c87f"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.6.1"
|
||||
},
|
||||
"python-dateutil": {
|
||||
"hashes": [
|
||||
"sha256:7e6584c74aeed623791615e26efd690f29817a27c73085b78e4bad02493df2fb",
|
||||
"sha256:c89805f6f4d64db21ed966fda138f8a5ed7a4fdbc1a8ee329ce1b74e3c74da9e"
|
||||
],
|
||||
"version": "==2.8.0"
|
||||
},
|
||||
"python-multipart": {
|
||||
"hashes": [
|
||||
"sha256:f7bb5f611fc600d15fa47b3974c8aa16e93724513b49b5f95c81e6624c83fa43"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.0.5"
|
||||
},
|
||||
"pytoml": {
|
||||
"hashes": [
|
||||
"sha256:ca2d0cb127c938b8b76a9a0d0f855cf930c1d50cc3a0af6d3595b566519a1013"
|
||||
],
|
||||
"version": "==0.1.20"
|
||||
},
|
||||
"pyyaml": {
|
||||
"hashes": [
|
||||
"sha256:1adecc22f88d38052fb787d959f003811ca858b799590a5eaa70e63dca50308c",
|
||||
"sha256:436bc774ecf7c103814098159fbb84c2715d25980175292c648f2da143909f95",
|
||||
"sha256:460a5a4248763f6f37ea225d19d5c205677d8d525f6a83357ca622ed541830c2",
|
||||
"sha256:5a22a9c84653debfbf198d02fe592c176ea548cccce47553f35f466e15cf2fd4",
|
||||
"sha256:7a5d3f26b89d688db27822343dfa25c599627bc92093e788956372285c6298ad",
|
||||
"sha256:9372b04a02080752d9e6f990179a4ab840227c6e2ce15b95e1278456664cf2ba",
|
||||
"sha256:a5dcbebee834eaddf3fa7366316b880ff4062e4bcc9787b78c7fbb4a26ff2dd1",
|
||||
"sha256:aee5bab92a176e7cd034e57f46e9df9a9862a71f8f37cad167c6fc74c65f5b4e",
|
||||
"sha256:c51f642898c0bacd335fc119da60baae0824f2cde95b0330b56c0553439f0673",
|
||||
"sha256:c68ea4d3ba1705da1e0d85da6684ac657912679a649e8868bd850d2c299cce13",
|
||||
"sha256:e23d0cc5299223dcc37885dae624f382297717e459ea24053709675a976a3e19"
|
||||
],
|
||||
"version": "==5.1"
|
||||
},
|
||||
"pyzmq": {
|
||||
"hashes": [
|
||||
"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.1"
|
||||
},
|
||||
"qtconsole": {
|
||||
"hashes": [
|
||||
"sha256:1ac4a65e81a27b0838330a6d351c2f8435d4013d98a95373e8a41119b2968390",
|
||||
"sha256:bc1ba15f50c29ed50f1268ad823bb6543be263c18dd093b80495e9df63b003ac"
|
||||
],
|
||||
"version": "==4.4.3"
|
||||
},
|
||||
"requests": {
|
||||
"hashes": [
|
||||
"sha256:502a824f31acdacb3a35b6690b5fbf0bc41d63a24a45c4004352b0242707598e",
|
||||
"sha256:7bf2a778576d825600030a110f3c0e3e8edc51dfaafe1c146e39a2027784957b"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.21.0"
|
||||
},
|
||||
"send2trash": {
|
||||
"hashes": [
|
||||
"sha256:60001cc07d707fe247c94f74ca6ac0d3255aabcb930529690897ca2a39db28b2",
|
||||
"sha256:f1691922577b6fa12821234aeb57599d887c4900b9ca537948d2dac34aea888b"
|
||||
],
|
||||
"version": "==1.5.0"
|
||||
},
|
||||
"six": {
|
||||
"hashes": [
|
||||
"sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c",
|
||||
"sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"
|
||||
],
|
||||
"version": "==1.12.0"
|
||||
},
|
||||
"sqlalchemy": {
|
||||
"hashes": [
|
||||
"sha256:781fb7b9d194ed3fc596b8f0dd4623ff160e3e825dd8c15472376a438c19598b"
|
||||
],
|
||||
"version": "==1.3.1"
|
||||
},
|
||||
"terminado": {
|
||||
"hashes": [
|
||||
"sha256:55abf9ade563b8f9be1f34e4233c7b7bde726059947a593322e8a553cc4c067a",
|
||||
"sha256:65011551baff97f5414c67018e908110693143cfbaeb16831b743fe7cad8b927"
|
||||
],
|
||||
"version": "==0.8.1"
|
||||
},
|
||||
"testpath": {
|
||||
"hashes": [
|
||||
"sha256:46c89ebb683f473ffe2aab0ed9f12581d4d078308a3cb3765d79c6b2317b0109",
|
||||
"sha256:b694b3d9288dbd81685c5d2e7140b81365d46c29f5db4bc659de5aa6b98780f8"
|
||||
],
|
||||
"version": "==0.4.2"
|
||||
},
|
||||
"toml": {
|
||||
"hashes": [
|
||||
"sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c",
|
||||
"sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e"
|
||||
],
|
||||
"version": "==0.10.0"
|
||||
},
|
||||
"tornado": {
|
||||
"hashes": [
|
||||
"sha256:1174dcb84d08887b55defb2cda1986faeeea715fff189ef3dc44cce99f5fca6b",
|
||||
"sha256:2613fab506bd2aedb3722c8c64c17f8f74f4070afed6eea17f20b2115e445aec",
|
||||
"sha256:44b82bc1146a24e5b9853d04c142576b4e8fa7a92f2e30bc364a85d1f75c4de2",
|
||||
"sha256:457fcbee4df737d2defc181b9073758d73f54a6cfc1f280533ff48831b39f4a8",
|
||||
"sha256:49603e1a6e24104961497ad0c07c799aec1caac7400a6762b687e74c8206677d",
|
||||
"sha256:8c2f40b99a8153893793559919a355d7b74649a11e59f411b0b0a1793e160bc0",
|
||||
"sha256:e1d897889c3b5a829426b7d52828fb37b28bc181cd598624e65c8be40ee3f7fa"
|
||||
],
|
||||
"version": "==6.0.2"
|
||||
},
|
||||
"traitlets": {
|
||||
"hashes": [
|
||||
"sha256:9c4bd2d267b7153df9152698efb1050a5d84982d3384a37b2c1f7723ba3e7835",
|
||||
"sha256:c6cb5e6f57c5a9bdaa40fa71ce7b4af30298fbab9ece9815b5d995ab6217c7d9"
|
||||
],
|
||||
"version": "==4.3.2"
|
||||
},
|
||||
"typed-ast": {
|
||||
"hashes": [
|
||||
"sha256:035a54ede6ce1380599b2ce57844c6554666522e376bd111eb940fbc7c3dad23",
|
||||
"sha256:037c35f2741ce3a9ac0d55abfcd119133cbd821fffa4461397718287092d9d15",
|
||||
"sha256:049feae7e9f180b64efacbdc36b3af64a00393a47be22fa9cb6794e68d4e73d3",
|
||||
"sha256:19228f7940beafc1ba21a6e8e070e0b0bfd1457902a3a81709762b8b9039b88d",
|
||||
"sha256:2ea681e91e3550a30c2265d2916f40a5f5d89b59469a20f3bad7d07adee0f7a6",
|
||||
"sha256:3a6b0a78af298d82323660df5497bcea0f0a4a25a0b003afd0ce5af049bd1f60",
|
||||
"sha256:5385da8f3b801014504df0852bf83524599df890387a3c2b17b7caa3d78b1773",
|
||||
"sha256:606d8afa07eef77280c2bf84335e24390055b478392e1975f96286d99d0cb424",
|
||||
"sha256:69245b5b23bbf7fb242c9f8f08493e9ecd7711f063259aefffaeb90595d62287",
|
||||
"sha256:6f6d839ab09830d59b7fa8fb6917023d8cb5498ee1f1dbd82d37db78eb76bc99",
|
||||
"sha256:730888475f5ac0e37c1de4bd05eeb799fdb742697867f524dc8a4cd74bcecc23",
|
||||
"sha256:9819b5162ffc121b9e334923c685b0d0826154e41dfe70b2ede2ce29034c71d8",
|
||||
"sha256:9e60ef9426efab601dd9aa120e4ff560f4461cf8442e9c0a2b92548d52800699",
|
||||
"sha256:af5fbdde0690c7da68e841d7fc2632345d570768ea7406a9434446d7b33b0ee1",
|
||||
"sha256:b64efdbdf3bbb1377562c179f167f3bf301251411eb5ac77dec6b7d32bcda463",
|
||||
"sha256:bac5f444c118aeb456fac1b0b5d14c6a71ea2a42069b09c176f75e9bd4c186f6",
|
||||
"sha256:bda9068aafb73859491e13b99b682bd299c1b5fd50644d697533775828a28ee0",
|
||||
"sha256:d659517ca116e6750101a1326107d3479028c5191f0ecee3c7203c50f5b915b0",
|
||||
"sha256:eddd3fb1f3e0f82e5915a899285a39ee34ce18fd25d89582bc89fc9fb16cd2c6"
|
||||
],
|
||||
"version": "==1.3.1"
|
||||
},
|
||||
"ujson": {
|
||||
"hashes": [
|
||||
"sha256:f66073e5506e91d204ab0c614a148d5aa938bdbf104751be66f8ad7a222f5f86"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.35"
|
||||
},
|
||||
"urllib3": {
|
||||
"hashes": [
|
||||
"sha256:61bf29cada3fc2fbefad4fdf059ea4bd1b4a86d2b6d15e1c7c0b582b9752fe39",
|
||||
"sha256:de9529817c93f27c8ccbfead6985011db27bd0ddfcdb2d86f3f663385c6a9c22"
|
||||
],
|
||||
"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",
|
||||
"sha256:f4ebe71925af7b40a864553f761ed559b43544f8f71746c2d756c7fe788ade7c"
|
||||
],
|
||||
"version": "==0.1.7"
|
||||
},
|
||||
"webencodings": {
|
||||
"hashes": [
|
||||
"sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78",
|
||||
"sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"
|
||||
],
|
||||
"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",
|
||||
"sha256:fa618be8435447a017fd1bf2c7ae922d0428056cfc7449f7a8641edf76b48265"
|
||||
],
|
||||
"version": "==3.4.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
26
README.md
@@ -43,6 +43,28 @@ The key features are:
|
||||
|
||||
<small>* estimation based on tests on an internal development team, building production applications.</small>
|
||||
|
||||
## Opinions
|
||||
|
||||
"*[...] I'm using **FastAPI** a ton these days. [...] I'm actually planning to use it for all of my team's **ML services at Microsoft**. Some of them are getting integrated into the core **Windows** product and some **Office** products.*"
|
||||
|
||||
<div style="text-align: right; margin-right: 10%;">Kabir Khan - <strong>Microsoft</strong> <a href="https://github.com/tiangolo/fastapi/pull/26" target="_blank"><small>(ref)</small></a></div>
|
||||
|
||||
---
|
||||
|
||||
"*I’m over the moon excited about **FastAPI**. It’s so fun!*"
|
||||
|
||||
<div style="text-align: right; margin-right: 10%;">Brian Okken - <strong><a href="https://pythonbytes.fm/episodes/show/123/time-to-right-the-py-wrongs?time_in_sec=855" target="_blank">Python Bytes</a> podcast host</strong> <a href="https://twitter.com/brianokken/status/1112220079972728832" target="_blank"><small>(ref)</small></a></div>
|
||||
|
||||
---
|
||||
|
||||
"*Honestly, what you've built looks super solid and polished. In many ways, it's what I wanted **Hug** to be - it's really inspiring to see someone build that.*"
|
||||
|
||||
<div style="text-align: right; margin-right: 10%;">Timothy Crosley - <strong><a href="http://www.hug.rest/" target="_blank">Hug</a> creator</strong> <a href="https://news.ycombinator.com/item?id=19455465" target="_blank"><small>(ref)</small></a></div>
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
## Requirements
|
||||
|
||||
@@ -60,7 +82,7 @@ FastAPI stands on the shoulders of giants:
|
||||
$ pip install fastapi
|
||||
```
|
||||
|
||||
You will also need an ASGI server, for production such as <a href="http://www.uvicorn.org" target="_blank">uvicorn</a>.
|
||||
You will also need an ASGI server, for production such as <a href="http://www.uvicorn.org" target="_blank">Uvicorn</a> or <a href="https://gitlab.com/pgjones/hypercorn" target="_blank">Hypercorn</a>.
|
||||
|
||||
```bash
|
||||
$ pip install uvicorn
|
||||
@@ -198,7 +220,7 @@ def read_item(item_id: int, q: str = None):
|
||||
|
||||
|
||||
@app.put("/items/{item_id}")
|
||||
def create_item(item_id: int, item: Item):
|
||||
def update_item(item_id: int, item: Item):
|
||||
return {"item_name": item.name, "item_id": item_id}
|
||||
```
|
||||
|
||||
|
||||
@@ -236,6 +236,23 @@ It was one of the first extremely fast Python frameworks based on `asyncio`. It
|
||||
|
||||
That's why **FastAPI** is based on Starlette, as it is the fastest framework available (tested by third-party benchmarks).
|
||||
|
||||
### <a href="https://falconframework.org/" target="_blank">Falcon</a>
|
||||
|
||||
Falcon is another high performance Python framework, it is designed to be minimal, and work as the foundation of other frameworks like Hug.
|
||||
|
||||
It uses the previous standard for Python web frameworks (WSGI) which is synchronous, so it can't handle WebSockets and other use cases. Nevertheless, it also has a very good performance.
|
||||
|
||||
It is designed to have functions that receive two parameters, one "request" and one "response". Then you "read" parts from the request, and "write" parts to the response. Because of this design, it is not possible to declare request parameters and bodies with standard Python type hints as function parameters.
|
||||
|
||||
So, data validation, serialization, and documentation, have to be done in code, not automatically. Or they have to be implemented as a framework on top of Falcon, like Hug. This same distinction happens in other frameworks that are inspired by Falcon's design, of having one request object and one response object as parameters.
|
||||
|
||||
!!! check "Inspired **FastAPI** to"
|
||||
Find ways to get great performance.
|
||||
|
||||
Along with Hug (as Hug is based on Falcon) inspired **FastAPI** to declare a `response` parameter in functions.
|
||||
|
||||
Although in FastAPI it's optional, and is used mainly to set headers, cookies, and alternative status codes.
|
||||
|
||||
### <a href="https://moltenframework.com/" target="_blank">Molten</a>
|
||||
|
||||
I discovered Molten in the first stages of building **FastAPI**. And it has quite similar ideas:
|
||||
@@ -257,11 +274,35 @@ Routes are declared in a single place, using functions declared in other places
|
||||
|
||||
This actually inspired updating parts of Pydantic, to support the same validation declaration style (all this functionality is now already available in Pydantic).
|
||||
|
||||
### <a href="http://www.hug.rest/" target="_blank">Hug</a>
|
||||
|
||||
Hug was one of the first frameworks to implement the declaration of API parameter types using Python type hints. This was a great idea that inspired other tools to do the same.
|
||||
|
||||
It used custom types in its declarations instead of standard Python types, but it was still a huge step forward.
|
||||
|
||||
It also was one of the first frameworks to generate a custom schema declaring the whole API in JSON.
|
||||
|
||||
It was not based on a standard like OpenAPI and JSON Schema. So it wouldn't be straightforward to integrate it with other tools, like Swagger UI. But again, it was a very innovative idea.
|
||||
|
||||
It has an interesting, uncommon feature: using the same framework, it's possible to create APIs and also CLIs.
|
||||
|
||||
As it is based on the previous standard for synchronous Python web frameworks (WSGI), it can't handle Websockets and other things, although it still has high performance too.
|
||||
|
||||
!!! info
|
||||
Hug was created by Timothy Crosley, the same creator of <a href="https://github.com/timothycrosley/isort" target="_blank">`isort`</a>, a great tool to automatically sort imports in Python files.
|
||||
|
||||
!!! check "Ideas inspired in **FastAPI**"
|
||||
Hug inspired parts of APIStar, and was one of the tools I found most promising, alongside APIStar.
|
||||
|
||||
Hug helped inspiring **FastAPI** to use Python type hints to declare parameters, and to generate a schema defining the API automatically.
|
||||
|
||||
Hug inspired **FastAPI** to declare a `response` parameter in functions to set headers and cookies.
|
||||
|
||||
### <a href="https://github.com/encode/apistar" target="_blank">APIStar</a> (<= 0.5)
|
||||
|
||||
Right before deciding to build **FastAPI** I found **APIStar** server. It had almost everything I was looking for and had a great design.
|
||||
|
||||
It was actually the first implementation of a framework using Python type hints to declare parameters and requests that I ever saw (before NestJS and Molten).
|
||||
It was one of the first implementations of a framework using Python type hints to declare parameters and requests that I ever saw (before NestJS and Molten). I found it more or less at the same time as Hug. But APIStar used the OpenAPI standard.
|
||||
|
||||
It had automatic data validation, data serialization and OpenAPI schema generation based on the same type hints in several places.
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Independent TechEmpower benchmarks show **FastAPI** applications running under Uvicorn as <a href="https://www.techempower.com/benchmarks/#section=test&runid=a979de55-980d-4721-a46f-77298b3f3923&hw=ph&test=fortune&l=zijzen-7" target="_blank">one of the fastest Python frameworks available</a>, only below Starlette and Uvicorn themselves (used internally by FastAPI). (*)
|
||||
Independent TechEmpower benchmarks show **FastAPI** applications running under Uvicorn as <a href="https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7" target="_blank">one of the fastest Python frameworks available</a>, only below Starlette and Uvicorn themselves (used internally by FastAPI). (*)
|
||||
|
||||
But when checking benchmarks and comparisons you should have the following in mind.
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
It is recommended to use <a href="https://www.docker.com/" target="_blank">**Docker**</a> for security, replicability, development simplicity, etc.
|
||||
You can use <a href="https://www.docker.com/" target="_blank">**Docker**</a> for deployment. It has several advantages like security, replicability, development simplicity, etc.
|
||||
|
||||
In this section you'll see instructions and links to guides to know how to:
|
||||
|
||||
@@ -237,7 +237,21 @@ The generated project has instructions to deploy it, doing it takes other 2 min.
|
||||
|
||||
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).
|
||||
You just need to install an ASGI compatible server like:
|
||||
|
||||
* <a href="https://www.uvicorn.org/" target="_blank">Uvicorn</a>, a lightning-fast ASGI server, built on uvloop and httptools.
|
||||
|
||||
```bash
|
||||
pip install uvicorn
|
||||
```
|
||||
|
||||
* <a href="https://gitlab.com/pgjones/hypercorn" target="_blank">Hypercorn</a>, an ASGI server also compatible with HTTP/2.
|
||||
|
||||
```bash
|
||||
pip install hypercorn
|
||||
```
|
||||
|
||||
...or any other ASGI server.
|
||||
|
||||
And run your application the same way you have done in the tutorials, but without the `--reload` option, e.g.:
|
||||
|
||||
@@ -245,9 +259,15 @@ And run your application the same way you have done in the tutorials, but withou
|
||||
uvicorn main:app --host 0.0.0.0 --port 80
|
||||
```
|
||||
|
||||
or with Hypercorn:
|
||||
|
||||
```bash
|
||||
hypercorn main:app --bind 0.0.0.0:80
|
||||
```
|
||||
|
||||
You might want to set up some tooling to make sure it is restarted automatically if it stops.
|
||||
|
||||
You might also want to install <a href="https://gunicorn.org/" target="_blank">Gunicorn</a> and <a href="https://www.uvicorn.org/#running-with-gunicorn" target="_blank">use it as a manager for Uvicorn</a>.
|
||||
You might also want to install <a href="https://gunicorn.org/" target="_blank">Gunicorn</a> and <a href="https://www.uvicorn.org/#running-with-gunicorn" target="_blank">use it as a manager for Uvicorn</a>, or use Hypercorn with multiple workers.
|
||||
|
||||
Making sure to fine-tune the number of workers, etc.
|
||||
|
||||
|
||||
56
docs/external-links.md
Normal file
@@ -0,0 +1,56 @@
|
||||
**FastAPI** has a great community constantly growing.
|
||||
|
||||
There are many posts, articles, tools, and projects, related to **FastAPI**.
|
||||
|
||||
Here's an incomplete list of some of them.
|
||||
|
||||
!!! tip
|
||||
If you have an article, project, tool, or anything related to **FastAPI** that is not yet listed here, create a <a href="https://github.com/tiangolo/fastapi/edit/master/docs/external-links.md" target="_blank">Pull Request adding it</a>.
|
||||
|
||||
## Articles
|
||||
|
||||
### English
|
||||
|
||||
* <a href="https://medium.com/@williamhayes/fastapi-starlette-debug-vs-prod-5f7561db3a59" target="_blank">FastAPI/Starlette debug vs prod</a> by <a href="https://medium.com/@williamhayes" target="_blank">William Hayes</a>.
|
||||
|
||||
* <a href="https://medium.com/data-rebels/fastapi-google-as-an-external-authentication-provider-3a527672cf33" target="_blank">FastAPI — Google as an external authentication provider</a> by <a href="https://medium.com/@nils_29588" target="_blank">Nils de Bruin</a>.
|
||||
|
||||
* <a href="https://medium.com/data-rebels/fastapi-how-to-add-basic-and-cookie-authentication-a45c85ef47d3" target="_blank">FastAPI — How to add basic and cookie authentication</a> by <a href="https://medium.com/@nils_29588" target="_blank">Nils de Bruin</a>.
|
||||
|
||||
* <a href="https://dev.to/errietta/introduction-to-the-fastapi-python-framework-2n10" target="_blank">Introduction to the fastapi python framework</a> by <a href="https://dev.to/errietta" target="_blank">Errieta Kostala</a>.
|
||||
|
||||
* <a href="http://nickc1.github.io/api,/scikit-learn/2019/01/10/scikit-fastapi.html" target="_blank">FastAPI and Scikit-Learn: Easily Deploy Models</a> by <a href="http://nickc1.github.io/" target="_blank">Nick Cortale</a>.
|
||||
|
||||
* <a href="https://medium.com/data-rebels/fastapi-authentication-revisited-enabling-api-key-authentication-122dc5975680" target="_blank">FastAPI authentication revisited: Enabling API key authentication</a> by <a href="https://medium.com/@nils_29588" target="_blank">Nils de Bruin</a>.
|
||||
|
||||
* <a href="https://blog.bartab.fr/fastapi-logging-on-the-fly/" target="_blank">FastAPI, a simple use case on logging</a> by <a href="https://blog.bartab.fr/" target="_blank">@euri10</a>.
|
||||
|
||||
### Japanese
|
||||
|
||||
* <a href="https://qiita.com/mtitg/items/47770e9a562dd150631d" target="_blank">FastAPI|DB接続してCRUDするPython製APIサーバーを構築</a> by <a href="https://qiita.com/mtitg" target="_blank">@mtitg</a>.
|
||||
|
||||
* <a href="https://qiita.com/ryoryomaru/items/59958ed385b3571d50de" target="_blank">python製の最新APIフレームワーク FastAPI を触ってみた</a> by <a href="https://qiita.com/ryoryomaru" target="_blank">@ryoryomaru</a>.
|
||||
|
||||
* <a href="https://qiita.com/angel_katayoku/items/0e1f5dbbe62efc612a78" target="_blank">FastAPIでCORSを回避</a> by <a href="https://qiita.com/angel_katayoku" target="_blank">@angel_katayoku</a>.
|
||||
|
||||
* <a href="https://qiita.com/angel_katayoku/items/4fbc1a4e2b33fa2237d2" target="_blank">FastAPIをMySQLと接続してDockerで管理してみる</a> by <a href="https://qiita.com/angel_katayoku" target="_blank">@angel_katayoku</a>.
|
||||
|
||||
* <a href="https://qiita.com/angel_katayoku/items/8a458a8952f50b73f420" target="_blank">FastAPIでPOSTされたJSONのレスポンスbodyを受け取る</a> by <a href="https://qiita.com/angel_katayoku" target="_blank">@angel_katayoku</a>.
|
||||
|
||||
* <a href="https://qiita.com/hikarut/items/b178af2e2440c67c6ac4" target="_blank">フロントエンド開発者向けのDockerによるPython開発環境構築</a> by <a href="https://qiita.com/hikarut" target="_blank">Hikaru Takahashi</a>.
|
||||
|
||||
### Chinese
|
||||
|
||||
* <a href="https://cloud.tencent.com/developer/article/1431448" target="_blank">使用FastAPI框架快速构建高性能的api服务</a> by <a href="https://cloud.tencent.com/developer/user/5471722" target="_blank">逍遥散人</a>.
|
||||
|
||||
### Vietnamese
|
||||
|
||||
* <a href="https://fullstackstation.com/fastapi-trien-khai-bang-docker/" target="_blank">FASTAPI: TRIỂN KHAI BẰNG DOCKER</a> by <a href="https://fullstackstation.com/author/figonking/" target="_blank">Nguyễn Nhân</a>.
|
||||
|
||||
### Russian
|
||||
|
||||
* <a href="https://habr.com/ru/post/454440/" target="_blank">Мелкая питонячая радость #2: Starlette - Солидная примочка – FastAPI</a> by <a href="https://habr.com/ru/users/57uff3r/" target="_blank">Andrey Korchak</a>.
|
||||
|
||||
## Podcasts
|
||||
|
||||
* <a href="https://pythonbytes.fm/episodes/show/123/time-to-right-the-py-wrongs?time_in_sec=855" target="_blank">FastAPI on PythonBytes</a> by <a href="https://pythonbytes.fm/" target="_blank">Python Bytes FM</a>.
|
||||
@@ -37,7 +37,7 @@ from datetime import date
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
# Declare a variable as an str
|
||||
# Declare a variable as a str
|
||||
# and get editor support inside the function
|
||||
def main(user_id: str):
|
||||
return user_id
|
||||
|
||||
@@ -70,10 +70,8 @@ You can let me know:
|
||||
|
||||
## Vote for FastAPI
|
||||
|
||||
You can vote to include FastAPI in several "awesome lists":
|
||||
|
||||
* <a href="https://github.com/vinta/awesome-python/pull/1209" target="_blank">Vote to include **FastAPI** in `awesome-python`</a>.
|
||||
* <a href="https://github.com/timofurrer/awesome-asyncio/pull/43" target="_blank">Vote to include **FastAPI** in `awesome-asyncio`</a>.
|
||||
* <a href="https://www.slant.co/options/34241/~fastapi-review" target="_blank">Vote for **FastAPI** in Slant</a>.
|
||||
|
||||
## Help others with issues in GitHub
|
||||
|
||||
|
||||
BIN
docs/img/tutorial/additional-responses/image01.png
Normal file
|
After Width: | Height: | Size: 80 KiB |
|
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 76 KiB |
|
Before Width: | Height: | Size: 87 KiB After Width: | Height: | Size: 85 KiB |
BIN
docs/img/tutorial/path-params/image03.png
Normal file
|
After Width: | Height: | Size: 82 KiB |
|
Before Width: | Height: | Size: 85 KiB After Width: | Height: | Size: 90 KiB |
BIN
docs/img/tutorial/security/image11.png
Normal file
|
After Width: | Height: | Size: 79 KiB |
BIN
docs/img/tutorial/security/image12.png
Normal file
|
After Width: | Height: | Size: 68 KiB |
@@ -43,6 +43,28 @@ The key features are:
|
||||
|
||||
<small>* estimation based on tests on an internal development team, building production applications.</small>
|
||||
|
||||
## Opinions
|
||||
|
||||
"*[...] I'm using **FastAPI** a ton these days. [...] I'm actually planning to use it for all of my team's **ML services at Microsoft**. Some of them are getting integrated into the core **Windows** product and some **Office** products.*"
|
||||
|
||||
<div style="text-align: right; margin-right: 10%;">Kabir Khan - <strong>Microsoft</strong> <a href="https://github.com/tiangolo/fastapi/pull/26" target="_blank"><small>(ref)</small></a></div>
|
||||
|
||||
---
|
||||
|
||||
"*I’m over the moon excited about **FastAPI**. It’s so fun!*"
|
||||
|
||||
<div style="text-align: right; margin-right: 10%;">Brian Okken - <strong><a href="https://pythonbytes.fm/episodes/show/123/time-to-right-the-py-wrongs?time_in_sec=855" target="_blank">Python Bytes</a> podcast host</strong> <a href="https://twitter.com/brianokken/status/1112220079972728832" target="_blank"><small>(ref)</small></a></div>
|
||||
|
||||
---
|
||||
|
||||
"*Honestly, what you've built looks super solid and polished. In many ways, it's what I wanted **Hug** to be - it's really inspiring to see someone build that.*"
|
||||
|
||||
<div style="text-align: right; margin-right: 10%;">Timothy Crosley - <strong><a href="http://www.hug.rest/" target="_blank">Hug</a> creator</strong> <a href="https://news.ycombinator.com/item?id=19455465" target="_blank"><small>(ref)</small></a></div>
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
## Requirements
|
||||
|
||||
@@ -60,7 +82,7 @@ FastAPI stands on the shoulders of giants:
|
||||
$ pip install fastapi
|
||||
```
|
||||
|
||||
You will also need an ASGI server, for production such as <a href="http://www.uvicorn.org" target="_blank">uvicorn</a>.
|
||||
You will also need an ASGI server, for production such as <a href="http://www.uvicorn.org" target="_blank">Uvicorn</a> or <a href="https://gitlab.com/pgjones/hypercorn" target="_blank">Hypercorn</a>.
|
||||
|
||||
```bash
|
||||
$ pip install uvicorn
|
||||
@@ -198,7 +220,7 @@ def read_item(item_id: int, q: str = None):
|
||||
|
||||
|
||||
@app.put("/items/{item_id}")
|
||||
def create_item(item_id: int, item: Item):
|
||||
def update_item(item_id: int, item: Item):
|
||||
return {"item_name": item.name, "item_id": item_id}
|
||||
```
|
||||
|
||||
|
||||
@@ -1,164 +1,509 @@
|
||||
## Next release
|
||||
## Latest changes
|
||||
|
||||
## 0.34.0
|
||||
|
||||
* Upgrade Starlette supported range to include the latest `0.12.7`. The new range is `0.11.1,<=0.12.7`. PR [#367](https://github.com/tiangolo/fastapi/pull/367) by [@dedsm](https://github.com/dedsm).
|
||||
|
||||
* Add test for OpenAPI schema with duplicate models from PR [#333](https://github.com/tiangolo/fastapi/pull/333) by [@dmontagu](https://github.com/dmontagu). PR [#385](https://github.com/tiangolo/fastapi/pull/385).
|
||||
|
||||
## 0.33.0
|
||||
|
||||
* Upgrade Pydantic version to `0.30.0`. PR [#384](https://github.com/tiangolo/fastapi/pull/384) by [@jekirl](https://github.com/jekirl).
|
||||
|
||||
## 0.32.0
|
||||
|
||||
* Fix typo in docs for features. PR [#380](https://github.com/tiangolo/fastapi/pull/380) by [@MartinoMensio](https://github.com/MartinoMensio).
|
||||
|
||||
* Fix source code `limit` for example in [Query Parameters](https://fastapi.tiangolo.com/tutorial/query-params/). PR [#366](https://github.com/tiangolo/fastapi/pull/366) by [@Smashman](https://github.com/Smashman).
|
||||
|
||||
* Update wording in docs about [OAuth2 scopes](https://fastapi.tiangolo.com/tutorial/security/oauth2-scopes/). PR [#371](https://github.com/tiangolo/fastapi/pull/371) by [@cjw296](https://github.com/cjw296).
|
||||
|
||||
* Update docs for `Enum`s to inherit from `str` and improve Swagger UI rendering. PR [#351](https://github.com/tiangolo/fastapi/pull/351).
|
||||
|
||||
* Fix regression, add Swagger UI deep linking again. PR [#350](https://github.com/tiangolo/fastapi/pull/350).
|
||||
|
||||
* Add test for having path templates in `prefix` of `.include_router`. PR [#349](https://github.com/tiangolo/fastapi/pull/349).
|
||||
|
||||
* Add note to docs: [Include the same router multiple times with different `prefix`](https://fastapi.tiangolo.com/tutorial/bigger-applications/#include-the-same-router-multiple-times-with-different-prefix). PR [#348](https://github.com/tiangolo/fastapi/pull/348).
|
||||
|
||||
* Fix OpenAPI/JSON Schema generation for two functions with the same name (in different modules) with the same composite bodies.
|
||||
* Composite bodies' IDs are now based on path, not only on route name, as the auto-generated name uses the function names, that can be duplicated in different modules.
|
||||
* The same new ID generation applies to response models.
|
||||
* This also changes the generated title for those models.
|
||||
* Only composite bodies and response models are affected because those are generated dynamically, they don't have a module (a Python file).
|
||||
* This also adds the possibility of using `.include_router()` with the same `APIRouter` *multiple* times, with different prefixes, e.g. `/api/v2` and `/api/latest`, and it will now work correctly.
|
||||
* PR [#347](https://github.com/tiangolo/fastapi/pull/347).
|
||||
|
||||
## 0.31.0
|
||||
|
||||
* Upgrade Pydantic supported version to `0.29.0`.
|
||||
* New supported version range is `"pydantic >=0.28,<=0.29.0"`.
|
||||
* This adds support for Pydantic [Generic Models](https://pydantic-docs.helpmanual.io/#generic-models), kudos to [@dmontagu](https://github.com/dmontagu).
|
||||
* PR [#344](https://github.com/tiangolo/fastapi/pull/344).
|
||||
|
||||
## 0.30.1
|
||||
|
||||
* Add section in docs about [External Links and Articles](https://fastapi.tiangolo.com/external-links/). PR [#341](https://github.com/tiangolo/fastapi/pull/341).
|
||||
|
||||
* Remove `Pipfile.lock` from the repository as it is only used by FastAPI contributors (developers of FastAPI itself). See the PR for more details. PR [#340](https://github.com/tiangolo/fastapi/pull/340).
|
||||
|
||||
* Update section about [Help FastAPI - Get Help](https://fastapi.tiangolo.com/help-fastapi/). PR [#339](https://github.com/tiangolo/fastapi/pull/339).
|
||||
|
||||
* Refine internal type declarations to improve/remove Mypy errors in users' code. PR [#338](https://github.com/tiangolo/fastapi/pull/338).
|
||||
|
||||
* Update and clarify [SQL tutorial with SQLAlchemy](https://fastapi.tiangolo.com/tutorial/sql-databases/). PR [#331](https://github.com/tiangolo/fastapi/pull/331) by [@mariacamilagl](https://github.com/mariacamilagl).
|
||||
|
||||
* Add SQLite [online viewers to the docs](https://fastapi.tiangolo.com/tutorial/sql-databases/#interact-with-the-database-directly). PR [#330](https://github.com/tiangolo/fastapi/pull/330) by [@cyrilbois](https://github.com/cyrilbois).
|
||||
|
||||
## 0.30.0
|
||||
|
||||
* Add support for Pydantic's ORM mode:
|
||||
* Updated documentation about SQL with SQLAlchemy, using Pydantic models with ORM mode, SQLAlchemy models with relations, separation of files, simplification of code and other changes. New docs: [SQL (Relational) Databases](https://fastapi.tiangolo.com/tutorial/sql-databases/).
|
||||
* The new support for ORM mode fixes issues/adds features related to ORMs with lazy-loading, hybrid properties, dynamic/getters (using `@property` decorators) and several other use cases.
|
||||
* This applies to ORMs like SQLAlchemy, Peewee, Tortoise ORM, GINO ORM and virtually any other.
|
||||
* If your *path operations* return an arbitrary object with attributes (e.g. `my_item.name` instead of `my_item["name"]`) AND you use a `response_model`, make sure to update the Pydantic models with `orm_mode = True` as described in the docs (link above).
|
||||
* New documentation about receiving plain `dict`s as request bodies: [Bodies of arbitrary `dict`s](https://fastapi.tiangolo.com/tutorial/body-nested-models/#bodies-of-arbitrary-dicts).
|
||||
* New documentation about returning arbitrary `dict`s in responses: [Response with arbitrary `dict`](https://fastapi.tiangolo.com/tutorial/extra-models/#response-with-arbitrary-dict).
|
||||
* **Technical Details**:
|
||||
* When declaring a `response_model` it is used directly to generate the response content, from whatever was returned from the *path operation function*.
|
||||
* Before this, the return content was first passed through `jsonable_encoder` to ensure it was a "jsonable" object, like a `dict`, instead of an arbitrary object with attributes (like an ORM model). That's why you should make sure to update your Pydantic models for objects with attributes to use `orm_mode = True`.
|
||||
* If you don't have a `response_model`, the return object will still be passed through `jsonable_encoder` first.
|
||||
* When a `response_model` is declared, the same `response_model` type declaration won't be used as is, it will be "cloned" to create an new one (a cloned Pydantic `Field` with all the submodels cloned as well).
|
||||
* This avoids/fixes a potential security issue: as the returned object is passed directly to Pydantic, if the returned object was a subclass of the `response_model` (e.g. you return a `UserInDB` that inherits from `User` but contains extra fields, like `hashed_password`, and `User` is used in the `response_model`), it would still pass the validation (because `UserInDB` is a subclass of `User`) and the object would be returned as-is, including the `hashed_password`. To fix this, the declared `response_model` is cloned, if it is a Pydantic model class (or contains Pydantic model classes in it, e.g. in a `List[Item]`), the Pydantic model class(es) will be a different one (the "cloned" one). So, an object that is a subclass won't simply pass the validation and returned as-is, because it is no longer a sub-class of the cloned `response_model`. Instead, a new Pydantic model object will be created with the contents of the returned object. So, it will be a new object (made with the data from the returned one), and will be filtered by the cloned `response_model`, containing only the declared fields as normally.
|
||||
* PR [#322](https://github.com/tiangolo/fastapi/pull/322).
|
||||
|
||||
* Remove/clean unused RegEx code in routing. PR [#314](https://github.com/tiangolo/fastapi/pull/314) by [@dmontagu](https://github.com/dmontagu).
|
||||
|
||||
* Use default response status code descriptions for additional responses. PR [#313](https://github.com/tiangolo/fastapi/pull/313) by [@duxiaoyao](https://github.com/duxiaoyao).
|
||||
|
||||
* Upgrade Pydantic support to `0.28`. PR [#320](https://github.com/tiangolo/fastapi/pull/320) by [@jekirl](https://github.com/jekirl).
|
||||
|
||||
## 0.29.1
|
||||
|
||||
* Fix handling an empty-body request with a required body param. PR [#311](https://github.com/tiangolo/fastapi/pull/311).
|
||||
|
||||
* Fix broken link in docs: [Return a Response directly](https://fastapi.tiangolo.com/tutorial/response-directly/). PR [#306](https://github.com/tiangolo/fastapi/pull/306) by [@dmontagu](https://github.com/dmontagu).
|
||||
|
||||
* Fix docs discrepancy in docs for [Response Model](https://fastapi.tiangolo.com/tutorial/response-model/). PR [#288](https://github.com/tiangolo/fastapi/pull/288) by [@awiddersheim](https://github.com/awiddersheim).
|
||||
|
||||
## 0.29.0
|
||||
|
||||
* Add support for declaring a `Response` parameter:
|
||||
* This allows declaring:
|
||||
* [Response Cookies](https://fastapi.tiangolo.com/tutorial/response-cookies/).
|
||||
* [Response Headers](https://fastapi.tiangolo.com/tutorial/response-headers/).
|
||||
* An HTTP Status Code different than the default: [Response - Change Status Code](https://fastapi.tiangolo.com/tutorial/response-change-status-code/).
|
||||
* All of this while still being able to return arbitrary objects (`dict`, DB model, etc).
|
||||
* Update attribution to Hug, for inspiring the `response` parameter pattern.
|
||||
* PR [#294](https://github.com/tiangolo/fastapi/pull/294).
|
||||
|
||||
## 0.28.0
|
||||
|
||||
* Implement dependency cache per request.
|
||||
* This avoids calling each dependency multiple times for the same request.
|
||||
* This is useful while calling external services, performing costly computation, etc.
|
||||
* This also means that if a dependency was declared as a *path operation decorator* dependency, possibly at the router level (with `.include_router()`) and then it is declared again in a specific *path operation*, the dependency will be called only once.
|
||||
* The cache can be disabled per dependency declaration, using `use_cache=False` as in `Depends(your_dependency, use_cache=False)`.
|
||||
* Updated docs at: [Using the same dependency multiple times](https://fastapi.tiangolo.com/tutorial/dependencies/sub-dependencies/#using-the-same-dependency-multiple-times).
|
||||
* PR [#292](https://github.com/tiangolo/fastapi/pull/292).
|
||||
|
||||
* Implement dependency overrides for testing.
|
||||
* This allows using overrides/mocks of dependencies during tests.
|
||||
* New docs: [Testing Dependencies with Overrides](https://fastapi.tiangolo.com/tutorial/testing-dependencies/).
|
||||
* PR [#291](https://github.com/tiangolo/fastapi/pull/291).
|
||||
|
||||
## 0.27.2
|
||||
|
||||
* Fix path and query parameters receiving `dict` as a valid type. It should be mapped to a body payload. PR [#287](https://github.com/tiangolo/fastapi/pull/287). Updated docs at: [Query parameter list / multiple values with defaults: Using `list`](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#using-list).
|
||||
|
||||
## 0.27.1
|
||||
|
||||
* Fix `auto_error=False` handling in `HTTPBearer` security scheme. Do not `raise` when there's an incorrect `Authorization` header if `auto_error=False`. PR [#282](https://github.com/tiangolo/fastapi/pull/282).
|
||||
|
||||
* Fix type declaration of `HTTPException`. PR [#279](https://github.com/tiangolo/fastapi/pull/279).
|
||||
|
||||
## 0.27.0
|
||||
|
||||
* Fix broken link in docs about OAuth 2.0 with scopes. PR [#275](https://github.com/tiangolo/fastapi/pull/275) by [@dmontagu](https://github.com/dmontagu).
|
||||
|
||||
* Refactor param extraction using Pydantic `Field`:
|
||||
* Large refactor, improvement, and simplification of param extraction from *path operations*.
|
||||
* Fix/add support for list *query parameters* with list defaults. New documentation: [Query parameter list / multiple values with defaults](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#query-parameter-list-multiple-values-with-defaults).
|
||||
* Add support for enumerations in *path operation* parameters. New documentation: [Path Parameters: Predefined values](https://fastapi.tiangolo.com/tutorial/path-params/#predefined-values).
|
||||
* Add support for type annotations using `Optional` as in `param: Optional[str] = None`. New documentation: [Optional type declarations](https://fastapi.tiangolo.com/tutorial/query-params/#optional-type-declarations).
|
||||
* PR [#278](https://github.com/tiangolo/fastapi/pull/278).
|
||||
|
||||
## 0.26.0
|
||||
|
||||
* Separate error handling for validation errors.
|
||||
* This will allow developers to customize the exception handlers.
|
||||
* Document better how to handle exceptions and use error handlers.
|
||||
* Include `RequestValidationError` and `WebSocketRequestValidationError` (this last one will be useful once [encode/starlette#527](https://github.com/encode/starlette/pull/527) or equivalent is merged).
|
||||
* New documentation about exceptions handlers:
|
||||
* [Install custom exception handlers](https://fastapi.tiangolo.com/tutorial/handling-errors/#install-custom-exception-handlers).
|
||||
* [Override the default exception handlers](https://fastapi.tiangolo.com/tutorial/handling-errors/#override-the-default-exception-handlers).
|
||||
* [Re-use **FastAPI's** exception handlers](https://fastapi.tiangolo.com/tutorial/handling-errors/#re-use-fastapis-exception-handlers).
|
||||
* PR [#273](https://github.com/tiangolo/fastapi/pull/273).
|
||||
|
||||
* Fix support for *paths* in *path parameters* without needing explicit `Path(...)`.
|
||||
* PR [#256](https://github.com/tiangolo/fastapi/pull/256).
|
||||
* Documented in PR [#272](https://github.com/tiangolo/fastapi/pull/272) by [@wshayes](https://github.com/wshayes).
|
||||
* New documentation at: [Path Parameters containing paths](https://fastapi.tiangolo.com/tutorial/path-params/#path-parameters-containing-paths).
|
||||
|
||||
* Update docs for testing FastAPI. Include using `POST`, sending JSON, testing headers, etc. New documentation: [Testing](https://fastapi.tiangolo.com/tutorial/testing/#testing-extended-example). PR [#271](https://github.com/tiangolo/fastapi/pull/271).
|
||||
|
||||
* Fix type declaration of `response_model` to allow generic Python types as `List[Model]`. Mainly to fix `mypy` for users. PR [#266](https://github.com/tiangolo/fastapi/pull/266).
|
||||
|
||||
## 0.25.0
|
||||
|
||||
* Add support for Pydantic's `include`, `exclude`, `by_alias`.
|
||||
* Update documentation: [Response Model](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
|
||||
* Add docs for: [Body - updates](https://fastapi.tiangolo.com/tutorial/body-updates/), using Pydantic's `skip_defaults`.
|
||||
* Add method consistency tests.
|
||||
* PR [#264](https://github.com/tiangolo/fastapi/pull/264).
|
||||
|
||||
* Add `CONTRIBUTING.md` file to GitHub, to help new contributors. PR [#255](https://github.com/tiangolo/fastapi/pull/255) by [@wshayes](https://github.com/wshayes).
|
||||
|
||||
* Add support for Pydantic's `skip_defaults`:
|
||||
* There's a new *path operation decorator* parameter `response_model_skip_defaults`.
|
||||
* The name of the parameter will most probably change in a future version to `response_skip_defaults`, `model_skip_defaults` or something similar.
|
||||
* New [documentation section about using `response_model_skip_defaults`](https://fastapi.tiangolo.com/tutorial/response-model/#response-model-encoding-parameters).
|
||||
* PR [#248](https://github.com/tiangolo/fastapi/pull/248) by [@wshayes](https://github.com/wshayes).
|
||||
|
||||
## 0.24.0
|
||||
|
||||
* Add support for WebSockets with dependencies and parameters.
|
||||
* Support included for:
|
||||
* `Depends`
|
||||
* `Security`
|
||||
* `Cookie`
|
||||
* `Header`
|
||||
* `Path`
|
||||
* `Query`
|
||||
* ...as these are compatible with the WebSockets protocol (e.g. `Body` is not).
|
||||
* [Updated documentation for WebSockets](https://fastapi.tiangolo.com/tutorial/websockets/).
|
||||
* PR [#178](https://github.com/tiangolo/fastapi/pull/178) by [@jekirl](https://github.com/jekirl).
|
||||
|
||||
* Upgrade the compatible version of Pydantic to `0.26.0`.
|
||||
* This includes JSON Schema support for IP address and network objects, bug fixes, and other features.
|
||||
* PR [#247](https://github.com/tiangolo/fastapi/pull/247) by [@euri10](https://github.com/euri10).
|
||||
|
||||
## 0.23.0
|
||||
|
||||
* Upgrade the compatible version of Starlette to `0.12.0`.
|
||||
* This includes support for ASGI 3 (the latest version of the standard).
|
||||
* It's now possible to use [Starlette's `StreamingResponse`](https://www.starlette.io/responses/#streamingresponse) with iterators, like [file-like](https://docs.python.org/3/glossary.html#term-file-like-object) objects (as those returned by `open()`).
|
||||
* It's now possible to use the low level utility `iterate_in_threadpool` from `starlette.concurrency` (for advanced scenarios).
|
||||
* PR [#243](https://github.com/tiangolo/fastapi/pull/243).
|
||||
|
||||
* Add OAuth2 redirect page for Swagger UI. This allows having delegated authentication in the Swagger UI docs. For this to work, you need to add `{your_origin}/docs/oauth2-redirect` to the allowed callbacks in your OAuth2 provider (in Auth0, Facebook, Google, etc).
|
||||
* For example, during development, it could be `http://localhost:8000/docs/oauth2-redirect`.
|
||||
* Have in mind that this callback URL is independent of whichever one is used by your frontend. You might also have another callback at `https://yourdomain.com/login/callback`.
|
||||
* This is only to allow delegated authentication in the API docs with Swagger UI.
|
||||
* PR [#198](https://github.com/tiangolo/fastapi/pull/198) by [@steinitzu](https://github.com/steinitzu).
|
||||
|
||||
* Make Swagger UI and ReDoc route handlers (*path operations*) be `async` functions instead of lambdas to improve performance. PR [#241](https://github.com/tiangolo/fastapi/pull/241) by [@Trim21](https://github.com/Trim21).
|
||||
|
||||
* Make Swagger UI and ReDoc URLs parameterizable, allowing to host and serve local versions of them and have offline docs. PR [#112](https://github.com/tiangolo/fastapi/pull/112) by [@euri10](https://github.com/euri10).
|
||||
|
||||
## 0.22.0
|
||||
|
||||
* Add support for `dependencies` parameter:
|
||||
* A parameter in *path operation decorators*, for dependencies that should be executed but the return value is not important or not used in the *path operation function*.
|
||||
* A parameter in the `.include_router()` method of FastAPI applications and routers, to include dependencies that should be executed in each *path operation* in a router.
|
||||
* This is useful, for example, to require authentication or permissions in specific group of *path operations*.
|
||||
* Different `dependencies` can be applied to different routers.
|
||||
* These `dependencies` are run before the normal parameter dependencies. And normal dependencies are run too. They can be combined.
|
||||
* Dependencies declared in a router are executed first, then the ones defined in *path operation decorators*, and then the ones declared in normal parameters. They are all combined and executed.
|
||||
* All this also supports using `Security` with `scopes` in those `dependencies` parameters, for more advanced OAuth 2.0 security scenarios with scopes.
|
||||
* New documentation about [dependencies in *path operation decorators*](https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-in-path-operation-decorators/).
|
||||
* New documentation about [dependencies in the `include_router()` method](https://fastapi.tiangolo.com/tutorial/bigger-applications/#include-an-apirouter-with-a-prefix-tags-responses-and-dependencies).
|
||||
* PR [#235](https://github.com/tiangolo/fastapi/pull/235).
|
||||
|
||||
* Fix OpenAPI documentation of Starlette URL convertors. Specially useful when using `path` convertors, to take a whole path as a parameter, like `/some/url/{p:path}`. PR [#234](https://github.com/tiangolo/fastapi/pull/234) by [@euri10](https://github.com/euri10).
|
||||
|
||||
* Make default parameter utilities exported from `fastapi` be functions instead of classes (the new functions return instances of those classes). To be able to override the return types and fix `mypy` errors in FastAPI's users' code. Applies to `Path`, `Query`, `Header`, `Cookie`, `Body`, `Form`, `File`, `Depends`, and `Security`. PR [#226](https://github.com/tiangolo/fastapi/pull/226) and PR [#231](https://github.com/tiangolo/fastapi/pull/231).
|
||||
|
||||
* Separate development scripts `test.sh`, `lint.sh`, and `format.sh`. PR [#232](https://github.com/tiangolo/fastapi/pull/232).
|
||||
|
||||
* Re-enable `black` formatting checks for Python 3.7. PR [#229](https://github.com/tiangolo/fastapi/pull/229) by [@zamiramir](https://github.com/zamiramir).
|
||||
|
||||
## 0.21.0
|
||||
|
||||
* On body parsing errors, raise `from` previous exception, to allow better introspection in logging code. PR [#192](https://github.com/tiangolo/fastapi/pull/195) by [@ricardomomm](https://github.com/ricardomomm).
|
||||
|
||||
* Use Python logger named "`fastapi`" instead of root logger. PR [#222](https://github.com/tiangolo/fastapi/pull/222) by [@euri10](https://github.com/euri10).
|
||||
|
||||
* Upgrade Pydantic to version 0.25. PR [#225](https://github.com/tiangolo/fastapi/pull/225) by [@euri10](https://github.com/euri10).
|
||||
|
||||
* Fix typo in routing. PR [#221](https://github.com/tiangolo/fastapi/pull/221) by [@djlambert](https://github.com/djlambert).
|
||||
|
||||
## 0.20.1
|
||||
|
||||
* Add typing information to package including file `py.typed`. PR [#209](https://github.com/tiangolo/fastapi/pull/209) by [@meadsteve](https://github.com/meadsteve).
|
||||
|
||||
* Add FastAPI bot for Gitter. To automatically announce new releases. PR [#189](https://github.com/tiangolo/fastapi/pull/189).
|
||||
|
||||
## 0.20.0
|
||||
|
||||
* Upgrade OAuth2:
|
||||
* Upgrade Password flow using Bearer tokens to use the correct HTTP status code 401 `UNAUTHORIZED`, with `WWW-Authenticate` headers.
|
||||
* Update, simplify, and improve all the [security docs](https://fastapi.tiangolo.com/tutorial/security/intro/).
|
||||
* Add new `scope_str` to `SecurityScopes` and update docs: [OAuth2 scopes](https://fastapi.tiangolo.com/tutorial/security/oauth2-scopes/).
|
||||
* Update docs, images, tests.
|
||||
* PR [#188](https://github.com/tiangolo/fastapi/pull/188).
|
||||
|
||||
* Include [Hypercorn](https://gitlab.com/pgjones/hypercorn) as an alternative ASGI server in the docs. PR [#187](https://github.com/tiangolo/fastapi/pull/187).
|
||||
|
||||
* Add docs for [Static Files](https://fastapi.tiangolo.com/tutorial/static-files/) and [Templates](https://fastapi.tiangolo.com/tutorial/templates/). PR [#186](https://github.com/tiangolo/fastapi/pull/186).
|
||||
|
||||
* Add docs for handling [Response Cookies](https://fastapi.tiangolo.com/tutorial/response-cookies/) and [Response Headers](https://fastapi.tiangolo.com/tutorial/response-headers/). PR [#185](https://github.com/tiangolo/fastapi/pull/185).
|
||||
|
||||
* Fix typos in docs. PR [#176](https://github.com/tiangolo/fastapi/pull/176) by [@chdsbd](https://github.com/chdsbd).
|
||||
|
||||
## 0.19.0
|
||||
|
||||
* Rename _path operation decorator_ parameter `content_type` to `response_class`. PR [#183](https://github.com/tiangolo/fastapi/pull/183).
|
||||
|
||||
* Add docs:
|
||||
* How to use the `jsonable_encoder` in [JSON compatible encoder](https://fastapi.tiangolo.com/tutorial/encoder/).
|
||||
* How to [Return a Response directly](https://fastapi.tiangolo.com/tutorial/response-directly/).
|
||||
* Update how to use a [Custom Response Class](https://fastapi.tiangolo.com/tutorial/custom-response/).
|
||||
* PR [#184](https://github.com/tiangolo/fastapi/pull/184).
|
||||
|
||||
## 0.18.0
|
||||
|
||||
* Add docs for [HTTP Basic Auth](https://fastapi.tiangolo.com/tutorial/custom-response/). PR [#177](https://github.com/tiangolo/fastapi/pull/177).
|
||||
|
||||
* Upgrade HTTP Basic Auth handling with automatic headers (automatic browser login prompt). PR [#175](https://github.com/tiangolo/fastapi/pull/175).
|
||||
|
||||
* Update dependencies for security. PR [#174](https://github.com/tiangolo/fastapi/pull/174).
|
||||
|
||||
* Add docs for [Middleware](https://fastapi.tiangolo.com/tutorial/middleware/). PR [#173](https://github.com/tiangolo/fastapi/pull/173).
|
||||
|
||||
## 0.17.0
|
||||
|
||||
* Make Flit publish from CI. PR [#170](https://github.com/tiangolo/fastapi/pull/170).
|
||||
|
||||
* Add documentation about handling [CORS (Cross-Origin Resource Sharing)](https://fastapi.tiangolo.com/tutorial/cors/). PR [#169](https://github.com/tiangolo/fastapi/pull/169).
|
||||
|
||||
* By default, encode by alias. This allows using Pydantic `alias` parameters working by default. PR [#168](https://github.com/tiangolo/fastapi/pull/168).
|
||||
|
||||
## 0.16.0
|
||||
|
||||
* Upgrade _path operation_ `doctsring` parsing to support proper Markdown descriptions. New documentation at [Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/#description-from-docstring). PR [#163](https://github.com/tiangolo/fastapi/pull/163).
|
||||
|
||||
* Refactor internal usage of Pydantic to use correct data types. PR [#164](https://github.com/tiangolo/fastapi/pull/164).
|
||||
|
||||
* Upgrade Pydantic to version `0.23`. PR [#160](https://github.com/tiangolo/fastapi/pull/160) by [@euri10](https://github.com/euri10).
|
||||
|
||||
* Fix typo in Tutorial about Extra Models. PR [#159](https://github.com/tiangolo/fastapi/pull/159) by [@danielmichaels](https://github.com/danielmichaels).
|
||||
|
||||
* Fix [Query Parameters](https://fastapi.tiangolo.com/tutorial/query-params/) URL examples in docs. PR [#157](https://github.com/tiangolo/fastapi/pull/157) by [@hayata-yamamoto](https://github.com/hayata-yamamoto).
|
||||
|
||||
## 0.15.0
|
||||
|
||||
* Add support for multiple file uploads (as a single form field). New docs at: [Multiple file uploads](https://fastapi.tiangolo.com/tutorial/request-files/#multiple-file-uploads). PR [#158](https://github.com/tiangolo/fastapi/pull/158).
|
||||
|
||||
* Add docs for: [Additional Status Codes](https://fastapi.tiangolo.com/tutorial/additional-status-codes/). PR [#156](https://github.com/tiangolo/fastapi/pull/156).
|
||||
|
||||
## 0.14.0
|
||||
|
||||
* Improve automatically generated names of path operations in OpenAPI (in API docs). A function `read_items` instead of having a generated name "Read Items Get" will have "Read Items". PR [#155](https://github.com/tiangolo/fastapi/pull/155).
|
||||
|
||||
* Add docs for: [Testing **FastAPI**](https://fastapi.tiangolo.com/tutorial/testing/). PR [#151](https://github.com/tiangolo/fastapi/pull/151).
|
||||
|
||||
* Update `/docs` Swagger UI to enable deep linking. This allows sharing the URL pointing directly to the path operation documentation in the docs. PR [#148](https://github.com/tiangolo/fastapi/pull/148) by [@wshayes](https://github.com/wshayes).
|
||||
|
||||
* Update development dependencies, `Pipfile.lock`. PR [#150](https://github.com/tiangolo/fastapi/pull/150).
|
||||
|
||||
* Include Falcon and Hug in: [Alternatives, Inspiration and Comparisons](https://fastapi.tiangolo.com/alternatives/).
|
||||
|
||||
## 0.13.0
|
||||
|
||||
* Improve/upgrade OAuth2 scopes support with `SecurityScopes`:
|
||||
* `SecurityScopes` can be declared as a parameter like `Request`, to get the scopes of all super-dependencies/dependants.
|
||||
* Improve `Security` handling, merging scopes when declaring `SecurityScopes`.
|
||||
* Allow using `SecurityBase` (like `OAuth2`) classes with `Depends` and still document them. `Security` now is needed only to declare `scopes`.
|
||||
* Updated docs about: [OAuth2 with Password (and hashing), Bearer with JWT tokens](https://fastapi.tiangolo.com/tutorial/security/oauth2-jwt/).
|
||||
* New docs about: [OAuth2 scopes](https://fastapi.tiangolo.com/tutorial/security/oauth2-scopes/).
|
||||
* PR [#141](https://github.com/tiangolo/fastapi/pull/141).
|
||||
|
||||
## 0.12.1
|
||||
|
||||
* Fix bug: handling additional `responses` in `APIRouter.include_router()`. PR [#140](https://github.com/tiangolo/fastapi/pull/140).
|
||||
|
||||
* Fix typo in SQL tutorial. PR [#138](https://github.com/tiangolo/fastapi/pull/138) by [@mostaphaRoudsari](https://github.com/mostaphaRoudsari).
|
||||
|
||||
* Fix typos in section about nested models and OAuth2 with JWT. PR [#127](https://github.com/tiangolo/fastapi/pull/127) by [@mmcloud](https://github.com/mmcloud).
|
||||
|
||||
## 0.12.0
|
||||
|
||||
* Add additional `responses` parameter to _path operation decorators_ to extend responses in OpenAPI (and API docs).
|
||||
* It also allows extending existing responses generated from `response_model`, declare other media types (like images), etc.
|
||||
* The new documentation is here: [Additional Responses](https://fastapi.tiangolo.com/tutorial/additional-responses/).
|
||||
* `responses` can also be added to `.include_router()`, the updated docs are here: [Bigger Applications](https://fastapi.tiangolo.com/tutorial/bigger-applications/#add-some-custom-tags-and-responses).
|
||||
* PR [#97](https://github.com/tiangolo/fastapi/pull/97) originally initiated by [@barsi](https://github.com/barsi).
|
||||
* Update `scripts/test-cov-html.sh` to allow passing extra parameters like `-vv`, for development.
|
||||
|
||||
## 0.11.0
|
||||
|
||||
* Add `auto_error` parameter to security utility functions. Allowing them to be optional. Also allowing to have multiple alternative security schemes that are then checked in a single dependency instead of each one verifying and returning the error to the client automatically when not satisfied. PR <a href="https://github.com/tiangolo/fastapi/pull/134" target="_blank">#134</a>.
|
||||
* Add `auto_error` parameter to security utility functions. Allowing them to be optional. Also allowing to have multiple alternative security schemes that are then checked in a single dependency instead of each one verifying and returning the error to the client automatically when not satisfied. PR [#134](https://github.com/tiangolo/fastapi/pull/134).
|
||||
|
||||
* Update <a href="https://fastapi.tiangolo.com/tutorial/sql-databases/#create-a-middleware-to-handle-sessions" target="_blank">SQL Tutorial</a> to close database sessions even when there are exceptions. PR <a href="https://github.com/tiangolo/fastapi/pull/89" target="_blank">#89</a> by <a href="https://github.com/alexiri" target="_blank">@alexiri</a>.
|
||||
* Update [SQL Tutorial](https://fastapi.tiangolo.com/tutorial/sql-databases/#create-a-middleware-to-handle-sessions) to close database sessions even when there are exceptions. PR [#89](https://github.com/tiangolo/fastapi/pull/89) by [@alexiri](https://github.com/alexiri).
|
||||
|
||||
* Fix duplicate dependency in `pyproject.toml`. PR <a href="https://github.com/tiangolo/fastapi/pull/128" target="_blank">#128</a> by <a href="https://github.com/zxalif" target="_blank">@zxalif</a>.
|
||||
* Fix duplicate dependency in `pyproject.toml`. PR [#128](https://github.com/tiangolo/fastapi/pull/128) by [@zxalif](https://github.com/zxalif).
|
||||
|
||||
## 0.10.3
|
||||
|
||||
* Add Gitter chat, badge, links, etc. <a href="https://gitter.im/tiangolo/fastapi" target="_blank">https://gitter.im/tiangolo/fastapi
|
||||
</a>. PR <a href="https://github.com/tiangolo/fastapi/pull/117" target="_blank">#117</a>.
|
||||
* Add Gitter chat, badge, links, etc. [https://gitter.im/tiangolo/fastapi](https://gitter.im/tiangolo/fastapi) . PR [#117](https://github.com/tiangolo/fastapi/pull/117).
|
||||
|
||||
* Add docs about <a href="https://fastapi.tiangolo.com/tutorial/extending-openapi/" target="_blank">Extending OpenAPI</a>. PR <a href="https://github.com/tiangolo/fastapi/pull/126" target="_blank">#126</a>.
|
||||
* Add docs about [Extending OpenAPI](https://fastapi.tiangolo.com/tutorial/extending-openapi/). PR [#126](https://github.com/tiangolo/fastapi/pull/126).
|
||||
|
||||
* Make Travis run Ubuntu Xenial (newer version) and Python 3.7 instead of Python 3.7-dev. PR <a href="https://github.com/tiangolo/fastapi/pull/92" target="_blank">#92</a> by <a href="https://github.com/blueyed" target="_blank">@blueyed</a>.
|
||||
* Make Travis run Ubuntu Xenial (newer version) and Python 3.7 instead of Python 3.7-dev. PR [#92](https://github.com/tiangolo/fastapi/pull/92) by [@blueyed](https://github.com/blueyed).
|
||||
|
||||
* Fix duplicated param variable creation. PR <a href="https://github.com/tiangolo/fastapi/pull/123" target="_blank">#123</a> by <a href="https://github.com/yihuang" target="_blank">@yihuang</a>.
|
||||
* Fix duplicated param variable creation. PR [#123](https://github.com/tiangolo/fastapi/pull/123) by [@yihuang](https://github.com/yihuang).
|
||||
|
||||
* Add note in <a href="https://fastapi.tiangolo.com/tutorial/response-model/" target="_blank">Response Model docs</a> about why using a function parameter instead of a function return type annotation. PR <a href="https://github.com/tiangolo/fastapi/pull/109" target="_blank">#109</a> by <a href="https://github.com/JHSaunders" target="_blank">@JHSaunders</a>.
|
||||
* Add note in [Response Model docs](https://fastapi.tiangolo.com/tutorial/response-model/) about why using a function parameter instead of a function return type annotation. PR [#109](https://github.com/tiangolo/fastapi/pull/109) by [@JHSaunders](https://github.com/JHSaunders).
|
||||
|
||||
* Fix event docs (startup/shutdown) function name. PR <a href="https://github.com/tiangolo/fastapi/pull/105" target="_blank">#105</a> by <a href="https://github.com/stratosgear" target="_blank">@stratosgear</a>.
|
||||
* Fix event docs (startup/shutdown) function name. PR [#105](https://github.com/tiangolo/fastapi/pull/105) by [@stratosgear](https://github.com/stratosgear).
|
||||
|
||||
## 0.10.2
|
||||
|
||||
* Fix OpenAPI (JSON Schema) for declarations of Python `Union` (JSON Schema `additionalProperties`). PR <a href="https://github.com/tiangolo/fastapi/pull/121" target="_blank">#121</a>.
|
||||
* Fix OpenAPI (JSON Schema) for declarations of Python `Union` (JSON Schema `additionalProperties`). PR [#121](https://github.com/tiangolo/fastapi/pull/121).
|
||||
|
||||
* Update <a href="https://fastapi.tiangolo.com/tutorial/background-tasks/" target="_blank">Background Tasks</a> with a note on Celery.
|
||||
* Update [Background Tasks](https://fastapi.tiangolo.com/tutorial/background-tasks/) with a note on Celery.
|
||||
|
||||
* Document response models using unions and lists, updated at: <a href="https://fastapi.tiangolo.com/tutorial/extra-models/" target="_blank">Extra Models</a>. PR <a href="https://github.com/tiangolo/fastapi/pull/108" target="_blank">#108</a>.
|
||||
* Document response models using unions and lists, updated at: [Extra Models](https://fastapi.tiangolo.com/tutorial/extra-models/). PR [#108](https://github.com/tiangolo/fastapi/pull/108).
|
||||
|
||||
## 0.10.1
|
||||
|
||||
* Add docs and tests for <a href="https://github.com/encode/databases" target="_blank">encode/databases</a>. New docs at: <a href="https://fastapi.tiangolo.com/tutorial/async-sql-databases/" target="_blank">Async SQL (Relational) Databases</a>. PR <a href="https://github.com/tiangolo/fastapi/pull/107" target="_blank">#107</a>.
|
||||
* Add docs and tests for [encode/databases](https://github.com/encode/databases). New docs at: [Async SQL (Relational) Databases](https://fastapi.tiangolo.com/tutorial/async-sql-databases/). PR [#107](https://github.com/tiangolo/fastapi/pull/107).
|
||||
|
||||
## 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 Background Tasks in _path operation functions_ and dependencies. New documentation about [Background Tasks is here](https://fastapi.tiangolo.com/tutorial/background-tasks/). PR [#103](https://github.com/tiangolo/fastapi/pull/103).
|
||||
|
||||
* 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>.
|
||||
* Add support for `.websocket_route()` in `APIRouter`. PR [#100](https://github.com/tiangolo/fastapi/pull/100) by [@euri10](https://github.com/euri10).
|
||||
|
||||
* 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>.
|
||||
* New docs section about [Events: startup - shutdown](https://fastapi.tiangolo.com/tutorial/events/). PR [#99](https://github.com/tiangolo/fastapi/pull/99).
|
||||
|
||||
## 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>.
|
||||
* Document receiving [Multiple values with the same query parameter](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#query-parameter-list-multiple-values) and [Duplicate headers](https://fastapi.tiangolo.com/tutorial/header-params/#duplicate-headers). PR [#95](https://github.com/tiangolo/fastapi/pull/95).
|
||||
|
||||
## 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>.
|
||||
* Upgrade compatible Pydantic version to `0.21.0`. PR [#90](https://github.com/tiangolo/fastapi/pull/90).
|
||||
|
||||
* Add documentation for: <a href="https://fastapi.tiangolo.com/tutorial/application-configuration/" target="_blank">Application Configuration</a>.
|
||||
* Add documentation for: [Application Configuration](https://fastapi.tiangolo.com/tutorial/application-configuration/).
|
||||
|
||||
* 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 typo in docs. PR [#76](https://github.com/tiangolo/fastapi/pull/76) by [@matthewhegarty](https://github.com/matthewhegarty).
|
||||
|
||||
* 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>.
|
||||
* Make development scripts executable. PR [#76](https://github.com/tiangolo/fastapi/pull/76) by [@euri10](https://github.com/euri10).
|
||||
|
||||
* 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>.
|
||||
* Add support for adding `tags` in `app.include_router()`. PR [#55](https://github.com/tiangolo/fastapi/pull/55) by [@euri10](https://github.com/euri10). Documentation updated in the section: [Bigger Applications](https://fastapi.tiangolo.com/tutorial/bigger-applications/).
|
||||
|
||||
* 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 docs related to Uvicorn to use new `--reload` option from version `0.5.x`. PR [#74](https://github.com/tiangolo/fastapi/pull/74).
|
||||
|
||||
* 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>.
|
||||
* Update `isort` imports and scripts to be compatible with newer versions. PR [#75](https://github.com/tiangolo/fastapi/pull/75).
|
||||
|
||||
## 0.7.1
|
||||
|
||||
* Update <a href="https://fastapi.tiangolo.com/async/#path-operation-functions" target="_blank">technical details about `async def` handling</a> with respect to previous frameworks. PR <a href="https://github.com/tiangolo/fastapi/pull/64" target="_blank">#64</a> by <a href="https://github.com/haizaar" target="_blank">@haizaar</a>.
|
||||
* Update [technical details about `async def` handling](https://fastapi.tiangolo.com/async/#path-operation-functions) with respect to previous frameworks. PR [#64](https://github.com/tiangolo/fastapi/pull/64) by [@haizaar](https://github.com/haizaar).
|
||||
|
||||
* Add <a href="https://fastapi.tiangolo.com/deployment/#raspberry-pi-and-other-architectures" target="_blank">deployment documentation for Docker in Raspberry Pi</a> and other architectures.
|
||||
* Add [deployment documentation for Docker in Raspberry Pi](https://fastapi.tiangolo.com/deployment/#raspberry-pi-and-other-architectures) and other architectures.
|
||||
|
||||
* Trigger Docker images build on Travis CI automatically. PR <a href="https://github.com/tiangolo/fastapi/pull/65" target="_blank">#65</a>.
|
||||
* Trigger Docker images build on Travis CI automatically. PR [#65](https://github.com/tiangolo/fastapi/pull/65).
|
||||
|
||||
## 0.7.0
|
||||
|
||||
* Add support for `UploadFile` in `File` parameter annotations.
|
||||
* This includes a file-like interface.
|
||||
* Here's the updated documentation for declaring <a href="https://fastapi.tiangolo.com/tutorial/request-files/#file-parameters-with-uploadfile" target="_blank"> `File` parameters with `UploadFile`</a>.
|
||||
* And here's the updated documentation for using <a href="https://fastapi.tiangolo.com/tutorial/request-forms-and-files/" target="_blank">`Form` parameters mixed with `File` parameters, supporting `bytes` and `UploadFile`</a> at the same time.
|
||||
* PR <a href="https://github.com/tiangolo/fastapi/pull/63" target="_blank">#63</a>.
|
||||
* Here's the updated documentation for declaring [`File` parameters with `UploadFile`](https://fastapi.tiangolo.com/tutorial/request-files/#file-parameters-with-uploadfile).
|
||||
* And here's the updated documentation for using [`Form` parameters mixed with `File` parameters, supporting `bytes` and `UploadFile`](https://fastapi.tiangolo.com/tutorial/request-forms-and-files/) at the same time.
|
||||
* PR [#63](https://github.com/tiangolo/fastapi/pull/63).
|
||||
|
||||
## 0.6.4
|
||||
|
||||
* Add <a href="https://fastapi.tiangolo.com/async/#very-technical-details" target="_blank">technical details about `async def` handling to docs</a>. PR <a href="https://github.com/tiangolo/fastapi/pull/61" target="_blank">#61</a>.
|
||||
* Add [technical details about `async def` handling to docs](https://fastapi.tiangolo.com/async/#very-technical-details). PR [#61](https://github.com/tiangolo/fastapi/pull/61).
|
||||
|
||||
* Add docs for <a href="https://fastapi.tiangolo.com/tutorial/debugging/" target="_blank">Debugging FastAPI applications in editors</a>.
|
||||
* Add docs for [Debugging FastAPI applications in editors](https://fastapi.tiangolo.com/tutorial/debugging/).
|
||||
|
||||
* Clarify <a href="https://fastapi.tiangolo.com/deployment/#bigger-applications" target="_blank">Bigger Applications deployed with Docker</a>.
|
||||
* Clarify [Bigger Applications deployed with Docker](https://fastapi.tiangolo.com/deployment/#bigger-applications).
|
||||
|
||||
* Fix typos in docs.
|
||||
|
||||
* Add section about <a href="https://fastapi.tiangolo.com/history-design-future/" target="_blank">History, Design and Future</a>.
|
||||
* Add section about [History, Design and Future](https://fastapi.tiangolo.com/history-design-future/).
|
||||
|
||||
* Add docs for using <a href="https://fastapi.tiangolo.com/tutorial/websockets/" target="_blank">WebSockets with **FastAPI**</a>. PR <a href="https://github.com/tiangolo/fastapi/pull/62" target="_blank">#62</a>.
|
||||
* Add docs for using [WebSockets with **FastAPI**](https://fastapi.tiangolo.com/tutorial/websockets/). PR [#62](https://github.com/tiangolo/fastapi/pull/62).
|
||||
|
||||
## 0.6.3
|
||||
|
||||
* Add Favicons to docs. PR <a href="https://github.com/tiangolo/fastapi/pull/53" target="_blank">#53</a>.
|
||||
* Add Favicons to docs. PR [#53](https://github.com/tiangolo/fastapi/pull/53).
|
||||
|
||||
## 0.6.2
|
||||
|
||||
* Introduce new project generator based on FastAPI and PostgreSQL: <a href="https://github.com/tiangolo/full-stack-fastapi-postgresql" target="_blank">https://github.com/tiangolo/full-stack-fastapi-postgresql</a>. PR <a href="https://github.com/tiangolo/fastapi/pull/52" target="_blank">#52</a>.
|
||||
* Introduce new project generator based on FastAPI and PostgreSQL: [https://github.com/tiangolo/full-stack-fastapi-postgresql](https://github.com/tiangolo/full-stack-fastapi-postgresql). PR [#52](https://github.com/tiangolo/fastapi/pull/52).
|
||||
|
||||
* Update <a href="https://fastapi.tiangolo.com/tutorial/sql-databases/" target="_blank">SQL tutorial with SQLAlchemy, using `Depends` to improve editor support and reduce code repetition</a>. PR <a href="https://github.com/tiangolo/fastapi/pull/52" target="_blank">#52</a>.
|
||||
* Update [SQL tutorial with SQLAlchemy, using `Depends` to improve editor support and reduce code repetition](https://fastapi.tiangolo.com/tutorial/sql-databases/). PR [#52](https://github.com/tiangolo/fastapi/pull/52).
|
||||
|
||||
* Improve middleware naming in tutorial for SQL with SQLAlchemy <a href="https://fastapi.tiangolo.com/tutorial/sql-databases/" target="_blank">https://fastapi.tiangolo.com/tutorial/sql-databases/</a>.
|
||||
* Improve middleware naming in tutorial for SQL with SQLAlchemy [https://fastapi.tiangolo.com/tutorial/sql-databases/](https://fastapi.tiangolo.com/tutorial/sql-databases/).
|
||||
|
||||
## 0.6.1
|
||||
|
||||
* Add docs for GraphQL: <a href="https://fastapi.tiangolo.com/tutorial/graphql/" target="_blank">https://fastapi.tiangolo.com/tutorial/graphql/</a>. PR <a href="https://github.com/tiangolo/fastapi/pull/48" target="_blank">#48</a>.
|
||||
* Add docs for GraphQL: [https://fastapi.tiangolo.com/tutorial/graphql/](https://fastapi.tiangolo.com/tutorial/graphql/). PR [#48](https://github.com/tiangolo/fastapi/pull/48).
|
||||
|
||||
## 0.6.0
|
||||
|
||||
* Update SQL with SQLAlchemy tutorial at <a href="https://fastapi.tiangolo.com/tutorial/sql-databases/" target="_blank">https://fastapi.tiangolo.com/tutorial/sql-databases/</a> using the new official `request.state`. PR <a href="https://github.com/tiangolo/fastapi/pull/45" target="_blank">#45</a>.
|
||||
* Update SQL with SQLAlchemy tutorial at [https://fastapi.tiangolo.com/tutorial/sql-databases/](https://fastapi.tiangolo.com/tutorial/sql-databases/) using the new official `request.state`. PR [#45](https://github.com/tiangolo/fastapi/pull/45).
|
||||
|
||||
* Upgrade Starlette to version `0.11.1` and add required compatibility changes. PR <a href="https://github.com/tiangolo/fastapi/pull/44" target="_blank">#44</a>.
|
||||
* Upgrade Starlette to version `0.11.1` and add required compatibility changes. PR [#44](https://github.com/tiangolo/fastapi/pull/44).
|
||||
|
||||
## 0.5.1
|
||||
|
||||
* Add section about <a href="https://fastapi.tiangolo.com/help-fastapi/" target="_blank">helping and getting help with **FastAPI**</a>.
|
||||
* Add section about [helping and getting help with **FastAPI**](https://fastapi.tiangolo.com/help-fastapi/).
|
||||
|
||||
* Add note about <a href="https://fastapi.tiangolo.com/tutorial/path-params/#order-matters" target="_blank">path operations order in docs</a>.
|
||||
* Add note about [path operations order in docs](https://fastapi.tiangolo.com/tutorial/path-params/#order-matters).
|
||||
|
||||
* Update <a href="https://fastapi.tiangolo.com/tutorial/handling-errors/" target="_blank">section about error handling</a> with more information and make relation with Starlette error handling utilities more explicit. PR <a href="https://github.com/tiangolo/fastapi/pull/41" target="_blank">#41</a>.
|
||||
* Update [section about error handling](https://fastapi.tiangolo.com/tutorial/handling-errors/) with more information and make relation with Starlette error handling utilities more explicit. PR [#41](https://github.com/tiangolo/fastapi/pull/41).
|
||||
|
||||
* Add <a href="" target="_blank">Development - Contributing section to the docs</a>. PR <a href="https://github.com/tiangolo/fastapi/pull/42" target="_blank">#42</a>.
|
||||
* Add <a href="" target="_blank">Development - Contributing section to the docs</a>. PR [#42](https://github.com/tiangolo/fastapi/pull/42).
|
||||
|
||||
## 0.5.0
|
||||
|
||||
* Add new `HTTPException` with support for custom headers. With new documentation for handling errors at: <a href="https://fastapi.tiangolo.com/tutorial/handling-errors/" target="_blank">https://fastapi.tiangolo.com/tutorial/handling-errors/</a>. PR <a href="https://github.com/tiangolo/fastapi/pull/35" target="_blank">#35</a>.
|
||||
* Add new `HTTPException` with support for custom headers. With new documentation for handling errors at: [https://fastapi.tiangolo.com/tutorial/handling-errors/](https://fastapi.tiangolo.com/tutorial/handling-errors/). PR [#35](https://github.com/tiangolo/fastapi/pull/35).
|
||||
|
||||
* Add <a href="https://fastapi.tiangolo.com/tutorial/using-request-directly/" target="_blank">documentation to use Starlette `Request` object</a> directly. Check <a href="https://github.com/tiangolo/fastapi/pull/25" target="_blank">#25</a> by <a href="https://github.com/euri10" target="_blank">@euri10</a>.
|
||||
* Add [documentation to use Starlette `Request` object](https://fastapi.tiangolo.com/tutorial/using-request-directly/) directly. Check [#25](https://github.com/tiangolo/fastapi/pull/25) by [@euri10](https://github.com/euri10).
|
||||
|
||||
* Add issue templates to simplify reporting bugs, getting help, etc: <a href="https://github.com/tiangolo/fastapi/pull/34" target="_blank">#34</a>.
|
||||
* Add issue templates to simplify reporting bugs, getting help, etc: [#34](https://github.com/tiangolo/fastapi/pull/34).
|
||||
|
||||
* Update example for the SQLAlchemy tutorial at <a href="https://fastapi.tiangolo.com/tutorial/sql-databases/" target="_blank">https://fastapi.tiangolo.com/tutorial/sql-databases/</a> using middleware and database session attached to request.
|
||||
* Update example for the SQLAlchemy tutorial at [https://fastapi.tiangolo.com/tutorial/sql-databases/](https://fastapi.tiangolo.com/tutorial/sql-databases/) using middleware and database session attached to request.
|
||||
|
||||
## 0.4.0
|
||||
|
||||
* Add `openapi_prefix`, support for reverse proxy and mounting sub-applications. See the docs at <a href="https://fastapi.tiangolo.com/tutorial/sub-applications-proxy/" target="_blank">https://fastapi.tiangolo.com/tutorial/sub-applications-proxy/</a>: <a href="https://github.com/tiangolo/fastapi/pull/26" target="_blank">#26</a> by <a href="https://github.com/kabirkhan" target="_blank">@kabirkhan</a>.
|
||||
* Add `openapi_prefix`, support for reverse proxy and mounting sub-applications. See the docs at [https://fastapi.tiangolo.com/tutorial/sub-applications-proxy/](https://fastapi.tiangolo.com/tutorial/sub-applications-proxy/): [#26](https://github.com/tiangolo/fastapi/pull/26) by [@kabirkhan](https://github.com/kabirkhan).
|
||||
|
||||
* Update <a href="https://fastapi.tiangolo.com/tutorial/sql-databases/" target="_blank">docs/tutorial for SQLAlchemy</a> including note about *DB Browser for SQLite*.
|
||||
* Update [docs/tutorial for SQLAlchemy](https://fastapi.tiangolo.com/tutorial/sql-databases/) including note about _DB Browser for SQLite_.
|
||||
|
||||
## 0.3.0
|
||||
|
||||
* Fix/add SQLAlchemy support, including ORM, and update <a href="https://fastapi.tiangolo.com/tutorial/sql-databases/" target="_blank">docs for SQLAlchemy</a>: <a href="https://github.com/tiangolo/fastapi/pull/30" target="_blank">#30</a>.
|
||||
* Fix/add SQLAlchemy support, including ORM, and update [docs for SQLAlchemy](https://fastapi.tiangolo.com/tutorial/sql-databases/): [#30](https://github.com/tiangolo/fastapi/pull/30).
|
||||
|
||||
## 0.2.1
|
||||
|
||||
* Fix `jsonable_encoder` for Pydantic models with `Config` but without `json_encoders`: <a href="https://github.com/tiangolo/fastapi/pull/29" target="_blank">#29</a>.
|
||||
* Fix `jsonable_encoder` for Pydantic models with `Config` but without `json_encoders`: [#29](https://github.com/tiangolo/fastapi/pull/29).
|
||||
|
||||
## 0.2.0
|
||||
|
||||
* Fix typos in Security section: <a href="https://github.com/tiangolo/fastapi/pull/24" target="_blank">#24</a> by <a href="https://github.com/kkinder" target="_blank">@kkinder</a>.
|
||||
* Fix typos in Security section: [#24](https://github.com/tiangolo/fastapi/pull/24) by [@kkinder](https://github.com/kkinder).
|
||||
|
||||
* Add support for Pydantic custom JSON encoders: <a href="https://github.com/tiangolo/fastapi/pull/21" target="_blank">#21</a> by <a href="https://github.com/euri10" target="_blank">@euri10</a>.
|
||||
* Add support for Pydantic custom JSON encoders: [#21](https://github.com/tiangolo/fastapi/pull/21) by [@euri10](https://github.com/euri10).
|
||||
|
||||
## 0.1.19
|
||||
|
||||
* Upgrade Starlette version to the current latest `0.10.1`: <a href="https://github.com/tiangolo/fastapi/pull/17" target="_blank">#17</a> by <a href="https://github.com/euri10" target="_blank">@euri10</a>.
|
||||
* Upgrade Starlette version to the current latest `0.10.1`: [#17](https://github.com/tiangolo/fastapi/pull/17) by [@euri10](https://github.com/euri10).
|
||||
|
||||
23
docs/src/additional_responses/tutorial001.py
Normal file
@@ -0,0 +1,23 @@
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
from starlette.responses import JSONResponse
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
id: str
|
||||
value: str
|
||||
|
||||
|
||||
class Message(BaseModel):
|
||||
message: str
|
||||
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/items/{item_id}", response_model=Item, responses={404: {"model": Message}})
|
||||
async def read_item(item_id: str):
|
||||
if item_id == "foo":
|
||||
return {"id": "foo", "value": "there goes my hero"}
|
||||
else:
|
||||
return JSONResponse(status_code=404, content={"message": "Item not found"})
|
||||
28
docs/src/additional_responses/tutorial002.py
Normal file
@@ -0,0 +1,28 @@
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
from starlette.responses import FileResponse
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
id: str
|
||||
value: str
|
||||
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get(
|
||||
"/items/{item_id}",
|
||||
response_model=Item,
|
||||
responses={
|
||||
200: {
|
||||
"content": {"image/png": {}},
|
||||
"description": "Return the JSON item or an image.",
|
||||
}
|
||||
},
|
||||
)
|
||||
async def read_item(item_id: str, img: bool = None):
|
||||
if img:
|
||||
return FileResponse("image.png", media_type="image/png")
|
||||
else:
|
||||
return {"id": "foo", "value": "there goes my hero"}
|
||||
37
docs/src/additional_responses/tutorial003.py
Normal file
@@ -0,0 +1,37 @@
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
from starlette.responses import JSONResponse
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
id: str
|
||||
value: str
|
||||
|
||||
|
||||
class Message(BaseModel):
|
||||
message: str
|
||||
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get(
|
||||
"/items/{item_id}",
|
||||
response_model=Item,
|
||||
responses={
|
||||
404: {"model": Message, "description": "The item was not found"},
|
||||
200: {
|
||||
"description": "Item requested by ID",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"example": {"id": "bar", "value": "The bar tenders"}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
async def read_item(item_id: str):
|
||||
if item_id == "foo":
|
||||
return {"id": "foo", "value": "there goes my hero"}
|
||||
else:
|
||||
return JSONResponse(status_code=404, content={"message": "Item not found"})
|
||||
30
docs/src/additional_responses/tutorial004.py
Normal file
@@ -0,0 +1,30 @@
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
from starlette.responses import FileResponse
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
id: str
|
||||
value: str
|
||||
|
||||
|
||||
responses = {
|
||||
404: {"description": "Item not found"},
|
||||
302: {"description": "The item was moved"},
|
||||
403: {"description": "Not enough privileges"},
|
||||
}
|
||||
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get(
|
||||
"/items/{item_id}",
|
||||
response_model=Item,
|
||||
responses={**responses, 200: {"content": {"image/png": {}}}},
|
||||
)
|
||||
async def read_item(item_id: str, img: bool = None):
|
||||
if img:
|
||||
return FileResponse("image.png", media_type="image/png")
|
||||
else:
|
||||
return {"id": "foo", "value": "there goes my hero"}
|
||||
20
docs/src/additional_status_codes/tutorial001.py
Normal file
@@ -0,0 +1,20 @@
|
||||
from fastapi import Body, FastAPI
|
||||
from starlette.responses import JSONResponse
|
||||
from starlette.status import HTTP_201_CREATED
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
items = {"foo": {"name": "Fighters", "size": 6}, "bar": {"name": "Tenders", "size": 3}}
|
||||
|
||||
|
||||
@app.put("/items/{item_id}")
|
||||
async def upsert_item(item_id: str, name: str = Body(None), size: int = Body(None)):
|
||||
if item_id in items:
|
||||
item = items[item_id]
|
||||
item["name"] = name
|
||||
item["size"] = size
|
||||
return item
|
||||
else:
|
||||
item = {"name": name, "size": size}
|
||||
items[item_id] = item
|
||||
return JSONResponse(status_code=HTTP_201_CREATED, content=item)
|
||||
0
docs/src/app_testing/__init__.py
Normal file
8
docs/src/app_testing/main.py
Normal file
@@ -0,0 +1,8 @@
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/")
|
||||
async def read_main():
|
||||
return {"msg": "Hello World"}
|
||||
36
docs/src/app_testing/main_b.py
Normal file
@@ -0,0 +1,36 @@
|
||||
from fastapi import FastAPI, Header, HTTPException
|
||||
from pydantic import BaseModel
|
||||
|
||||
fake_secret_token = "coneofsilence"
|
||||
|
||||
fake_db = {
|
||||
"foo": {"id": "foo", "title": "Foo", "description": "There goes my hero"},
|
||||
"bar": {"id": "bar", "title": "Bar", "description": "The bartenders"},
|
||||
}
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
id: str
|
||||
title: str
|
||||
description: str = None
|
||||
|
||||
|
||||
@app.get("/items/{item_id}", response_model=Item)
|
||||
async def read_main(item_id: str, x_token: str = Header(...)):
|
||||
if x_token != fake_secret_token:
|
||||
raise HTTPException(status_code=400, detail="Invalid X-Token header")
|
||||
if item_id not in fake_db:
|
||||
raise HTTPException(status_code=404, detail="Item not found")
|
||||
return fake_db[item_id]
|
||||
|
||||
|
||||
@app.post("/items/", response_model=Item)
|
||||
async def create_item(item: Item, x_token: str = Header(...)):
|
||||
if x_token != fake_secret_token:
|
||||
raise HTTPException(status_code=400, detail="Invalid X-Token header")
|
||||
if item.id in fake_db:
|
||||
raise HTTPException(status_code=400, detail="Item already exists")
|
||||
fake_db[item.id] = item
|
||||
return item
|
||||
11
docs/src/app_testing/test_main.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from starlette.testclient import TestClient
|
||||
|
||||
from .main import app
|
||||
|
||||
client = TestClient(app)
|
||||
|
||||
|
||||
def test_read_main():
|
||||
response = client.get("/")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {"msg": "Hello World"}
|
||||
65
docs/src/app_testing/test_main_b.py
Normal file
@@ -0,0 +1,65 @@
|
||||
from starlette.testclient import TestClient
|
||||
|
||||
from .main_b import app
|
||||
|
||||
client = TestClient(app)
|
||||
|
||||
|
||||
def test_read_item():
|
||||
response = client.get("/items/foo", headers={"X-Token": "coneofsilence"})
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {
|
||||
"id": "foo",
|
||||
"title": "Foo",
|
||||
"description": "There goes my hero",
|
||||
}
|
||||
|
||||
|
||||
def test_read_item_bad_token():
|
||||
response = client.get("/items/foo", headers={"X-Token": "hailhydra"})
|
||||
assert response.status_code == 400
|
||||
assert response.json() == {"detail": "Invalid X-Token header"}
|
||||
|
||||
|
||||
def test_read_inexistent_item():
|
||||
response = client.get("/items/baz", headers={"X-Token": "coneofsilence"})
|
||||
assert response.status_code == 404
|
||||
assert response.json() == {"detail": "Item not found"}
|
||||
|
||||
|
||||
def test_create_item():
|
||||
response = client.post(
|
||||
"/items/",
|
||||
headers={"X-Token": "coneofsilence"},
|
||||
json={"id": "foobar", "title": "Foo Bar", "description": "The Foo Barters"},
|
||||
)
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {
|
||||
"id": "foobar",
|
||||
"title": "Foo Bar",
|
||||
"description": "The Foo Barters",
|
||||
}
|
||||
|
||||
|
||||
def test_create_item_bad_token():
|
||||
response = client.post(
|
||||
"/items/",
|
||||
headers={"X-Token": "hailhydra"},
|
||||
json={"id": "bazz", "title": "Bazz", "description": "Drop the bazz"},
|
||||
)
|
||||
assert response.status_code == 400
|
||||
assert response.json() == {"detail": "Invalid X-Token header"}
|
||||
|
||||
|
||||
def test_create_existing_token():
|
||||
response = client.post(
|
||||
"/items/",
|
||||
headers={"X-Token": "coneofsilence"},
|
||||
json={
|
||||
"id": "foo",
|
||||
"title": "The Foo ID Stealers",
|
||||
"description": "There goes my stealer",
|
||||
},
|
||||
)
|
||||
assert response.status_code == 400
|
||||
assert response.json() == {"detail": "Item already exists"}
|
||||
18
docs/src/app_testing/tutorial001.py
Normal file
@@ -0,0 +1,18 @@
|
||||
from fastapi import FastAPI
|
||||
from starlette.testclient import TestClient
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/")
|
||||
async def read_main():
|
||||
return {"msg": "Hello World"}
|
||||
|
||||
|
||||
client = TestClient(app)
|
||||
|
||||
|
||||
def test_read_main():
|
||||
response = client.get("/")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {"msg": "Hello World"}
|
||||
31
docs/src/app_testing/tutorial002.py
Normal file
@@ -0,0 +1,31 @@
|
||||
from fastapi import FastAPI
|
||||
from starlette.testclient import TestClient
|
||||
from starlette.websockets import WebSocket
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/")
|
||||
async def read_main():
|
||||
return {"msg": "Hello World"}
|
||||
|
||||
|
||||
@app.websocket_route("/ws")
|
||||
async def websocket(websocket: WebSocket):
|
||||
await websocket.accept()
|
||||
await websocket.send_json({"msg": "Hello WebSocket"})
|
||||
await websocket.close()
|
||||
|
||||
|
||||
def test_read_main():
|
||||
client = TestClient(app)
|
||||
response = client.get("/")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {"msg": "Hello World"}
|
||||
|
||||
|
||||
def test_websocket():
|
||||
client = TestClient(app)
|
||||
with client.websocket_connect("/ws") as websocket:
|
||||
data = websocket.receive_json()
|
||||
assert data == {"msg": "Hello WebSocket"}
|
||||
24
docs/src/app_testing/tutorial003.py
Normal file
@@ -0,0 +1,24 @@
|
||||
from fastapi import FastAPI
|
||||
from starlette.testclient import TestClient
|
||||
|
||||
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_items(item_id: str):
|
||||
return items[item_id]
|
||||
|
||||
|
||||
def test_read_items():
|
||||
with TestClient(app) as client:
|
||||
response = client.get("/items/foo")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {"name": "Fighters"}
|
||||
@@ -1,8 +1,20 @@
|
||||
from fastapi import FastAPI
|
||||
from fastapi import Depends, FastAPI, Header, HTTPException
|
||||
|
||||
from .routers import items, users
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
async def get_token_header(x_token: str = Header(...)):
|
||||
if x_token != "fake-super-secret-token":
|
||||
raise HTTPException(status_code=400, detail="X-Token header invalid")
|
||||
|
||||
|
||||
app.include_router(users.router)
|
||||
app.include_router(items.router, prefix="/items", tags=["items"])
|
||||
app.include_router(
|
||||
items.router,
|
||||
prefix="/items",
|
||||
tags=["items"],
|
||||
dependencies=[Depends(get_token_header)],
|
||||
responses={404: {"description": "Not found"}},
|
||||
)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from fastapi import APIRouter
|
||||
from fastapi import APIRouter, HTTPException
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@@ -11,3 +11,14 @@ async def read_items():
|
||||
@router.get("/{item_id}")
|
||||
async def read_item(item_id: str):
|
||||
return {"name": "Fake Specific Item", "item_id": item_id}
|
||||
|
||||
|
||||
@router.put(
|
||||
"/{item_id}",
|
||||
tags=["custom"],
|
||||
responses={403: {"description": "Operation forbidden"}},
|
||||
)
|
||||
async def update_item(item_id: str):
|
||||
if item_id != "foo":
|
||||
raise HTTPException(status_code=403, detail="You can only update the item: foo")
|
||||
return {"item_id": item_id, "name": "The Fighters"}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
from typing import Set
|
||||
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
from pydantic.types import UrlStr
|
||||
from pydantic import BaseModel, UrlStr
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
from typing import List, Set
|
||||
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
from pydantic.types import UrlStr
|
||||
from pydantic import BaseModel, UrlStr
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
@@ -18,7 +17,7 @@ class Item(BaseModel):
|
||||
price: float
|
||||
tax: float = None
|
||||
tags: Set[str] = []
|
||||
image: List[Image] = None
|
||||
images: List[Image] = None
|
||||
|
||||
|
||||
@app.put("/items/{item_id}")
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
from typing import List, Set
|
||||
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
from pydantic.types import UrlStr
|
||||
from pydantic import BaseModel, UrlStr
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
@@ -18,7 +17,7 @@ class Item(BaseModel):
|
||||
price: float
|
||||
tax: float = None
|
||||
tags: Set[str] = []
|
||||
image: List[Image] = None
|
||||
images: List[Image] = None
|
||||
|
||||
|
||||
class Offer(BaseModel):
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
from typing import List
|
||||
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
from pydantic.types import UrlStr
|
||||
from pydantic import BaseModel, UrlStr
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
10
docs/src/body_nested_models/tutorial009.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from typing import Dict
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.post("/index-weights/")
|
||||
async def create_index_weights(weights: Dict[int, float]):
|
||||
return weights
|
||||
34
docs/src/body_updates/tutorial001.py
Normal file
@@ -0,0 +1,34 @@
|
||||
from typing import List
|
||||
|
||||
from fastapi import FastAPI
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: str = None
|
||||
description: str = None
|
||||
price: float = None
|
||||
tax: float = 10.5
|
||||
tags: List[str] = []
|
||||
|
||||
|
||||
items = {
|
||||
"foo": {"name": "Foo", "price": 50.2},
|
||||
"bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
|
||||
"baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
|
||||
}
|
||||
|
||||
|
||||
@app.get("/items/{item_id}", response_model=Item)
|
||||
async def read_item(item_id: str):
|
||||
return items[item_id]
|
||||
|
||||
|
||||
@app.put("/items/{item_id}", response_model=Item)
|
||||
async def update_item(item_id: str, item: Item):
|
||||
update_item_encoded = jsonable_encoder(item)
|
||||
items[item_id] = update_item_encoded
|
||||
return update_item_encoded
|
||||
37
docs/src/body_updates/tutorial002.py
Normal file
@@ -0,0 +1,37 @@
|
||||
from typing import List
|
||||
|
||||
from fastapi import FastAPI
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: str = None
|
||||
description: str = None
|
||||
price: float = None
|
||||
tax: float = 10.5
|
||||
tags: List[str] = []
|
||||
|
||||
|
||||
items = {
|
||||
"foo": {"name": "Foo", "price": 50.2},
|
||||
"bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
|
||||
"baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
|
||||
}
|
||||
|
||||
|
||||
@app.get("/items/{item_id}", response_model=Item)
|
||||
async def read_item(item_id: str):
|
||||
return items[item_id]
|
||||
|
||||
|
||||
@app.patch("/items/{item_id}", response_model=Item)
|
||||
async def update_item(item_id: str, item: Item):
|
||||
stored_item_data = items[item_id]
|
||||
stored_item_model = Item(**stored_item_data)
|
||||
update_data = item.dict(skip_defaults=True)
|
||||
updated_item = stored_item_model.copy(update=update_data)
|
||||
items[item_id] = jsonable_encoder(updated_item)
|
||||
return updated_item
|
||||
19
docs/src/cors/tutorial001.py
Normal file
@@ -0,0 +1,19 @@
|
||||
from fastapi import FastAPI
|
||||
from starlette.middleware.cors import CORSMiddleware
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
origins = [
|
||||
"http://localhost.tiangolo.com",
|
||||
"https://localhost.tiangolo.com",
|
||||
"http:localhost",
|
||||
"http:localhost:8080",
|
||||
]
|
||||
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=origins,
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
@@ -4,6 +4,6 @@ from starlette.responses import UJSONResponse
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/items/", content_type=UJSONResponse)
|
||||
@app.get("/items/", response_class=UJSONResponse)
|
||||
async def read_items():
|
||||
return [{"item_id": "Foo"}]
|
||||
|
||||
@@ -4,7 +4,7 @@ from starlette.responses import HTMLResponse
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/items/", content_type=HTMLResponse)
|
||||
@app.get("/items/", response_class=HTMLResponse)
|
||||
async def read_items():
|
||||
return """
|
||||
<html>
|
||||
|
||||
@@ -18,6 +18,6 @@ def generate_html_response():
|
||||
return HTMLResponse(content=html_content, status_code=200)
|
||||
|
||||
|
||||
@app.get("/items/", content_type=HTMLResponse)
|
||||
@app.get("/items/", response_class=HTMLResponse)
|
||||
async def read_items():
|
||||
return generate_html_response()
|
||||
|
||||
@@ -1,21 +1,19 @@
|
||||
from fastapi import Depends, FastAPI
|
||||
from fastapi import Depends, FastAPI, Header, HTTPException
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class FixedContentQueryChecker:
|
||||
def __init__(self, fixed_content: str):
|
||||
self.fixed_content = fixed_content
|
||||
|
||||
def __call__(self, q: str = ""):
|
||||
if q:
|
||||
return self.fixed_content in q
|
||||
return False
|
||||
async def verify_token(x_token: str = Header(...)):
|
||||
if x_token != "fake-super-secret-token":
|
||||
raise HTTPException(status_code=400, detail="X-Token header invalid")
|
||||
|
||||
|
||||
checker = FixedContentQueryChecker("bar")
|
||||
async def verify_key(x_key: str = Header(...)):
|
||||
if x_key != "fake-super-secret-key":
|
||||
raise HTTPException(status_code=400, detail="X-Key header invalid")
|
||||
return x_key
|
||||
|
||||
|
||||
@app.get("/query-checker/")
|
||||
async def read_query_check(fixed_content_included: bool = Depends(checker)):
|
||||
return {"fixed_content_in_query": fixed_content_included}
|
||||
@app.get("/items/", dependencies=[Depends(verify_token), Depends(verify_key)])
|
||||
async def read_items():
|
||||
return [{"item": "Foo"}, {"item": "Bar"}]
|
||||
|
||||
21
docs/src/dependencies/tutorial007.py
Normal file
@@ -0,0 +1,21 @@
|
||||
from fastapi import Depends, FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class FixedContentQueryChecker:
|
||||
def __init__(self, fixed_content: str):
|
||||
self.fixed_content = fixed_content
|
||||
|
||||
def __call__(self, q: str = ""):
|
||||
if q:
|
||||
return self.fixed_content in q
|
||||
return False
|
||||
|
||||
|
||||
checker = FixedContentQueryChecker("bar")
|
||||
|
||||
|
||||
@app.get("/query-checker/")
|
||||
async def read_query_check(fixed_content_included: bool = Depends(checker)):
|
||||
return {"fixed_content_in_query": fixed_content_included}
|
||||
55
docs/src/dependency_testing/tutorial001.py
Normal file
@@ -0,0 +1,55 @@
|
||||
from fastapi import Depends, FastAPI
|
||||
from starlette.testclient import TestClient
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
async def common_parameters(q: str = None, skip: int = 0, limit: int = 100):
|
||||
return {"q": q, "skip": skip, "limit": limit}
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(commons: dict = Depends(common_parameters)):
|
||||
return {"message": "Hello Items!", "params": commons}
|
||||
|
||||
|
||||
@app.get("/users/")
|
||||
async def read_users(commons: dict = Depends(common_parameters)):
|
||||
return {"message": "Hello Users!", "params": commons}
|
||||
|
||||
|
||||
client = TestClient(app)
|
||||
|
||||
|
||||
async def override_dependency(q: str = None):
|
||||
return {"q": q, "skip": 5, "limit": 10}
|
||||
|
||||
|
||||
app.dependency_overrides[common_parameters] = override_dependency
|
||||
|
||||
|
||||
def test_override_in_items():
|
||||
response = client.get("/items/")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {
|
||||
"message": "Hello Items!",
|
||||
"params": {"q": None, "skip": 5, "limit": 10},
|
||||
}
|
||||
|
||||
|
||||
def test_override_in_items_with_q():
|
||||
response = client.get("/items/?q=foo")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {
|
||||
"message": "Hello Items!",
|
||||
"params": {"q": "foo", "skip": 5, "limit": 10},
|
||||
}
|
||||
|
||||
|
||||
def test_override_in_items_with_params():
|
||||
response = client.get("/items/?q=foo&skip=100&limit=200")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {
|
||||
"message": "Hello Items!",
|
||||
"params": {"q": "foo", "skip": 5, "limit": 10},
|
||||
}
|
||||
22
docs/src/encoder/tutorial001.py
Normal file
@@ -0,0 +1,22 @@
|
||||
from datetime import datetime
|
||||
|
||||
from fastapi import FastAPI
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
from pydantic import BaseModel
|
||||
|
||||
fake_db = {}
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
title: str
|
||||
timestamp: datetime
|
||||
description: str = None
|
||||
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.put("/items/{id}")
|
||||
def update_item(id: str, item: Item):
|
||||
json_compatible_item_data = jsonable_encoder(item)
|
||||
fake_db[id] = json_compatible_item_data
|
||||
@@ -12,5 +12,5 @@ async def startup_event():
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
async def read_item(item_id: str):
|
||||
async def read_items(item_id: str):
|
||||
return items[item_id]
|
||||
|
||||
10
docs/src/extra_models/tutorial005.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from typing import Dict
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/keyword-weights/", response_model=Dict[str, float])
|
||||
async def read_keyword_weights():
|
||||
return {"foo": 2.3, "bar": 3.4}
|
||||
@@ -6,7 +6,7 @@ items = {"foo": "The Foo Wrestlers"}
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
async def create_item(item_id: str):
|
||||
async def read_item(item_id: str):
|
||||
if item_id not in items:
|
||||
raise HTTPException(status_code=404, detail="Item not found")
|
||||
return {"item": items[item_id]}
|
||||
|
||||
@@ -6,7 +6,7 @@ items = {"foo": "The Foo Wrestlers"}
|
||||
|
||||
|
||||
@app.get("/items-header/{item_id}")
|
||||
async def create_item_header(item_id: str):
|
||||
async def read_item_header(item_id: str):
|
||||
if item_id not in items:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
|
||||
@@ -1,15 +1,26 @@
|
||||
from fastapi import FastAPI
|
||||
from starlette.exceptions import HTTPException
|
||||
from starlette.responses import PlainTextResponse
|
||||
from starlette.requests import Request
|
||||
from starlette.responses import JSONResponse
|
||||
|
||||
|
||||
class UnicornException(Exception):
|
||||
def __init__(self, name: str):
|
||||
self.name = name
|
||||
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.exception_handler(HTTPException)
|
||||
async def http_exception(request, exc):
|
||||
return PlainTextResponse(str(exc.detail), status_code=exc.status_code)
|
||||
@app.exception_handler(UnicornException)
|
||||
async def unicorn_exception_handler(request: Request, exc: UnicornException):
|
||||
return JSONResponse(
|
||||
status_code=418,
|
||||
content={"message": f"Oops! {exc.name} did something. There goes a rainbow..."},
|
||||
)
|
||||
|
||||
|
||||
@app.get("/")
|
||||
async def root():
|
||||
return {"message": "Hello World"}
|
||||
@app.get("/unicorns/{name}")
|
||||
async def read_unicorn(name: str):
|
||||
if name == "yolo":
|
||||
raise UnicornException(name=name)
|
||||
return {"unicorn_name": name}
|
||||
|
||||
23
docs/src/handling_errors/tutorial004.py
Normal file
@@ -0,0 +1,23 @@
|
||||
from fastapi import FastAPI, HTTPException
|
||||
from fastapi.exceptions import RequestValidationError
|
||||
from starlette.exceptions import HTTPException as StarletteHTTPException
|
||||
from starlette.responses import PlainTextResponse
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.exception_handler(StarletteHTTPException)
|
||||
async def http_exception_handler(request, exc):
|
||||
return PlainTextResponse(str(exc.detail), status_code=exc.status_code)
|
||||
|
||||
|
||||
@app.exception_handler(RequestValidationError)
|
||||
async def validation_exception_handler(request, exc):
|
||||
return PlainTextResponse(str(exc), status_code=400)
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
async def read_item(item_id: int):
|
||||
if item_id == 3:
|
||||
raise HTTPException(status_code=418, detail="Nope! I don't like 3.")
|
||||
return {"item_id": item_id}
|
||||
28
docs/src/handling_errors/tutorial005.py
Normal file
@@ -0,0 +1,28 @@
|
||||
from fastapi import FastAPI, HTTPException
|
||||
from fastapi.exception_handlers import (
|
||||
http_exception_handler,
|
||||
request_validation_exception_handler,
|
||||
)
|
||||
from fastapi.exceptions import RequestValidationError
|
||||
from starlette.exceptions import HTTPException as StarletteHTTPException
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.exception_handler(StarletteHTTPException)
|
||||
async def custom_http_exception_handler(request, exc):
|
||||
print(f"OMG! An HTTP error!: {exc}")
|
||||
return await http_exception_handler(request, exc)
|
||||
|
||||
|
||||
@app.exception_handler(RequestValidationError)
|
||||
async def validation_exception_handler(request, exc):
|
||||
print(f"OMG! The client sent invalid data!: {exc}")
|
||||
return await request_validation_exception_handler(request, exc)
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
async def read_item(item_id: int):
|
||||
if item_id == 3:
|
||||
raise HTTPException(status_code=418, detail="Nope! I don't like 3.")
|
||||
return {"item_id": item_id}
|
||||
15
docs/src/middleware/tutorial001.py
Normal file
@@ -0,0 +1,15 @@
|
||||
import time
|
||||
|
||||
from fastapi import FastAPI
|
||||
from starlette.requests import Request
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.middleware("http")
|
||||
async def add_process_time_header(request: Request, call_next):
|
||||
start_time = time.time()
|
||||
response = await call_next(request)
|
||||
process_time = time.time() - start_time
|
||||
response.headers["X-Process-Time"] = str(process_time)
|
||||
return response
|
||||
@@ -18,11 +18,11 @@ class Item(BaseModel):
|
||||
async def create_item(*, item: Item):
|
||||
"""
|
||||
Create an item with all the information:
|
||||
|
||||
* name: each item must have a name
|
||||
* description: a long description
|
||||
* price: required
|
||||
* tax: if the item doesn't have tax, you can omit this
|
||||
* tags: a set of unique tag strings for this item
|
||||
|
||||
- **name**: each item must have a name
|
||||
- **description**: a long description
|
||||
- **price**: required
|
||||
- **tax**: if the item doesn't have tax, you can omit this
|
||||
- **tags**: a set of unique tag strings for this item
|
||||
"""
|
||||
return item
|
||||
|
||||
@@ -23,11 +23,11 @@ class Item(BaseModel):
|
||||
async def create_item(*, item: Item):
|
||||
"""
|
||||
Create an item with all the information:
|
||||
|
||||
* name: each item must have a name
|
||||
* description: a long description
|
||||
* price: required
|
||||
* tax: if the item doesn't have tax, you can omit this
|
||||
* tags: a set of unique tag strings for this item
|
||||
|
||||
- **name**: each item must have a name
|
||||
- **description**: a long description
|
||||
- **price**: required
|
||||
- **tax**: if the item doesn't have tax, you can omit this
|
||||
- **tags**: a set of unique tag strings for this item
|
||||
"""
|
||||
return item
|
||||
|
||||
8
docs/src/path_params/tutorial004.py
Normal file
@@ -0,0 +1,8 @@
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/files/{file_path:path}")
|
||||
async def read_user_me(file_path: str):
|
||||
return {"file_path": file_path}
|
||||
21
docs/src/path_params/tutorial005.py
Normal file
@@ -0,0 +1,21 @@
|
||||
from enum import Enum
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
|
||||
class ModelName(str, Enum):
|
||||
alexnet = "alexnet"
|
||||
resnet = "resnet"
|
||||
lenet = "lenet"
|
||||
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/model/{model_name}")
|
||||
async def get_model(model_name: ModelName):
|
||||
if model_name == ModelName.alexnet:
|
||||
return {"model_name": model_name, "message": "Deep Learning FTW!"}
|
||||
if model_name.value == "lenet":
|
||||
return {"model_name": model_name, "message": "LeCNN all the images"}
|
||||
return {"model_name": model_name, "message": "Have some residuals"}
|
||||
@@ -6,5 +6,5 @@ fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_item(skip: int = 0, limit: int = 100):
|
||||
async def read_item(skip: int = 0, limit: int = 10):
|
||||
return fake_items_db[skip : skip + limit]
|
||||
|
||||
11
docs/src/query_params/tutorial007.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
async def read_user_item(item_id: str, limit: Optional[int] = None):
|
||||
item = {"item_id": item_id, "limit": limit}
|
||||
return item
|
||||
11
docs/src/query_params_str_validations/tutorial012.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(["foo", "bar"])):
|
||||
query_items = {"q": q}
|
||||
return query_items
|
||||
9
docs/src/query_params_str_validations/tutorial013.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from fastapi import FastAPI, Query
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(q: list = Query(None)):
|
||||
query_items = {"q": q}
|
||||
return query_items
|
||||
33
docs/src/request_files/tutorial002.py
Normal file
@@ -0,0 +1,33 @@
|
||||
from typing import List
|
||||
|
||||
from fastapi import FastAPI, File, UploadFile
|
||||
from starlette.responses import HTMLResponse
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.post("/files/")
|
||||
async def create_files(files: List[bytes] = File(...)):
|
||||
return {"file_sizes": [len(file) for file in files]}
|
||||
|
||||
|
||||
@app.post("/uploadfiles/")
|
||||
async def create_upload_files(files: List[UploadFile] = File(...)):
|
||||
return {"filenames": [file.filename for file in files]}
|
||||
|
||||
|
||||
@app.get("/")
|
||||
async def main():
|
||||
content = """
|
||||
<body>
|
||||
<form action="/files/" enctype="multipart/form-data" method="post">
|
||||
<input name="files" type="file" multiple>
|
||||
<input type="submit">
|
||||
</form>
|
||||
<form action="/uploadfiles/" enctype="multipart/form-data" method="post">
|
||||
<input name="files" type="file" multiple>
|
||||
<input type="submit">
|
||||
</form>
|
||||
</body>
|
||||
"""
|
||||
return HTMLResponse(content=content)
|
||||
15
docs/src/response_change_status_code/tutorial001.py
Normal file
@@ -0,0 +1,15 @@
|
||||
from fastapi import FastAPI
|
||||
from starlette.responses import Response
|
||||
from starlette.status import HTTP_201_CREATED
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
tasks = {"foo": "Listen to the Bar Fighters"}
|
||||
|
||||
|
||||
@app.put("/get-or-create-task/{task_id}", status_code=200)
|
||||
def get_or_create_task(task_id: str, response: Response):
|
||||
if task_id not in tasks:
|
||||
tasks[task_id] = "This didn't exist before"
|
||||
response.status_code = HTTP_201_CREATED
|
||||
return tasks[task_id]
|
||||
12
docs/src/response_cookies/tutorial001.py
Normal file
@@ -0,0 +1,12 @@
|
||||
from fastapi import FastAPI
|
||||
from starlette.responses import JSONResponse
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.post("/cookie/")
|
||||
def create_cookie():
|
||||
content = {"message": "Come to the dark side, we have cookies"}
|
||||
response = JSONResponse(content=content)
|
||||
response.set_cookie(key="fakesession", value="fake-cookie-session-value")
|
||||
return response
|
||||
10
docs/src/response_cookies/tutorial002.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from fastapi import FastAPI
|
||||
from starlette.responses import Response
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.post("/cookie-and-object/")
|
||||
def create_cookie(response: Response):
|
||||
response.set_cookie(key="fakesession", value="fake-cookie-session-value")
|
||||
return {"message": "Come to the dark side, we have cookies"}
|
||||
21
docs/src/response_directly/tutorial001.py
Normal file
@@ -0,0 +1,21 @@
|
||||
from datetime import datetime
|
||||
|
||||
from fastapi import FastAPI
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
from pydantic import BaseModel
|
||||
from starlette.responses import JSONResponse
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
title: str
|
||||
timestamp: datetime
|
||||
description: str = None
|
||||
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.put("/items/{id}")
|
||||
def update_item(id: str, item: Item):
|
||||
json_compatible_item_data = jsonable_encoder(item)
|
||||
return JSONResponse(content=json_compatible_item_data)
|
||||
20
docs/src/response_directly/tutorial002.py
Normal file
@@ -0,0 +1,20 @@
|
||||
from fastapi import FastAPI
|
||||
from starlette.responses import Response
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/legacy/")
|
||||
def get_legacy_data():
|
||||
data = """
|
||||
<?xml version="1.0"?>
|
||||
<shampoo>
|
||||
<Header>
|
||||
Apply shampoo here.
|
||||
<Header>
|
||||
<Body>
|
||||
You'll have to use soap here.
|
||||
</Body>
|
||||
</shampoo>
|
||||
"""
|
||||
return Response(content=data, media_type="application/xml")
|
||||
11
docs/src/response_headers/tutorial001.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from fastapi import FastAPI
|
||||
from starlette.responses import JSONResponse
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/headers/")
|
||||
def get_headers():
|
||||
content = {"message": "Hello World"}
|
||||
headers = {"X-Cat-Dog": "alone in the world", "Content-Language": "en-US"}
|
||||
return JSONResponse(content=content, headers=headers)
|
||||
10
docs/src/response_headers/tutorial002.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from fastapi import FastAPI
|
||||
from starlette.responses import Response
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/headers-and-object/")
|
||||
def get_headers(response: Response):
|
||||
response.headers["X-Cat-Dog"] = "alone in the world"
|
||||
return {"message": "Hello World"}
|
||||
@@ -1,4 +1,4 @@
|
||||
from typing import Set
|
||||
from typing import List
|
||||
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
@@ -11,9 +11,9 @@ class Item(BaseModel):
|
||||
description: str = None
|
||||
price: float
|
||||
tax: float = None
|
||||
tags: Set[str] = []
|
||||
tags: List[str] = []
|
||||
|
||||
|
||||
@app.post("/items/", response_model=Item)
|
||||
async def create_item(*, item: Item):
|
||||
async def create_item(item: Item):
|
||||
return item
|
||||
|
||||
26
docs/src/response_model/tutorial004.py
Normal file
@@ -0,0 +1,26 @@
|
||||
from typing import List
|
||||
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
description: str = None
|
||||
price: float
|
||||
tax: float = 10.5
|
||||
tags: List[str] = []
|
||||
|
||||
|
||||
items = {
|
||||
"foo": {"name": "Foo", "price": 50.2},
|
||||
"bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
|
||||
"baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
|
||||
}
|
||||
|
||||
|
||||
@app.get("/items/{item_id}", response_model=Item, response_model_skip_defaults=True)
|
||||
async def read_item(item_id: str):
|
||||
return items[item_id]
|
||||
37
docs/src/response_model/tutorial005.py
Normal file
@@ -0,0 +1,37 @@
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
description: str = None
|
||||
price: float
|
||||
tax: float = 10.5
|
||||
|
||||
|
||||
items = {
|
||||
"foo": {"name": "Foo", "price": 50.2},
|
||||
"bar": {"name": "Bar", "description": "The Bar fighters", "price": 62, "tax": 20.2},
|
||||
"baz": {
|
||||
"name": "Baz",
|
||||
"description": "There goes my baz",
|
||||
"price": 50.2,
|
||||
"tax": 10.5,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@app.get(
|
||||
"/items/{item_id}/name",
|
||||
response_model=Item,
|
||||
response_model_include={"name", "description"},
|
||||
)
|
||||
async def read_item_name(item_id: str):
|
||||
return items[item_id]
|
||||
|
||||
|
||||
@app.get("/items/{item_id}/public", response_model=Item, response_model_exclude={"tax"})
|
||||
async def read_item_public_data(item_id: str):
|
||||
return items[item_id]
|
||||
37
docs/src/response_model/tutorial006.py
Normal file
@@ -0,0 +1,37 @@
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
description: str = None
|
||||
price: float
|
||||
tax: float = 10.5
|
||||
|
||||
|
||||
items = {
|
||||
"foo": {"name": "Foo", "price": 50.2},
|
||||
"bar": {"name": "Bar", "description": "The Bar fighters", "price": 62, "tax": 20.2},
|
||||
"baz": {
|
||||
"name": "Baz",
|
||||
"description": "There goes my baz",
|
||||
"price": 50.2,
|
||||
"tax": 10.5,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@app.get(
|
||||
"/items/{item_id}/name",
|
||||
response_model=Item,
|
||||
response_model_include=["name", "description"],
|
||||
)
|
||||
async def read_item_name(item_id: str):
|
||||
return items[item_id]
|
||||
|
||||
|
||||
@app.get("/items/{item_id}/public", response_model=Item, response_model_exclude=["tax"])
|
||||
async def read_item_public_data(item_id: str):
|
||||
return items[item_id]
|
||||
@@ -1,4 +1,4 @@
|
||||
from fastapi import FastAPI, Security
|
||||
from fastapi import Depends, FastAPI
|
||||
from fastapi.security import OAuth2PasswordBearer
|
||||
|
||||
app = FastAPI()
|
||||
@@ -7,5 +7,5 @@ oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/token")
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(token: str = Security(oauth2_scheme)):
|
||||
async def read_items(token: str = Depends(oauth2_scheme)):
|
||||
return {"token": token}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import Depends, FastAPI, Security
|
||||
from fastapi import Depends, FastAPI
|
||||
from fastapi.security import OAuth2PasswordBearer
|
||||
from pydantic import BaseModel
|
||||
|
||||
@@ -22,7 +22,7 @@ def fake_decode_token(token):
|
||||
)
|
||||
|
||||
|
||||
async def get_current_user(token: str = Security(oauth2_scheme)):
|
||||
async def get_current_user(token: str = Depends(oauth2_scheme)):
|
||||
user = fake_decode_token(token)
|
||||
return user
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from fastapi import Depends, FastAPI, HTTPException, Security
|
||||
from fastapi import Depends, FastAPI, HTTPException
|
||||
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
|
||||
from pydantic import BaseModel
|
||||
from starlette.status import HTTP_401_UNAUTHORIZED
|
||||
|
||||
fake_users_db = {
|
||||
"johndoe": {
|
||||
@@ -53,11 +54,13 @@ def fake_decode_token(token):
|
||||
return user
|
||||
|
||||
|
||||
async def get_current_user(token: str = Security(oauth2_scheme)):
|
||||
async def get_current_user(token: str = Depends(oauth2_scheme)):
|
||||
user = fake_decode_token(token)
|
||||
if not user:
|
||||
raise HTTPException(
|
||||
status_code=400, detail="Invalid authentication credentials"
|
||||
status_code=HTTP_401_UNAUTHORIZED,
|
||||
detail="Invalid authentication credentials",
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
return user
|
||||
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
import jwt
|
||||
from fastapi import Depends, FastAPI, HTTPException, Security
|
||||
from fastapi import Depends, FastAPI, HTTPException
|
||||
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
|
||||
from jwt import PyJWTError
|
||||
from passlib.context import CryptContext
|
||||
from pydantic import BaseModel
|
||||
from starlette.status import HTTP_403_FORBIDDEN
|
||||
from starlette.status import HTTP_401_UNAUTHORIZED
|
||||
|
||||
# to get a string like this run:
|
||||
# openssl rand -hex 32
|
||||
SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7"
|
||||
ALGORITHM = "HS256"
|
||||
TOKEN_SUBJECT = "access"
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES = 30
|
||||
|
||||
|
||||
@@ -32,7 +31,7 @@ class Token(BaseModel):
|
||||
token_type: str
|
||||
|
||||
|
||||
class TokenPayload(BaseModel):
|
||||
class TokenData(BaseModel):
|
||||
username: str = None
|
||||
|
||||
|
||||
@@ -83,20 +82,28 @@ def create_access_token(*, data: dict, expires_delta: timedelta = None):
|
||||
expire = datetime.utcnow() + expires_delta
|
||||
else:
|
||||
expire = datetime.utcnow() + timedelta(minutes=15)
|
||||
to_encode.update({"exp": expire, "sub": TOKEN_SUBJECT})
|
||||
to_encode.update({"exp": expire})
|
||||
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
|
||||
return encoded_jwt
|
||||
|
||||
|
||||
async def get_current_user(token: str = Security(oauth2_scheme)):
|
||||
async def get_current_user(token: str = Depends(oauth2_scheme)):
|
||||
credentials_exception = HTTPException(
|
||||
status_code=HTTP_401_UNAUTHORIZED,
|
||||
detail="Could not validate credentials",
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
try:
|
||||
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
|
||||
token_data = TokenPayload(**payload)
|
||||
username: str = payload.get("sub")
|
||||
if username is None:
|
||||
raise credentials_exception
|
||||
token_data = TokenData(username=username)
|
||||
except PyJWTError:
|
||||
raise HTTPException(
|
||||
status_code=HTTP_403_FORBIDDEN, detail="Could not validate credentials"
|
||||
)
|
||||
raise credentials_exception
|
||||
user = get_user(fake_users_db, username=token_data.username)
|
||||
if user is None:
|
||||
raise credentials_exception
|
||||
return user
|
||||
|
||||
|
||||
@@ -107,18 +114,22 @@ async def get_current_active_user(current_user: User = Depends(get_current_user)
|
||||
|
||||
|
||||
@app.post("/token", response_model=Token)
|
||||
async def route_login_access_token(form_data: OAuth2PasswordRequestForm = Depends()):
|
||||
async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends()):
|
||||
user = authenticate_user(fake_users_db, form_data.username, form_data.password)
|
||||
if not user:
|
||||
raise HTTPException(status_code=400, detail="Incorrect email or password")
|
||||
raise HTTPException(
|
||||
status_code=HTTP_401_UNAUTHORIZED,
|
||||
detail="Incorrect username or password",
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
|
||||
access_token = create_access_token(
|
||||
data={"username": form_data.username}, expires_delta=access_token_expires
|
||||
data={"sub": user.username}, expires_delta=access_token_expires
|
||||
)
|
||||
return {"access_token": access_token, "token_type": "bearer"}
|
||||
|
||||
|
||||
@app.get("/users/me", response_model=User)
|
||||
@app.get("/users/me/", response_model=User)
|
||||
async def read_users_me(current_user: User = Depends(get_current_active_user)):
|
||||
return current_user
|
||||
|
||||
|
||||
175
docs/src/security/tutorial005.py
Normal file
@@ -0,0 +1,175 @@
|
||||
from datetime import datetime, timedelta
|
||||
from typing import List
|
||||
|
||||
import jwt
|
||||
from fastapi import Depends, FastAPI, HTTPException, Security
|
||||
from fastapi.security import (
|
||||
OAuth2PasswordBearer,
|
||||
OAuth2PasswordRequestForm,
|
||||
SecurityScopes,
|
||||
)
|
||||
from jwt import PyJWTError
|
||||
from passlib.context import CryptContext
|
||||
from pydantic import BaseModel, ValidationError
|
||||
from starlette.status import HTTP_401_UNAUTHORIZED
|
||||
|
||||
# to get a string like this run:
|
||||
# openssl rand -hex 32
|
||||
SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7"
|
||||
ALGORITHM = "HS256"
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES = 30
|
||||
|
||||
|
||||
fake_users_db = {
|
||||
"johndoe": {
|
||||
"username": "johndoe",
|
||||
"full_name": "John Doe",
|
||||
"email": "johndoe@example.com",
|
||||
"hashed_password": "$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW",
|
||||
"disabled": False,
|
||||
},
|
||||
"alice": {
|
||||
"username": "alice",
|
||||
"full_name": "Alice Chains",
|
||||
"email": "alicechains@example.com",
|
||||
"hashed_password": "$2b$12$gSvqqUPvlXP2tfVFaWK1Be7DlH.PKZbv5H8KnzzVgXXbVxpva.pFm",
|
||||
"disabled": True,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class Token(BaseModel):
|
||||
access_token: str
|
||||
token_type: str
|
||||
|
||||
|
||||
class TokenData(BaseModel):
|
||||
username: str = None
|
||||
scopes: List[str] = []
|
||||
|
||||
|
||||
class User(BaseModel):
|
||||
username: str
|
||||
email: str = None
|
||||
full_name: str = None
|
||||
disabled: bool = None
|
||||
|
||||
|
||||
class UserInDB(User):
|
||||
hashed_password: str
|
||||
|
||||
|
||||
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
|
||||
|
||||
oauth2_scheme = OAuth2PasswordBearer(
|
||||
tokenUrl="/token",
|
||||
scopes={"me": "Read information about the current user.", "items": "Read items."},
|
||||
)
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
def verify_password(plain_password, hashed_password):
|
||||
return pwd_context.verify(plain_password, hashed_password)
|
||||
|
||||
|
||||
def get_password_hash(password):
|
||||
return pwd_context.hash(password)
|
||||
|
||||
|
||||
def get_user(db, username: str):
|
||||
if username in db:
|
||||
user_dict = db[username]
|
||||
return UserInDB(**user_dict)
|
||||
|
||||
|
||||
def authenticate_user(fake_db, username: str, password: str):
|
||||
user = get_user(fake_db, username)
|
||||
if not user:
|
||||
return False
|
||||
if not verify_password(password, user.hashed_password):
|
||||
return False
|
||||
return user
|
||||
|
||||
|
||||
def create_access_token(*, data: dict, expires_delta: timedelta = None):
|
||||
to_encode = data.copy()
|
||||
if expires_delta:
|
||||
expire = datetime.utcnow() + expires_delta
|
||||
else:
|
||||
expire = datetime.utcnow() + timedelta(minutes=15)
|
||||
to_encode.update({"exp": expire})
|
||||
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
|
||||
return encoded_jwt
|
||||
|
||||
|
||||
async def get_current_user(
|
||||
security_scopes: SecurityScopes, token: str = Depends(oauth2_scheme)
|
||||
):
|
||||
if security_scopes.scopes:
|
||||
authenticate_value = f'Bearer scope="{security_scopes.scope_str}"'
|
||||
else:
|
||||
authenticate_value = f"Bearer"
|
||||
credentials_exception = HTTPException(
|
||||
status_code=HTTP_401_UNAUTHORIZED,
|
||||
detail="Could not validate credentials",
|
||||
headers={"WWW-Authenticate": authenticate_value},
|
||||
)
|
||||
try:
|
||||
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
|
||||
username: str = payload.get("sub")
|
||||
if username is None:
|
||||
raise credentials_exception
|
||||
token_scopes = payload.get("scopes", [])
|
||||
token_data = TokenData(scopes=token_scopes, username=username)
|
||||
except (PyJWTError, ValidationError):
|
||||
raise credentials_exception
|
||||
user = get_user(fake_users_db, username=token_data.username)
|
||||
if user is None:
|
||||
raise credentials_exception
|
||||
for scope in security_scopes.scopes:
|
||||
if scope not in token_data.scopes:
|
||||
raise HTTPException(
|
||||
status_code=HTTP_401_UNAUTHORIZED,
|
||||
detail="Not enough permissions",
|
||||
headers={"WWW-Authenticate": authenticate_value},
|
||||
)
|
||||
return user
|
||||
|
||||
|
||||
async def get_current_active_user(
|
||||
current_user: User = Security(get_current_user, scopes=["me"])
|
||||
):
|
||||
if current_user.disabled:
|
||||
raise HTTPException(status_code=400, detail="Inactive user")
|
||||
return current_user
|
||||
|
||||
|
||||
@app.post("/token", response_model=Token)
|
||||
async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends()):
|
||||
user = authenticate_user(fake_users_db, form_data.username, form_data.password)
|
||||
if not user:
|
||||
raise HTTPException(status_code=400, detail="Incorrect username or password")
|
||||
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
|
||||
access_token = create_access_token(
|
||||
data={"sub": user.username, "scopes": form_data.scopes},
|
||||
expires_delta=access_token_expires,
|
||||
)
|
||||
return {"access_token": access_token, "token_type": "bearer"}
|
||||
|
||||
|
||||
@app.get("/users/me/", response_model=User)
|
||||
async def read_users_me(current_user: User = Depends(get_current_active_user)):
|
||||
return current_user
|
||||
|
||||
|
||||
@app.get("/users/me/items/")
|
||||
async def read_own_items(
|
||||
current_user: User = Security(get_current_active_user, scopes=["items"])
|
||||
):
|
||||
return [{"item_id": "Foo", "owner": current_user.username}]
|
||||
|
||||
|
||||
@app.get("/status/")
|
||||
async def read_system_status(current_user: User = Depends(get_current_user)):
|
||||
return {"status": "ok"}
|
||||
11
docs/src/security/tutorial006.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from fastapi import Depends, FastAPI
|
||||
from fastapi.security import HTTPBasic, HTTPBasicCredentials
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
security = HTTPBasic()
|
||||
|
||||
|
||||
@app.get("/users/me")
|
||||
def read_current_user(credentials: HTTPBasicCredentials = Depends(security)):
|
||||
return {"username": credentials.username, "password": credentials.password}
|
||||
22
docs/src/security/tutorial007.py
Normal file
@@ -0,0 +1,22 @@
|
||||
from fastapi import Depends, FastAPI, HTTPException
|
||||
from fastapi.security import HTTPBasic, HTTPBasicCredentials
|
||||
from starlette.status import HTTP_401_UNAUTHORIZED
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
security = HTTPBasic()
|
||||
|
||||
|
||||
def get_current_username(credentials: HTTPBasicCredentials = Depends(security)):
|
||||
if credentials.username != "foo" or credentials.password != "password":
|
||||
raise HTTPException(
|
||||
status_code=HTTP_401_UNAUTHORIZED,
|
||||
detail="Incorrect email or password",
|
||||
headers={"WWW-Authenticate": "Basic"},
|
||||
)
|
||||
return credentials.username
|
||||
|
||||
|
||||
@app.get("/users/me")
|
||||
def read_current_user(username: str = Depends(get_current_username)):
|
||||
return {"username": username}
|
||||
0
docs/src/sql_databases/__init__.py
Normal file
0
docs/src/sql_databases/sql_app/__init__.py
Normal file
36
docs/src/sql_databases/sql_app/crud.py
Normal file
@@ -0,0 +1,36 @@
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from . import models, schemas
|
||||
|
||||
|
||||
def get_user(db: Session, user_id: int):
|
||||
return db.query(models.User).filter(models.User.id == user_id).first()
|
||||
|
||||
|
||||
def get_user_by_email(db: Session, email: str):
|
||||
return db.query(models.User).filter(models.User.email == email).first()
|
||||
|
||||
|
||||
def get_users(db: Session, skip: int = 0, limit: int = 100):
|
||||
return db.query(models.User).offset(skip).limit(limit).all()
|
||||
|
||||
|
||||
def create_user(db: Session, user: schemas.UserCreate):
|
||||
fake_hashed_password = user.password + "notreallyhashed"
|
||||
db_user = models.User(email=user.email, hashed_password=fake_hashed_password)
|
||||
db.add(db_user)
|
||||
db.commit()
|
||||
db.refresh(db_user)
|
||||
return db_user
|
||||
|
||||
|
||||
def get_items(db: Session, skip: int = 0, limit: int = 100):
|
||||
return db.query(models.Item).offset(skip).limit(limit).all()
|
||||
|
||||
|
||||
def create_user_item(db: Session, item: schemas.ItemCreate, user_id: int):
|
||||
db_item = models.Item(**item.dict(), owner_id=user_id)
|
||||
db.add(db_item)
|
||||
db.commit()
|
||||
db.refresh(db_item)
|
||||
return db_item
|
||||
13
docs/src/sql_databases/sql_app/database.py
Normal file
@@ -0,0 +1,13 @@
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
SQLALCHEMY_DATABASE_URL = "sqlite:///./test.db"
|
||||
# SQLALCHEMY_DATABASE_URL = "postgresql://user:password@postgresserver/db"
|
||||
|
||||
engine = create_engine(
|
||||
SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}
|
||||
)
|
||||
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||
|
||||
Base = declarative_base()
|
||||
64
docs/src/sql_databases/sql_app/main.py
Normal file
@@ -0,0 +1,64 @@
|
||||
from typing import List
|
||||
|
||||
from fastapi import Depends, FastAPI, HTTPException
|
||||
from sqlalchemy.orm import Session
|
||||
from starlette.requests import Request
|
||||
from starlette.responses import Response
|
||||
|
||||
from . import crud, models, schemas
|
||||
from .database import SessionLocal, engine
|
||||
|
||||
models.Base.metadata.create_all(bind=engine)
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.middleware("http")
|
||||
async def db_session_middleware(request: Request, call_next):
|
||||
response = Response("Internal server error", status_code=500)
|
||||
try:
|
||||
request.state.db = SessionLocal()
|
||||
response = await call_next(request)
|
||||
finally:
|
||||
request.state.db.close()
|
||||
return response
|
||||
|
||||
|
||||
# Dependency
|
||||
def get_db(request: Request):
|
||||
return request.state.db
|
||||
|
||||
|
||||
@app.post("/users/", response_model=schemas.User)
|
||||
def create_user(user: schemas.UserCreate, db: Session = Depends(get_db)):
|
||||
db_user = crud.get_user_by_email(db, email=user.email)
|
||||
if db_user:
|
||||
raise HTTPException(status_code=400, detail="Email already registered")
|
||||
return crud.create_user(db=db, user=user)
|
||||
|
||||
|
||||
@app.get("/users/", response_model=List[schemas.User])
|
||||
def read_users(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
|
||||
users = crud.get_users(db, skip=skip, limit=limit)
|
||||
return users
|
||||
|
||||
|
||||
@app.get("/users/{user_id}", response_model=schemas.User)
|
||||
def read_user(user_id: int, db: Session = Depends(get_db)):
|
||||
db_user = crud.get_user(db, user_id=user_id)
|
||||
if db_user is None:
|
||||
raise HTTPException(status_code=404, detail="User not found")
|
||||
return db_user
|
||||
|
||||
|
||||
@app.post("/users/{user_id}/items/", response_model=schemas.Item)
|
||||
def create_item_for_user(
|
||||
user_id: int, item: schemas.ItemCreate, db: Session = Depends(get_db)
|
||||
):
|
||||
return crud.create_user_item(db=db, item=item, user_id=user_id)
|
||||
|
||||
|
||||
@app.get("/items/", response_model=List[schemas.Item])
|
||||
def read_items(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
|
||||
items = crud.get_items(db, skip=skip, limit=limit)
|
||||
return items
|
||||
26
docs/src/sql_databases/sql_app/models.py
Normal file
@@ -0,0 +1,26 @@
|
||||
from sqlalchemy import Boolean, Column, ForeignKey, Integer, String
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
from .database import Base
|
||||
|
||||
|
||||
class User(Base):
|
||||
__tablename__ = "users"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
email = Column(String, unique=True, index=True)
|
||||
hashed_password = Column(String)
|
||||
is_active = Column(Boolean, default=True)
|
||||
|
||||
items = relationship("Item", back_populates="owner")
|
||||
|
||||
|
||||
class Item(Base):
|
||||
__tablename__ = "items"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
title = Column(String, index=True)
|
||||
description = Column(String, index=True)
|
||||
owner_id = Column(Integer, ForeignKey("users.id"))
|
||||
|
||||
owner = relationship("User", back_populates="items")
|
||||
37
docs/src/sql_databases/sql_app/schemas.py
Normal file
@@ -0,0 +1,37 @@
|
||||
from typing import List
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class ItemBase(BaseModel):
|
||||
title: str
|
||||
description: str = None
|
||||
|
||||
|
||||
class ItemCreate(ItemBase):
|
||||
pass
|
||||
|
||||
|
||||
class Item(ItemBase):
|
||||
id: int
|
||||
owner_id: int
|
||||
|
||||
class Config:
|
||||
orm_mode = True
|
||||
|
||||
|
||||
class UserBase(BaseModel):
|
||||
email: str
|
||||
|
||||
|
||||
class UserCreate(UserBase):
|
||||
password: str
|
||||
|
||||
|
||||
class User(UserBase):
|
||||
id: int
|
||||
is_active: bool
|
||||
items: List[Item] = []
|
||||
|
||||
class Config:
|
||||
orm_mode = True
|
||||
@@ -1,76 +0,0 @@
|
||||
from fastapi import Depends, FastAPI
|
||||
from sqlalchemy import Boolean, Column, Integer, String, create_engine
|
||||
from sqlalchemy.ext.declarative import declarative_base, declared_attr
|
||||
from sqlalchemy.orm import Session, sessionmaker
|
||||
from starlette.requests import Request
|
||||
from starlette.responses import Response
|
||||
|
||||
# SQLAlchemy specific code, as with any other app
|
||||
SQLALCHEMY_DATABASE_URI = "sqlite:///./test.db"
|
||||
# SQLALCHEMY_DATABASE_URI = "postgresql://user:password@postgresserver/db"
|
||||
|
||||
engine = create_engine(
|
||||
SQLALCHEMY_DATABASE_URI, connect_args={"check_same_thread": False}
|
||||
)
|
||||
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||
|
||||
|
||||
class CustomBase:
|
||||
# Generate __tablename__ automatically
|
||||
@declared_attr
|
||||
def __tablename__(cls):
|
||||
return cls.__name__.lower()
|
||||
|
||||
|
||||
Base = declarative_base(cls=CustomBase)
|
||||
|
||||
|
||||
class User(Base):
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
email = Column(String, unique=True, index=True)
|
||||
hashed_password = Column(String)
|
||||
is_active = Column(Boolean(), default=True)
|
||||
|
||||
|
||||
Base.metadata.create_all(bind=engine)
|
||||
|
||||
db_session = SessionLocal()
|
||||
|
||||
first_user = db_session.query(User).first()
|
||||
if not first_user:
|
||||
u = User(email="johndoe@example.com", hashed_password="notreallyhashed")
|
||||
db_session.add(u)
|
||||
db_session.commit()
|
||||
|
||||
db_session.close()
|
||||
|
||||
|
||||
# Utility
|
||||
def get_user(db_session: Session, user_id: int):
|
||||
return db_session.query(User).filter(User.id == user_id).first()
|
||||
|
||||
|
||||
# Dependency
|
||||
def get_db(request: Request):
|
||||
return request.state.db
|
||||
|
||||
|
||||
# FastAPI specific code
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/users/{user_id}")
|
||||
def read_user(user_id: int, db: Session = Depends(get_db)):
|
||||
user = get_user(db, user_id=user_id)
|
||||
return user
|
||||
|
||||
|
||||
@app.middleware("http")
|
||||
async def db_session_middleware(request: Request, call_next):
|
||||
response = Response("Internal server error", status_code=500)
|
||||
try:
|
||||
request.state.db = SessionLocal()
|
||||
response = await call_next(request)
|
||||
finally:
|
||||
request.state.db.close()
|
||||
return response
|
||||
6
docs/src/static_files/tutorial001.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from fastapi import FastAPI
|
||||
from starlette.staticfiles import StaticFiles
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
app.mount("/static", StaticFiles(directory="static"), name="static")
|
||||
3
docs/src/templates/static/styles.css
Normal file
@@ -0,0 +1,3 @@
|
||||
h1 {
|
||||
color: green;
|
||||
}
|
||||
9
docs/src/templates/templates/item.html
Normal file
@@ -0,0 +1,9 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Item Details</title>
|
||||
<link href="{{ url_for('static', path='/styles.css') }}" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Item ID: {{ id }}</h1>
|
||||
</body>
|
||||
</html>
|
||||
16
docs/src/templates/tutorial001.py
Normal file
@@ -0,0 +1,16 @@
|
||||
from fastapi import FastAPI
|
||||
from starlette.requests import Request
|
||||
from starlette.staticfiles import StaticFiles
|
||||
from starlette.templating import Jinja2Templates
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
app.mount("/static", StaticFiles(directory="static"), name="static")
|
||||
|
||||
|
||||
templates = Jinja2Templates(directory="templates")
|
||||
|
||||
|
||||
@app.get("/items/{id}")
|
||||
async def read_item(request: Request, id: str):
|
||||
return templates.TemplateResponse("item.html", {"request": request, "id": id})
|
||||
0
docs/src/websockets/__init__.py
Normal file
@@ -44,10 +44,9 @@ async def get():
|
||||
return HTMLResponse(html)
|
||||
|
||||
|
||||
@app.websocket_route("/ws")
|
||||
@app.websocket("/ws")
|
||||
async def websocket_endpoint(websocket: WebSocket):
|
||||
await websocket.accept()
|
||||
while True:
|
||||
data = await websocket.receive_text()
|
||||
await websocket.send_text(f"Message text was: {data}")
|
||||
await websocket.close()
|
||||
|
||||