diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index 3052ec1e2..b9bd521b3 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -36,9 +36,9 @@ jobs: - uses: actions/upload-artifact@v3 with: name: docs-zip - path: ./docs.zip + path: ./site/docs.zip - name: Deploy to Netlify - uses: nwtgck/actions-netlify@v1.2.4 + uses: nwtgck/actions-netlify@v2.0.0 with: publish-dir: './site' production-branch: master diff --git a/.github/workflows/preview-docs.yml b/.github/workflows/preview-docs.yml index a2e5976fb..7d31a9c64 100644 --- a/.github/workflows/preview-docs.yml +++ b/.github/workflows/preview-docs.yml @@ -11,6 +11,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 + - name: Clean site + run: | + rm -rf ./site + mkdir ./site - name: Download Artifact Docs uses: dawidd6/action-download-artifact@v2.24.2 with: @@ -18,14 +22,15 @@ jobs: workflow: build-docs.yml run_id: ${{ github.event.workflow_run.id }} name: docs-zip + path: ./site/ - name: Unzip docs run: | - rm -rf ./site + cd ./site unzip docs.zip rm -f docs.zip - name: Deploy to Netlify id: netlify - uses: nwtgck/actions-netlify@v1.2.4 + uses: nwtgck/actions-netlify@v2.0.0 with: publish-dir: './site' production-deploy: false diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index fe4c5ee86..8ffb493a4 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -18,6 +18,8 @@ jobs: uses: actions/setup-python@v4 with: python-version: "3.7" + cache: "pip" + cache-dependency-path: pyproject.toml - uses: actions/cache@v3 id: cache with: @@ -29,7 +31,7 @@ jobs: - name: Build distribution run: python -m build - name: Publish - uses: pypa/gh-action-pypi-publish@v1.5.1 + uses: pypa/gh-action-pypi-publish@v1.5.2 with: password: ${{ secrets.PYPI_API_TOKEN }} - name: Dump GitHub context diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7f87be700..ddc43c942 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -21,11 +21,13 @@ jobs: uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} + cache: "pip" + cache-dependency-path: pyproject.toml - uses: actions/cache@v3 id: cache with: path: ${{ env.pythonLocation }} - key: ${{ runner.os }}-python-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml') }}-test-v02 + key: ${{ runner.os }}-python-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml') }}-test-v03 - name: Install Dependencies if: steps.cache.outputs.cache-hit != 'true' run: pip install -e .[all,dev,doc,test] @@ -52,6 +54,8 @@ jobs: - uses: actions/setup-python@v4 with: python-version: '3.8' + cache: "pip" + cache-dependency-path: pyproject.toml - name: Get coverage files uses: actions/download-artifact@v3 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e59e05abe..96f097caa 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -12,14 +12,14 @@ repos: - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/asottile/pyupgrade - rev: v3.1.0 + rev: v3.2.2 hooks: - id: pyupgrade args: - --py3-plus - --keep-runtime-typing - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: v0.0.114 + rev: v0.0.138 hooks: - id: ruff args: diff --git a/docs/en/data/github_sponsors.yml b/docs/en/data/github_sponsors.yml index 3d0b37dac..1953df801 100644 --- a/docs/en/data/github_sponsors.yml +++ b/docs/en/data/github_sponsors.yml @@ -8,12 +8,15 @@ sponsors: - login: cryptapi avatarUrl: https://avatars.githubusercontent.com/u/44925437?u=61369138589bc7fee6c417f3fbd50fbd38286cc4&v=4 url: https://github.com/cryptapi -- - login: ObliviousAI + - login: jrobbins-LiveData + avatarUrl: https://avatars.githubusercontent.com/u/79278744?u=bae8175fc3f09db281aca1f97a9ddc1a914a8c4f&v=4 + url: https://github.com/jrobbins-LiveData +- - login: nihpo + avatarUrl: https://avatars.githubusercontent.com/u/1841030?u=0264956d7580f7e46687a762a7baa629f84cf97c&v=4 + url: https://github.com/nihpo + - login: ObliviousAI avatarUrl: https://avatars.githubusercontent.com/u/65656077?v=4 url: https://github.com/ObliviousAI - - login: Lovage-Labs - avatarUrl: https://avatars.githubusercontent.com/u/71685552?v=4 - url: https://github.com/Lovage-Labs - login: chaserowbotham avatarUrl: https://avatars.githubusercontent.com/u/97751084?v=4 url: https://github.com/chaserowbotham @@ -38,9 +41,18 @@ sponsors: - - login: InesIvanova avatarUrl: https://avatars.githubusercontent.com/u/22920417?u=409882ec1df6dbd77455788bb383a8de223dbf6f&v=4 url: https://github.com/InesIvanova -- - login: SendCloud +- - login: vyos + avatarUrl: https://avatars.githubusercontent.com/u/5647000?v=4 + url: https://github.com/vyos + - login: SendCloud avatarUrl: https://avatars.githubusercontent.com/u/7831959?v=4 url: https://github.com/SendCloud + - login: matallan + avatarUrl: https://avatars.githubusercontent.com/u/12107723?v=4 + url: https://github.com/matallan + - login: takashi-yoneya + avatarUrl: https://avatars.githubusercontent.com/u/33813153?u=2d0522bceba0b8b69adf1f2db866503bd96f944e&v=4 + url: https://github.com/takashi-yoneya - login: mercedes-benz avatarUrl: https://avatars.githubusercontent.com/u/34240465?v=4 url: https://github.com/mercedes-benz @@ -56,12 +68,18 @@ sponsors: - - login: johnadjei avatarUrl: https://avatars.githubusercontent.com/u/767860?v=4 url: https://github.com/johnadjei + - login: gvisniuc + avatarUrl: https://avatars.githubusercontent.com/u/1614747?u=502dfdb2b087ddcf5460026297c98c7907bc2795&v=4 + url: https://github.com/gvisniuc - login: HiredScore avatarUrl: https://avatars.githubusercontent.com/u/3908850?v=4 url: https://github.com/HiredScore - login: Trivie avatarUrl: https://avatars.githubusercontent.com/u/8161763?v=4 url: https://github.com/Trivie + - login: Lovage-Labs + avatarUrl: https://avatars.githubusercontent.com/u/71685552?v=4 + url: https://github.com/Lovage-Labs - - login: moellenbeck avatarUrl: https://avatars.githubusercontent.com/u/169372?v=4 url: https://github.com/moellenbeck @@ -80,27 +98,18 @@ sponsors: - login: jmaralc avatarUrl: https://avatars.githubusercontent.com/u/21101214?u=b15a9f07b7cbf6c9dcdbcb6550bbd2c52f55aa50&v=4 url: https://github.com/jmaralc - - login: takashi-yoneya - avatarUrl: https://avatars.githubusercontent.com/u/33813153?u=2d0522bceba0b8b69adf1f2db866503bd96f944e&v=4 - url: https://github.com/takashi-yoneya - login: mainframeindustries avatarUrl: https://avatars.githubusercontent.com/u/55092103?v=4 url: https://github.com/mainframeindustries - - login: DelfinaCare - avatarUrl: https://avatars.githubusercontent.com/u/83734439?v=4 - url: https://github.com/DelfinaCare -- - login: karelhusa - avatarUrl: https://avatars.githubusercontent.com/u/11407706?u=4f787cffc30ea198b15935c751940f0b48a26a17&v=4 - url: https://github.com/karelhusa - - login: povilasb avatarUrl: https://avatars.githubusercontent.com/u/1213442?u=b11f58ed6ceea6e8297c9b310030478ebdac894d&v=4 url: https://github.com/povilasb - login: primer-io avatarUrl: https://avatars.githubusercontent.com/u/62146168?v=4 url: https://github.com/primer-io -- - login: DucNgn - avatarUrl: https://avatars.githubusercontent.com/u/43587865?u=a9f9f61569aebdc0ce74df85b8cc1a219a7ab570&v=4 - url: https://github.com/DucNgn +- - login: indeedeng + avatarUrl: https://avatars.githubusercontent.com/u/2905043?v=4 + url: https://github.com/indeedeng - login: A-Edge avatarUrl: https://avatars.githubusercontent.com/u/59514131?v=4 url: https://github.com/A-Edge @@ -119,9 +128,6 @@ sponsors: - login: kamalgill avatarUrl: https://avatars.githubusercontent.com/u/133923?u=0df9181d97436ce330e9acf90ab8a54b7022efe7&v=4 url: https://github.com/kamalgill - - login: gazpachoking - avatarUrl: https://avatars.githubusercontent.com/u/187133?v=4 - url: https://github.com/gazpachoking - login: dekoza avatarUrl: https://avatars.githubusercontent.com/u/210980?u=c03c78a8ae1039b500dfe343665536ebc51979b2&v=4 url: https://github.com/dekoza @@ -155,6 +161,12 @@ sponsors: - login: ltieman avatarUrl: https://avatars.githubusercontent.com/u/1084689?u=e69b17de17cb3ca141a17daa7ccbe173ceb1eb17&v=4 url: https://github.com/ltieman + - login: mrkmcknz + avatarUrl: https://avatars.githubusercontent.com/u/1089376?u=2b9b8a8c25c33a4f6c220095638bd821cdfd13a3&v=4 + url: https://github.com/mrkmcknz + - login: coffeewasmyidea + avatarUrl: https://avatars.githubusercontent.com/u/1636488?u=8e32a4f200eff54dd79cd79d55d254bfce5e946d&v=4 + url: https://github.com/coffeewasmyidea - login: corleyma avatarUrl: https://avatars.githubusercontent.com/u/2080732?u=aed2ff652294a87d666b1c3f6dbe98104db76d26&v=4 url: https://github.com/corleyma @@ -170,8 +182,11 @@ sponsors: - login: Shark009 avatarUrl: https://avatars.githubusercontent.com/u/3163309?u=0c6f4091b0eda05c44c390466199826e6dc6e431&v=4 url: https://github.com/Shark009 + - login: ColliotL + avatarUrl: https://avatars.githubusercontent.com/u/3412402?u=ca64b07ecbef2f9da1cc2cac3f37522aa4814902&v=4 + url: https://github.com/ColliotL - login: grillazz - avatarUrl: https://avatars.githubusercontent.com/u/3415861?u=0b32b7073ae1ab8b7f6d2db0188c2e1e357ff451&v=4 + avatarUrl: https://avatars.githubusercontent.com/u/3415861?u=453cd1725c8d7fe3e258016bc19cff861d4fcb53&v=4 url: https://github.com/grillazz - login: dblackrun avatarUrl: https://avatars.githubusercontent.com/u/3528486?v=4 @@ -179,20 +194,20 @@ sponsors: - login: zsinx6 avatarUrl: https://avatars.githubusercontent.com/u/3532625?u=ba75a5dc744d1116ccfeaaf30d41cb2fe81fe8dd&v=4 url: https://github.com/zsinx6 - - login: bauyrzhanospan - avatarUrl: https://avatars.githubusercontent.com/u/3536037?u=25c86201d0212497aefcc1688cccf509057a1dc4&v=4 - url: https://github.com/bauyrzhanospan - login: MarekBleschke avatarUrl: https://avatars.githubusercontent.com/u/3616870?v=4 url: https://github.com/MarekBleschke - login: aacayaco avatarUrl: https://avatars.githubusercontent.com/u/3634801?u=eaadda178c964178fcb64886f6c732172c8f8219&v=4 url: https://github.com/aacayaco + - login: anomaly + avatarUrl: https://avatars.githubusercontent.com/u/3654837?v=4 + url: https://github.com/anomaly - login: peterHoburg avatarUrl: https://avatars.githubusercontent.com/u/3860655?u=f55f47eb2d6a9b495e806ac5a044e3ae01ccc1fa&v=4 url: https://github.com/peterHoburg - login: jgreys - avatarUrl: https://avatars.githubusercontent.com/u/4136890?u=b579fd97033269a5e703ab509c7d5478b146cc2d&v=4 + avatarUrl: https://avatars.githubusercontent.com/u/4136890?u=c66ae617d614f6c886f1f1c1799d22100b3c848d&v=4 url: https://github.com/jgreys - login: gorhack avatarUrl: https://avatars.githubusercontent.com/u/4141690?u=ec119ebc4bdf00a7bc84657a71aa17834f4f27f3&v=4 @@ -203,6 +218,9 @@ sponsors: - login: oliverxchen avatarUrl: https://avatars.githubusercontent.com/u/4471774?u=534191f25e32eeaadda22dfab4b0a428733d5489&v=4 url: https://github.com/oliverxchen + - login: Rey8d01 + avatarUrl: https://avatars.githubusercontent.com/u/4836190?u=5942598a23a377602c1669522334ab5ebeaf9165&v=4 + url: https://github.com/Rey8d01 - login: ScrimForever avatarUrl: https://avatars.githubusercontent.com/u/5040124?u=091ec38bfe16d6e762099e91309b59f248616a65&v=4 url: https://github.com/ScrimForever @@ -239,9 +257,9 @@ sponsors: - login: wdwinslow avatarUrl: https://avatars.githubusercontent.com/u/11562137?u=dc01daafb354135603a263729e3d26d939c0c452&v=4 url: https://github.com/wdwinslow - - login: Ge0f3 - avatarUrl: https://avatars.githubusercontent.com/u/11887760?u=ccd80f1ac36dcb8517ef5c4e702e8cc5a80cad2f&v=4 - url: https://github.com/Ge0f3 + - login: bapi24 + avatarUrl: https://avatars.githubusercontent.com/u/11890901?u=45cc721d8f66ad2f62b086afc3d4761d0c16b9c6&v=4 + url: https://github.com/bapi24 - login: svats2k avatarUrl: https://avatars.githubusercontent.com/u/12378398?u=ecf28c19f61052e664bdfeb2391f8107d137915c&v=4 url: https://github.com/svats2k @@ -257,15 +275,12 @@ sponsors: - login: pablonnaoji avatarUrl: https://avatars.githubusercontent.com/u/15187159?u=afc15bd5a4ba9c5c7206bbb1bcaeef606a0932e0&v=4 url: https://github.com/pablonnaoji - - login: robintully - avatarUrl: https://avatars.githubusercontent.com/u/17059673?u=862b9bb01513f5acd30df97433cb97a24dbfb772&v=4 - url: https://github.com/robintully - login: wedwardbeck avatarUrl: https://avatars.githubusercontent.com/u/19333237?u=1de4ae2bf8d59eb4c013f21d863cbe0f2010575f&v=4 url: https://github.com/wedwardbeck - - login: RedCarpetUp - avatarUrl: https://avatars.githubusercontent.com/u/20360440?v=4 - url: https://github.com/RedCarpetUp + - login: m4knV + avatarUrl: https://avatars.githubusercontent.com/u/19666130?u=843383978814886be93c137d10d2e20e9f13af07&v=4 + url: https://github.com/m4knV - login: Filimoa avatarUrl: https://avatars.githubusercontent.com/u/21352040?u=75e02d102d2ee3e3d793e555fa5c63045913ccb0&v=4 url: https://github.com/Filimoa @@ -302,6 +317,9 @@ sponsors: - login: ProteinQure avatarUrl: https://avatars.githubusercontent.com/u/33707203?v=4 url: https://github.com/ProteinQure + - login: faviasono + avatarUrl: https://avatars.githubusercontent.com/u/37707874?u=f0b75ca4248987c08ed8fb8ed682e7e74d5d7091&v=4 + url: https://github.com/faviasono - login: ybressler avatarUrl: https://avatars.githubusercontent.com/u/40807730?u=41e2c00f1eebe3c402635f0325e41b4e6511462c&v=4 url: https://github.com/ybressler @@ -341,9 +359,12 @@ sponsors: - login: anthonycepeda avatarUrl: https://avatars.githubusercontent.com/u/72019805?u=4252c6b6dc5024af502a823a3ac5e7a03a69963f&v=4 url: https://github.com/anthonycepeda - - login: dotlas - avatarUrl: https://avatars.githubusercontent.com/u/88832003?v=4 - url: https://github.com/dotlas + - login: fpiem + avatarUrl: https://avatars.githubusercontent.com/u/77389987?u=f667a25cd4832b28801189013b74450e06cc232c&v=4 + url: https://github.com/fpiem + - login: DelfinaCare + avatarUrl: https://avatars.githubusercontent.com/u/83734439?v=4 + url: https://github.com/DelfinaCare - login: programvx avatarUrl: https://avatars.githubusercontent.com/u/96057906?v=4 url: https://github.com/programvx @@ -354,7 +375,7 @@ sponsors: avatarUrl: https://avatars.githubusercontent.com/u/115501964?v=4 url: https://github.com/Dagmaara - - login: linux-china - avatarUrl: https://avatars.githubusercontent.com/u/46711?v=4 + avatarUrl: https://avatars.githubusercontent.com/u/46711?u=cd77c65338b158750eb84dc7ff1acf3209ccfc4f&v=4 url: https://github.com/linux-china - login: ddanier avatarUrl: https://avatars.githubusercontent.com/u/113563?u=ed1dc79de72f93bd78581f88ebc6952b62f472da&v=4 @@ -371,15 +392,9 @@ sponsors: - login: hhatto avatarUrl: https://avatars.githubusercontent.com/u/150309?u=3e8f63c27bf996bfc68464b0ce3f7a3e40e6ea7f&v=4 url: https://github.com/hhatto - - login: yourkin - avatarUrl: https://avatars.githubusercontent.com/u/178984?u=b43a7e5f8818f7d9083d3b110118d9c27d48a794&v=4 - url: https://github.com/yourkin - login: slafs avatarUrl: https://avatars.githubusercontent.com/u/210173?v=4 url: https://github.com/slafs - - login: assem-ch - avatarUrl: https://avatars.githubusercontent.com/u/315228?u=e0c5ab30726d3243a40974bb9bae327866e42d9b&v=4 - url: https://github.com/assem-ch - login: adamghill avatarUrl: https://avatars.githubusercontent.com/u/317045?u=f1349d5ffe84a19f324e204777859fbf69ddf633&v=4 url: https://github.com/adamghill @@ -494,9 +509,6 @@ sponsors: - login: logan-connolly avatarUrl: https://avatars.githubusercontent.com/u/16244943?u=8ae66dfbba936463cc8aa0dd7a6d2b4c0cc757eb&v=4 url: https://github.com/logan-connolly - - login: stevenayers - avatarUrl: https://avatars.githubusercontent.com/u/16361214?u=098b797d8d48afb8cd964b717847943b61d24a6d&v=4 - url: https://github.com/stevenayers - login: cdsre avatarUrl: https://avatars.githubusercontent.com/u/16945936?v=4 url: https://github.com/cdsre @@ -521,6 +533,9 @@ sponsors: - login: SebTota avatarUrl: https://avatars.githubusercontent.com/u/25122511?v=4 url: https://github.com/SebTota + - login: joerambo + avatarUrl: https://avatars.githubusercontent.com/u/26282974?v=4 + url: https://github.com/joerambo - login: mertguvencli avatarUrl: https://avatars.githubusercontent.com/u/29762151?u=16a906d90df96c8cff9ea131a575c4bc171b1523&v=4 url: https://github.com/mertguvencli @@ -533,6 +548,9 @@ sponsors: - login: engineerjoe440 avatarUrl: https://avatars.githubusercontent.com/u/33275230?u=eb223cad27017bb1e936ee9b429b450d092d0236&v=4 url: https://github.com/engineerjoe440 + - login: bnkc + avatarUrl: https://avatars.githubusercontent.com/u/34930566?u=20f362505e2a994805233f42e69f9f14b4a9dd0c&v=4 + url: https://github.com/bnkc - login: declon avatarUrl: https://avatars.githubusercontent.com/u/36180226?v=4 url: https://github.com/declon @@ -590,12 +608,9 @@ sponsors: - login: pondDevThai avatarUrl: https://avatars.githubusercontent.com/u/71592181?u=08af9a59bccfd8f6b101de1005aa9822007d0a44&v=4 url: https://github.com/pondDevThai - - login: marlonmartins2 - avatarUrl: https://avatars.githubusercontent.com/u/87719558?u=d07ffecfdabf4fd9aca987f8b5cd9128c65e598e&v=4 - url: https://github.com/marlonmartins2 -- - login: 2niuhe - avatarUrl: https://avatars.githubusercontent.com/u/13382324?u=ac918d72e0329c87ba29b3bf85e03e98a4ee9427&v=4 - url: https://github.com/2niuhe +- - login: wardal + avatarUrl: https://avatars.githubusercontent.com/u/15804042?v=4 + url: https://github.com/wardal - login: gabrielmbmb avatarUrl: https://avatars.githubusercontent.com/u/29572918?u=6d1e00b5d558e96718312ff910a2318f47cc3145&v=4 url: https://github.com/gabrielmbmb @@ -605,6 +620,3 @@ sponsors: - login: Moises6669 avatarUrl: https://avatars.githubusercontent.com/u/66188523?u=96af25b8d5be9f983cb96e9dd7c605c716caf1f5&v=4 url: https://github.com/Moises6669 - - login: su-shubham - avatarUrl: https://avatars.githubusercontent.com/u/75021117?v=4 - url: https://github.com/su-shubham diff --git a/docs/en/data/people.yml b/docs/en/data/people.yml index 730528795..51940a6b1 100644 --- a/docs/en/data/people.yml +++ b/docs/en/data/people.yml @@ -1,12 +1,12 @@ maintainers: - login: tiangolo - answers: 1315 - prs: 342 + answers: 1837 + prs: 360 avatarUrl: https://avatars.githubusercontent.com/u/1326112?u=740f11212a731f56798f558ceddb0bd07642afa7&v=4 url: https://github.com/tiangolo experts: - login: Kludex - count: 367 + count: 374 avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=62adc405ef418f4b6c8caa93d3eb8ab107bc4927&v=4 url: https://github.com/Kludex - login: dmontagu @@ -15,14 +15,14 @@ experts: url: https://github.com/dmontagu - login: ycd count: 221 - avatarUrl: https://avatars.githubusercontent.com/u/62724709?u=fa40e037060d62bf82e16b505d870a2866725f38&v=4 + avatarUrl: https://avatars.githubusercontent.com/u/62724709?u=bba5af018423a2858d49309bed2a899bb5c34ac5&v=4 url: https://github.com/ycd - login: Mause count: 207 avatarUrl: https://avatars.githubusercontent.com/u/1405026?v=4 url: https://github.com/Mause - login: JarroVGIT - count: 187 + count: 192 avatarUrl: https://avatars.githubusercontent.com/u/13659033?u=e8bea32d07a5ef72f7dde3b2079ceb714923ca05&v=4 url: https://github.com/JarroVGIT - login: euri10 @@ -33,22 +33,26 @@ experts: count: 130 avatarUrl: https://avatars.githubusercontent.com/u/331403?v=4 url: https://github.com/phy25 +- login: iudeen + count: 87 + avatarUrl: https://avatars.githubusercontent.com/u/10519440?u=2843b3303282bff8b212dcd4d9d6689452e4470c&v=4 + url: https://github.com/iudeen - login: raphaelauv count: 77 avatarUrl: https://avatars.githubusercontent.com/u/10202690?u=e6f86f5c0c3026a15d6b51792fa3e532b12f1371&v=4 url: https://github.com/raphaelauv -- login: iudeen - count: 76 - avatarUrl: https://avatars.githubusercontent.com/u/10519440?u=2843b3303282bff8b212dcd4d9d6689452e4470c&v=4 - url: https://github.com/iudeen - login: ArcLightSlavik count: 71 avatarUrl: https://avatars.githubusercontent.com/u/31127044?u=b0f2c37142f4b762e41ad65dc49581813422bd71&v=4 url: https://github.com/ArcLightSlavik - login: falkben - count: 58 + count: 59 avatarUrl: https://avatars.githubusercontent.com/u/653031?u=0c8d8f33d87f1aa1a6488d3f02105e9abc838105&v=4 url: https://github.com/falkben +- login: jgould22 + count: 55 + avatarUrl: https://avatars.githubusercontent.com/u/4335847?u=ed77f67e0bb069084639b24d812dbb2a2b1dc554&v=4 + url: https://github.com/jgould22 - login: sm-Fifteen count: 50 avatarUrl: https://avatars.githubusercontent.com/u/516999?u=437c0c5038558c67e887ccd863c1ba0f846c03da&v=4 @@ -57,10 +61,6 @@ experts: count: 46 avatarUrl: https://avatars.githubusercontent.com/u/16958893?u=f8be7088d5076d963984a21f95f44e559192d912&v=4 url: https://github.com/insomnes -- login: jgould22 - count: 46 - avatarUrl: https://avatars.githubusercontent.com/u/4335847?u=ed77f67e0bb069084639b24d812dbb2a2b1dc554&v=4 - url: https://github.com/jgould22 - login: Dustyposa count: 45 avatarUrl: https://avatars.githubusercontent.com/u/27180793?u=5cf2877f50b3eb2bc55086089a78a36f07042889&v=4 @@ -78,19 +78,19 @@ experts: avatarUrl: https://avatars.githubusercontent.com/u/5167622?u=de8f597c81d6336fcebc37b32dfd61a3f877160c&v=4 url: https://github.com/STeveShary - login: chbndrhnns - count: 35 + count: 36 avatarUrl: https://avatars.githubusercontent.com/u/7534547?v=4 url: https://github.com/chbndrhnns +- login: frankie567 + count: 33 + avatarUrl: https://avatars.githubusercontent.com/u/1144727?u=85c025e3fcc7bd79a5665c63ee87cdf8aae13374&v=4 + url: https://github.com/frankie567 - login: prostomarkeloff count: 33 avatarUrl: https://avatars.githubusercontent.com/u/28061158?u=72309cc1f2e04e40fa38b29969cb4e9d3f722e7b&v=4 url: https://github.com/prostomarkeloff -- login: frankie567 - count: 31 - avatarUrl: https://avatars.githubusercontent.com/u/1144727?u=85c025e3fcc7bd79a5665c63ee87cdf8aae13374&v=4 - url: https://github.com/frankie567 - login: acidjunk - count: 31 + count: 32 avatarUrl: https://avatars.githubusercontent.com/u/685002?u=b5094ab4527fc84b006c0ac9ff54367bdebb2267&v=4 url: https://github.com/acidjunk - login: krishnardt @@ -113,12 +113,16 @@ experts: count: 25 avatarUrl: https://avatars.githubusercontent.com/u/43723790?u=9bcce836bbce55835291c5b2ac93a4e311f4b3c3&v=4 url: https://github.com/dbanty +- login: yinziyan1206 + count: 25 + avatarUrl: https://avatars.githubusercontent.com/u/37829370?u=da44ca53aefd5c23f346fab8e9fd2e108294c179&v=4 + url: https://github.com/yinziyan1206 - login: SirTelemak count: 24 avatarUrl: https://avatars.githubusercontent.com/u/9435877?u=719327b7d2c4c62212456d771bfa7c6b8dbb9eac&v=4 url: https://github.com/SirTelemak - login: odiseo0 - count: 23 + count: 24 avatarUrl: https://avatars.githubusercontent.com/u/87550035?u=16f9255804161c6ff3c8b7ef69848f0126bcd405&v=4 url: https://github.com/odiseo0 - login: acnebs @@ -137,14 +141,14 @@ experts: count: 19 avatarUrl: https://avatars.githubusercontent.com/u/24581770?v=4 url: https://github.com/retnikt +- login: rafsaf + count: 19 + avatarUrl: https://avatars.githubusercontent.com/u/51059348?u=f8f0d6d6e90fac39fa786228158ba7f013c74271&v=4 + url: https://github.com/rafsaf - login: Hultner count: 18 avatarUrl: https://avatars.githubusercontent.com/u/2669034?u=115e53df959309898ad8dc9443fbb35fee71df07&v=4 url: https://github.com/Hultner -- login: rafsaf - count: 18 - avatarUrl: https://avatars.githubusercontent.com/u/51059348?u=f8f0d6d6e90fac39fa786228158ba7f013c74271&v=4 - url: https://github.com/rafsaf - login: jorgerpo count: 17 avatarUrl: https://avatars.githubusercontent.com/u/12537771?u=7444d20019198e34911082780cc7ad73f2b97cb3&v=4 @@ -161,10 +165,6 @@ experts: count: 16 avatarUrl: https://avatars.githubusercontent.com/u/39515546?u=ec35139777597cdbbbddda29bf8b9d4396b429a9&v=4 url: https://github.com/waynerv -- login: yinziyan1206 - count: 16 - avatarUrl: https://avatars.githubusercontent.com/u/37829370?v=4 - url: https://github.com/yinziyan1206 - login: dstlny count: 16 avatarUrl: https://avatars.githubusercontent.com/u/41964673?u=9f2174f9d61c15c6e3a4c9e3aeee66f711ce311f&v=4 @@ -173,6 +173,10 @@ experts: count: 15 avatarUrl: https://avatars.githubusercontent.com/u/26334101?u=071c062d2861d3dd127f6b4a5258cd8ef55d4c50&v=4 url: https://github.com/jonatasoli +- login: mbroton + count: 15 + avatarUrl: https://avatars.githubusercontent.com/u/50829834?u=a48610bf1bffaa9c75d03228926e2eb08a2e24ee&v=4 + url: https://github.com/mbroton - login: hellocoldworld count: 14 avatarUrl: https://avatars.githubusercontent.com/u/47581948?u=3d2186796434c507a6cb6de35189ab0ad27c356f&v=4 @@ -193,27 +197,39 @@ experts: count: 12 avatarUrl: https://avatars.githubusercontent.com/u/2964996?v=4 url: https://github.com/n8sty -- login: mbroton - count: 12 - avatarUrl: https://avatars.githubusercontent.com/u/50829834?u=a48610bf1bffaa9c75d03228926e2eb08a2e24ee&v=4 - url: https://github.com/mbroton last_month_active: -- login: JarroVGIT - count: 18 - avatarUrl: https://avatars.githubusercontent.com/u/13659033?u=e8bea32d07a5ef72f7dde3b2079ceb714923ca05&v=4 - url: https://github.com/JarroVGIT +- login: jgould22 + count: 9 + avatarUrl: https://avatars.githubusercontent.com/u/4335847?u=ed77f67e0bb069084639b24d812dbb2a2b1dc554&v=4 + url: https://github.com/jgould22 +- login: yinziyan1206 + count: 9 + avatarUrl: https://avatars.githubusercontent.com/u/37829370?u=da44ca53aefd5c23f346fab8e9fd2e108294c179&v=4 + url: https://github.com/yinziyan1206 - login: iudeen count: 8 avatarUrl: https://avatars.githubusercontent.com/u/10519440?u=2843b3303282bff8b212dcd4d9d6689452e4470c&v=4 url: https://github.com/iudeen -- login: mbroton +- login: Kludex + count: 5 + avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=62adc405ef418f4b6c8caa93d3eb8ab107bc4927&v=4 + url: https://github.com/Kludex +- login: JarroVGIT + count: 5 + avatarUrl: https://avatars.githubusercontent.com/u/13659033?u=e8bea32d07a5ef72f7dde3b2079ceb714923ca05&v=4 + url: https://github.com/JarroVGIT +- login: TheJumpyWizard count: 4 + avatarUrl: https://avatars.githubusercontent.com/u/90986815?u=67e9c13c9f063dd4313db7beb64eaa2f3a37f1fe&v=4 + url: https://github.com/TheJumpyWizard +- login: mbroton + count: 3 avatarUrl: https://avatars.githubusercontent.com/u/50829834?u=a48610bf1bffaa9c75d03228926e2eb08a2e24ee&v=4 url: https://github.com/mbroton -- login: yinziyan1206 +- login: mateoradman count: 3 - avatarUrl: https://avatars.githubusercontent.com/u/37829370?v=4 - url: https://github.com/yinziyan1206 + avatarUrl: https://avatars.githubusercontent.com/u/48420316?u=066f36b8e8e263b0d90798113b0f291d3266db7c&v=4 + url: https://github.com/mateoradman top_contributors: - login: waynerv count: 25 @@ -223,22 +239,18 @@ top_contributors: count: 22 avatarUrl: https://avatars.githubusercontent.com/u/41147016?u=55010621aece725aa702270b54fed829b6a1fe60&v=4 url: https://github.com/tokusumi +- login: jaystone776 + count: 17 + avatarUrl: https://avatars.githubusercontent.com/u/11191137?u=299205a95e9b6817a43144a48b643346a5aac5cc&v=4 + url: https://github.com/jaystone776 - login: dmontagu count: 16 avatarUrl: https://avatars.githubusercontent.com/u/35119617?u=58ed2a45798a4339700e2f62b2e12e6e54bf0396&v=4 url: https://github.com/dmontagu -- login: jaystone776 - count: 16 - avatarUrl: https://avatars.githubusercontent.com/u/11191137?u=299205a95e9b6817a43144a48b643346a5aac5cc&v=4 - url: https://github.com/jaystone776 - login: Kludex count: 15 avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=62adc405ef418f4b6c8caa93d3eb8ab107bc4927&v=4 url: https://github.com/Kludex -- login: dependabot - count: 14 - avatarUrl: https://avatars.githubusercontent.com/in/29110?v=4 - url: https://github.com/apps/dependabot - login: euri10 count: 13 avatarUrl: https://avatars.githubusercontent.com/u/1104190?u=321a2e953e6645a7d09b732786c7a8061e0f8a8b&v=4 @@ -267,10 +279,6 @@ top_contributors: count: 7 avatarUrl: https://avatars.githubusercontent.com/u/56785022?u=d5c3a02567c8649e146fcfc51b6060ccaf8adef8&v=4 url: https://github.com/rjNemo -- login: pre-commit-ci - count: 6 - avatarUrl: https://avatars.githubusercontent.com/in/68672?v=4 - url: https://github.com/apps/pre-commit-ci - login: wshayes count: 5 avatarUrl: https://avatars.githubusercontent.com/u/365303?u=07ca03c5ee811eb0920e633cc3c3db73dbec1aa5&v=4 @@ -287,6 +295,10 @@ top_contributors: count: 5 avatarUrl: https://avatars.githubusercontent.com/u/43503750?u=f440bc9062afb3c43b9b9c6cdfdcfe31d58699ef&v=4 url: https://github.com/ComicShrimp +- login: batlopes + count: 5 + avatarUrl: https://avatars.githubusercontent.com/u/33462923?u=0fb3d7acb316764616f11e4947faf080e49ad8d9&v=4 + url: https://github.com/batlopes - login: jekirl count: 4 avatarUrl: https://avatars.githubusercontent.com/u/2546697?u=a027452387d85bd4a14834e19d716c99255fb3b7&v=4 @@ -301,7 +313,7 @@ top_contributors: url: https://github.com/jfunez - login: ycd count: 4 - avatarUrl: https://avatars.githubusercontent.com/u/62724709?u=fa40e037060d62bf82e16b505d870a2866725f38&v=4 + avatarUrl: https://avatars.githubusercontent.com/u/62724709?u=bba5af018423a2858d49309bed2a899bb5c34ac5&v=4 url: https://github.com/ycd - login: komtaki count: 4 @@ -319,21 +331,17 @@ top_contributors: count: 4 avatarUrl: https://avatars.githubusercontent.com/u/79563565?u=1741703bd6c8f491503354b363a86e879b4c1cab&v=4 url: https://github.com/NinaHwang -- login: batlopes - count: 4 - avatarUrl: https://avatars.githubusercontent.com/u/33462923?u=0fb3d7acb316764616f11e4947faf080e49ad8d9&v=4 - url: https://github.com/batlopes top_reviewers: - login: Kludex - count: 108 + count: 109 avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=62adc405ef418f4b6c8caa93d3eb8ab107bc4927&v=4 url: https://github.com/Kludex - login: BilalAlpaslan - count: 65 + count: 70 avatarUrl: https://avatars.githubusercontent.com/u/47563997?u=63ed66e304fe8d765762c70587d61d9196e5c82d&v=4 url: https://github.com/BilalAlpaslan - login: yezz123 - count: 56 + count: 59 avatarUrl: https://avatars.githubusercontent.com/u/52716203?u=636b4f79645176df4527dd45c12d5dbb5a4193cf&v=4 url: https://github.com/yezz123 - login: tokusumi @@ -350,7 +358,7 @@ top_reviewers: url: https://github.com/Laineyzhang55 - login: ycd count: 45 - avatarUrl: https://avatars.githubusercontent.com/u/62724709?u=fa40e037060d62bf82e16b505d870a2866725f38&v=4 + avatarUrl: https://avatars.githubusercontent.com/u/62724709?u=bba5af018423a2858d49309bed2a899bb5c34ac5&v=4 url: https://github.com/ycd - login: cikay count: 41 @@ -364,30 +372,30 @@ top_reviewers: count: 33 avatarUrl: https://avatars.githubusercontent.com/u/1024932?u=b2ea249c6b41ddf98679c8d110d0f67d4a3ebf93&v=4 url: https://github.com/AdrianDeAnda +- login: iudeen + count: 33 + avatarUrl: https://avatars.githubusercontent.com/u/10519440?u=2843b3303282bff8b212dcd4d9d6689452e4470c&v=4 + url: https://github.com/iudeen - login: ArcLightSlavik count: 31 avatarUrl: https://avatars.githubusercontent.com/u/31127044?u=b0f2c37142f4b762e41ad65dc49581813422bd71&v=4 url: https://github.com/ArcLightSlavik -- login: iudeen - count: 26 - avatarUrl: https://avatars.githubusercontent.com/u/10519440?u=2843b3303282bff8b212dcd4d9d6689452e4470c&v=4 - url: https://github.com/iudeen +- login: komtaki + count: 27 + avatarUrl: https://avatars.githubusercontent.com/u/39375566?u=260ad6b1a4b34c07dbfa728da5e586f16f6d1824&v=4 + url: https://github.com/komtaki - login: cassiobotaro - count: 25 + count: 26 avatarUrl: https://avatars.githubusercontent.com/u/3127847?u=b0a652331da17efeb85cd6e3a4969182e5004804&v=4 url: https://github.com/cassiobotaro - login: lsglucas - count: 25 + count: 26 avatarUrl: https://avatars.githubusercontent.com/u/61513630?u=320e43fe4dc7bc6efc64e9b8f325f8075634fd20&v=4 url: https://github.com/lsglucas - login: dmontagu count: 23 avatarUrl: https://avatars.githubusercontent.com/u/35119617?u=58ed2a45798a4339700e2f62b2e12e6e54bf0396&v=4 url: https://github.com/dmontagu -- login: komtaki - count: 21 - avatarUrl: https://avatars.githubusercontent.com/u/39375566?u=260ad6b1a4b34c07dbfa728da5e586f16f6d1824&v=4 - url: https://github.com/komtaki - login: hard-coders count: 20 avatarUrl: https://avatars.githubusercontent.com/u/9651103?u=95db33927bbff1ed1c07efddeb97ac2ff33068ed&v=4 @@ -429,7 +437,7 @@ top_reviewers: avatarUrl: https://avatars.githubusercontent.com/u/63476957?u=6c86e59b48e0394d4db230f37fc9ad4d7e2c27c7&v=4 url: https://github.com/delhi09 - login: odiseo0 - count: 14 + count: 15 avatarUrl: https://avatars.githubusercontent.com/u/87550035?u=16f9255804161c6ff3c8b7ef69848f0126bcd405&v=4 url: https://github.com/odiseo0 - login: sh0nk @@ -464,6 +472,14 @@ top_reviewers: count: 10 avatarUrl: https://avatars.githubusercontent.com/u/43503750?u=f440bc9062afb3c43b9b9c6cdfdcfe31d58699ef&v=4 url: https://github.com/ComicShrimp +- login: peidrao + count: 10 + avatarUrl: https://avatars.githubusercontent.com/u/32584628?u=88c2cb42a99e0f50cdeae3606992568184783ee5&v=4 + url: https://github.com/peidrao +- login: izaguerreiro + count: 9 + avatarUrl: https://avatars.githubusercontent.com/u/2241504?v=4 + url: https://github.com/izaguerreiro - login: graingert count: 9 avatarUrl: https://avatars.githubusercontent.com/u/413772?u=64b77b6aa405c68a9c6bcf45f84257c66eea5f32&v=4 @@ -480,10 +496,6 @@ top_reviewers: count: 9 avatarUrl: https://avatars.githubusercontent.com/u/69092910?u=4ac58eab99bd37d663f3d23551df96d4fbdbf760&v=4 url: https://github.com/bezaca -- login: izaguerreiro - count: 8 - avatarUrl: https://avatars.githubusercontent.com/u/2241504?v=4 - url: https://github.com/izaguerreiro - login: raphaelauv count: 8 avatarUrl: https://avatars.githubusercontent.com/u/10202690?u=e6f86f5c0c3026a15d6b51792fa3e532b12f1371&v=4 @@ -504,6 +516,10 @@ top_reviewers: count: 8 avatarUrl: https://avatars.githubusercontent.com/u/662249?v=4 url: https://github.com/dimaqq +- login: Xewus + count: 8 + avatarUrl: https://avatars.githubusercontent.com/u/85196001?u=4bdd4a0300530a504987db27488ba79c37f2fb18&v=4 + url: https://github.com/Xewus - login: Serrones count: 7 avatarUrl: https://avatars.githubusercontent.com/u/22691749?u=4795b880e13ca33a73e52fc0ef7dc9c60c8fce47&v=4 @@ -512,11 +528,3 @@ top_reviewers: count: 7 avatarUrl: https://avatars.githubusercontent.com/u/21287303?u=b049eac3e51a4c0473c2efe66b4d28a7d8f2b572&v=4 url: https://github.com/jovicon -- login: ryuckel - count: 7 - avatarUrl: https://avatars.githubusercontent.com/u/36391432?u=094eec0cfddd5013f76f31e55e56147d78b19553&v=4 - url: https://github.com/ryuckel -- login: NastasiaSaby - count: 7 - avatarUrl: https://avatars.githubusercontent.com/u/8245071?u=b3afd005f9e4bf080c219ef61a592b3a8004b764&v=4 - url: https://github.com/NastasiaSaby diff --git a/docs/en/docs/advanced/middleware.md b/docs/en/docs/advanced/middleware.md index ed90f29be..3bf49e392 100644 --- a/docs/en/docs/advanced/middleware.md +++ b/docs/en/docs/advanced/middleware.md @@ -68,7 +68,7 @@ Enforces that all incoming requests have a correctly set `Host` header, in order The following arguments are supported: -* `allowed_hosts` - A list of domain names that should be allowed as hostnames. Wildcard domains such as `*.example.com` are supported for matching subdomains to allow any hostname either use `allowed_hosts=["*"]` or omit the middleware. +* `allowed_hosts` - A list of domain names that should be allowed as hostnames. Wildcard domains such as `*.example.com` are supported for matching subdomains. To allow any hostname either use `allowed_hosts=["*"]` or omit the middleware. If an incoming request does not validate correctly then a `400` response will be sent. diff --git a/docs/en/docs/release-notes.md b/docs/en/docs/release-notes.md index 1eb2fad32..b65460294 100644 --- a/docs/en/docs/release-notes.md +++ b/docs/en/docs/release-notes.md @@ -2,6 +2,36 @@ ## Latest Changes +* ⬆ Bump nwtgck/actions-netlify from 1.2.4 to 2.0.0. PR [#5757](https://github.com/tiangolo/fastapi/pull/5757) by [@dependabot[bot]](https://github.com/apps/dependabot). +* đŸ‘· Refactor CI artifact upload/download for docs previews. PR [#5793](https://github.com/tiangolo/fastapi/pull/5793) by [@tiangolo](https://github.com/tiangolo). +* ⬆ Bump pypa/gh-action-pypi-publish from 1.5.1 to 1.5.2. PR [#5714](https://github.com/tiangolo/fastapi/pull/5714) by [@dependabot[bot]](https://github.com/apps/dependabot). +* đŸ‘„ Update FastAPI People. PR [#5722](https://github.com/tiangolo/fastapi/pull/5722) by [@github-actions[bot]](https://github.com/apps/github-actions). +* 🔧 Update sponsors, disable course bundle. PR [#5713](https://github.com/tiangolo/fastapi/pull/5713) by [@tiangolo](https://github.com/tiangolo). +* ⬆ Update typer[all] requirement from <0.7.0,>=0.6.1 to >=0.6.1,<0.8.0. PR [#5639](https://github.com/tiangolo/fastapi/pull/5639) by [@dependabot[bot]](https://github.com/apps/dependabot). + +## 0.88.0 + +### Upgrades + +* ⬆ Bump Starlette to version `0.22.0` to fix bad encoding for query parameters in new `TestClient`. PR [#5659](https://github.com/tiangolo/fastapi/pull/5659) by [@azogue](https://github.com/azogue). + +### Docs + +* ✏ Fix typo in docs for `docs/en/docs/advanced/middleware.md`. PR [#5376](https://github.com/tiangolo/fastapi/pull/5376) by [@rifatrakib](https://github.com/rifatrakib). + +### Translations + +* 🌐 Add Portuguese translation for `docs/pt/docs/deployment/docker.md`. PR [#5663](https://github.com/tiangolo/fastapi/pull/5663) by [@ayr-ton](https://github.com/ayr-ton). + +### Internal + +* đŸ‘· Tweak build-docs to improve CI performance. PR [#5699](https://github.com/tiangolo/fastapi/pull/5699) by [@tiangolo](https://github.com/tiangolo). +* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#5566](https://github.com/tiangolo/fastapi/pull/5566) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci). +* âŹ†ïž Upgrade Ruff. PR [#5698](https://github.com/tiangolo/fastapi/pull/5698) by [@tiangolo](https://github.com/tiangolo). +* đŸ‘· Remove pip cache for Smokeshow as it depends on a requirements.txt. PR [#5700](https://github.com/tiangolo/fastapi/pull/5700) by [@tiangolo](https://github.com/tiangolo). +* 💚 Fix pip cache for Smokeshow. PR [#5697](https://github.com/tiangolo/fastapi/pull/5697) by [@tiangolo](https://github.com/tiangolo). +* đŸ‘· Fix and tweak CI cache handling. PR [#5696](https://github.com/tiangolo/fastapi/pull/5696) by [@tiangolo](https://github.com/tiangolo). +* đŸ‘· Update `setup-python` action in tests to use new caching feature. PR [#5680](https://github.com/tiangolo/fastapi/pull/5680) by [@madkinsz](https://github.com/madkinsz). * ⬆ Bump black from 22.8.0 to 22.10.0. PR [#5569](https://github.com/tiangolo/fastapi/pull/5569) by [@dependabot[bot]](https://github.com/apps/dependabot). ## 0.87.0 diff --git a/docs/en/overrides/main.html b/docs/en/overrides/main.html index c5eb94870..e9b9f60eb 100644 --- a/docs/en/overrides/main.html +++ b/docs/en/overrides/main.html @@ -22,12 +22,6 @@
-
- - - - -
@@ -40,12 +34,6 @@
-
diff --git a/docs/pt/docs/deployment/docker.md b/docs/pt/docs/deployment/docker.md new file mode 100644 index 000000000..42c31db29 --- /dev/null +++ b/docs/pt/docs/deployment/docker.md @@ -0,0 +1,701 @@ +# FastAPI em contĂȘineres - Docker + +Ao fazer o deploy de aplicaçÔes FastAPI uma abordagem comum Ă© construir uma **imagem de contĂȘiner Linux**. Isso normalmente Ă© feito usando o **Docker**. VocĂȘ pode a partir disso fazer o deploy dessa imagem de algumas maneiras. + +Usando contĂȘineres Linux vocĂȘ tem diversas vantagens incluindo **segurança**, **replicabilidade**, **simplicidade**, entre outras. + +!!! Dica + EstĂĄ com pressa e jĂĄ sabe dessas coisas? Pode ir direto para [`Dockerfile` abaixo 👇](#build-a-docker-image-for-fastapi). + + +
+Visualização do Dockerfile 👀 + +```Dockerfile +FROM python:3.9 + +WORKDIR /code + +COPY ./requirements.txt /code/requirements.txt + +RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt + +COPY ./app /code/app + +CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"] + +# If running behind a proxy like Nginx or Traefik add --proxy-headers +# CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80", "--proxy-headers"] +``` + +
+ +## O que Ă© um ContĂȘiner + +ContĂȘineres (especificamente contĂȘineres Linux) sĂŁo um jeito muito **leve** de empacotar aplicaçÔes contendo todas as dependĂȘncias e arquivos necessĂĄrios enquanto os mantĂ©m isolados de outros contĂȘineres (outras aplicaçÔes ou componentes) no mesmo sistema. + +ContĂȘineres Linux rodam usando o mesmo kernel Linux do hospedeiro (mĂĄquina, mĂĄquina virtual, servidor na nuvem, etc). Isso simplesmente significa que eles sĂŁo muito leves (comparados com mĂĄquinas virtuais emulando um sistema operacional completo). + +Dessa forma, contĂȘineres consomem **poucos recursos**, uma quantidade comparĂĄvel com rodar os processos diretamente (uma mĂĄquina virtual consumiria muito mais). + +ContĂȘineres tambĂ©m possuem seus prĂłprios processos (comumente um Ășnico processo), sistema de arquivos e rede **isolados** simplificando deploy, segurança, desenvolvimento, etc. + +## O que Ă© uma Imagem de ContĂȘiner + +Um **contĂȘiner** roda a partir de uma **imagem de contĂȘiner**. + +Uma imagem de contĂȘiner Ă© uma versĂŁo **estĂĄtica** de todos os arquivos, variĂĄveis de ambiente e do comando/programa padrĂŁo que deve estar presente num contĂȘiner. **EstĂĄtica** aqui significa que a **imagem** de contĂȘiner nĂŁo estĂĄ rodando, nĂŁo estĂĄ sendo executada, somente contĂ©m os arquivos e metadados empacotados. + +Em contraste com a "**imagem de contĂȘiner**" que contĂ©m os conteĂșdos estĂĄticos armazenados, um "**contĂȘiner**" normalmente se refere Ă  instĂąncia rodando, a coisa que estĂĄ sendo **executada**. + +Quando o **contĂȘiner** Ă© iniciado e estĂĄ rodando (iniciado a partir de uma **imagem de contĂȘiner**), ele pode criar ou modificar arquivos, variĂĄveis de ambiente, etc. Essas mudanças vĂŁo existir somente nesse contĂȘiner, mas nĂŁo persistirĂŁo na imagem subjacente do container (nĂŁo serĂŁo salvas no disco). + +Uma imagem de contĂȘiner Ă© comparĂĄvel ao arquivo de **programa** e seus conteĂșdos, ex.: `python` e algum arquivo `main.py`. + +E o **contĂȘiner** em si (em contraste Ă  **imagem de contĂȘiner**) Ă© a prĂłpria instĂąncia da imagem rodando, comparĂĄvel a um **processo**. Na verdade, um contĂȘiner estĂĄ rodando somente quando hĂĄ um **processo rodando** (e normalmente Ă© somente um processo). O contĂȘiner finaliza quando nĂŁo hĂĄ um processo rodando nele. + +## Imagens de contĂȘiner + +Docker tem sido uma das principais ferramentas para criar e gerenciar **imagens de contĂȘiner** e **contĂȘineres**. + +E existe um Docker Hub pĂșblico com **imagens de contĂȘiner oficiais** prĂ©-prontas para diversas ferramentas, ambientes, bancos de dados e aplicaçÔes. + +Por exemplo, hĂĄ uma Imagem Python oficial. + +E existe muitas outras imagens para diferentes coisas, como bancos de dados, por exemplo: + +* PostgreSQL +* MySQL +* MongoDB +* Redis, etc. + +Usando imagens de contĂȘiner prĂ©-prontas Ă© muito fĂĄcil **combinar** e usar diferentes ferramentas. Por exemplo, para testar um novo banco de dados. Em muitos casos, vocĂȘ pode usar as **imagens oficiais** precisando somente de variĂĄveis de ambiente para configurĂĄ-las. + +Dessa forma, em muitos casos vocĂȘ pode aprender sobre contĂȘineres e Docker e re-usar essa experiĂȘncia com diversos componentes e ferramentas. + +EntĂŁo, vocĂȘ rodaria **vĂĄrios contĂȘineres** com coisas diferentes, como um banco de dados, uma aplicação Python, um servidor web com uma aplicação frontend React, e conectĂĄ-los juntos via sua rede interna. + +Todos os sistemas de gerenciamento de contĂȘineres (como Docker ou Kubernetes) possuem essas funcionalidades de rede integradas a eles. + +## ContĂȘineres e Processos + +Uma **imagem de contĂȘiner** normalmente inclui em seus metadados o programa padrĂŁo ou comando que deve ser executado quando o **contĂȘiner** Ă© iniciado e os parĂąmetros a serem passados para esse programa. Muito similar ao que seria se estivesse na linha de comando. + +Quando um **contĂȘiner** Ă© iniciado, ele irĂĄ rodar esse comando/programa (embora vocĂȘ possa sobrescrevĂȘ-lo e fazer com que ele rode um comando/programa diferente). + +Um contĂȘiner estĂĄ rodando enquanto o **processo principal** (comando ou programa) estiver rodando. + +Um contĂȘiner normalmente tem um **Ășnico processo**, mas tambĂ©m Ă© possĂ­vel iniciar sub-processos a partir do processo principal, e dessa forma vocĂȘ terĂĄ **vĂĄrios processos** no mesmo contĂȘiner. + +Mas nĂŁo Ă© possĂ­vel ter um contĂȘiner rodando sem **pelo menos um processo rodando**. Se o processo principal parar, o contĂȘiner tambĂ©m para. + +## Construindo uma Imagem Docker para FastAPI + +Okay, vamos construir algo agora! 🚀 + +Eu vou mostrar como construir uma **imagem Docker** para FastAPI **do zero**, baseado na **imagem oficial do Python**. + +Isso Ă© o que vocĂȘ quer fazer na **maioria dos casos**, por exemplo: + +* Usando **Kubernetes** ou ferramentas similares +* Quando rodando em uma **Raspberry Pi** +* Usando um serviço em nuvem que irĂĄ rodar uma imagem de contĂȘiner para vocĂȘ, etc. + +### O Pacote Requirements + +VocĂȘ normalmente teria os **requisitos do pacote** para sua aplicação em algum arquivo. + +Isso pode depender principalmente da ferramenta que vocĂȘ usa para **instalar** esses requisitos. + +O caminho mais comum de fazer isso Ă© ter um arquivo `requirements.txt` com os nomes dos pacotes e suas versĂ”es, um por linha. + +VocĂȘ, naturalmente, usaria as mesmas ideias que vocĂȘ leu em [Sobre VersĂ”es do FastAPI](./versions.md){.internal-link target=_blank} para definir os intervalos de versĂ”es. + +Por exemplo, seu `requirements.txt` poderia parecer com: + +``` +fastapi>=0.68.0,<0.69.0 +pydantic>=1.8.0,<2.0.0 +uvicorn>=0.15.0,<0.16.0 +``` + +E vocĂȘ normalmente instalaria essas dependĂȘncias de pacote com `pip`, por exemplo: + +
+ +```console +$ pip install -r requirements.txt +---> 100% +Successfully installed fastapi pydantic uvicorn +``` + +
+ +!!! info + HĂĄ outros formatos e ferramentas para definir e instalar dependĂȘncias de pacote. + + Eu vou mostrar um exemplo depois usando Poetry em uma seção abaixo. 👇 + +### Criando o CĂłdigo do **FastAPI** + +* Crie um diretĂłrio `app` e entre nele. +* Crie um arquivo vazio `__init__.py`. +* Crie um arquivo `main.py` com: + +```Python +from typing import Optional + +from fastapi import FastAPI + +app = FastAPI() + + +@app.get("/") +def read_root(): + return {"Hello": "World"} + + +@app.get("/items/{item_id}") +def read_item(item_id: int, q: Union[str, None] = None): + return {"item_id": item_id, "q": q} +``` + +### Dockerfile + +Agora, no mesmo diretĂłrio do projeto, crie um arquivo `Dockerfile` com: + +```{ .dockerfile .annotate } +# (1) +FROM python:3.9 + +# (2) +WORKDIR /code + +# (3) +COPY ./requirements.txt /code/requirements.txt + +# (4) +RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt + +# (5) +COPY ./app /code/app + +# (6) +CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"] +``` + +1. Inicie a partir da imagem base oficial do Python. + +2. Defina o diretĂłrio de trabalho atual para `/code`. + + Esse Ă© o diretĂłrio onde colocaremos o arquivo `requirements.txt` e o diretĂłrio `app`. + +3. Copie o arquivo com os requisitos para o diretĂłrio `/code`. + + Copie **somente** o arquivo com os requisitos primeiro, nĂŁo o resto do cĂłdigo. + + Como esse arquivo **nĂŁo muda com frequĂȘncia**, o Docker irĂĄ detectĂĄ-lo e usar o **cache** para esse passo, habilitando o cache para o prĂłximo passo tambĂ©m. + +4. Instale as dependĂȘncias de pacote vindas do arquivo de requisitos. + + A opção `--no-cache-dir` diz ao `pip` para nĂŁo salvar os pacotes baixados localmente, pois isso sĂł aconteceria se `pip` fosse executado novamente para instalar os mesmos pacotes, mas esse nĂŁo Ă© o caso quando trabalhamos com contĂȘineres. + + !!! note + `--no-cache-dir` Ă© apenas relacionado ao `pip`, nĂŁo tem nada a ver com Docker ou contĂȘineres. + + A opção `--upgrade` diz ao `pip` para atualizar os pacotes se eles jĂĄ estiverem instalados. + + Por causa do passo anterior de copiar o arquivo, ele pode ser detectado pelo **cache do Docker**, esse passo tambĂ©m **usarĂĄ o cache do Docker** quando disponĂ­vel. + + Usando o cache nesse passo irĂĄ **salvar** muito **tempo** quando vocĂȘ for construir a imagem repetidas vezes durante o desenvolvimento, ao invĂ©s de **baixar e instalar** todas as dependĂȘncias **toda vez**. + +5. Copie o diretĂłrio `./app` dentro do diretĂłrio `/code`. + + Como isso tem todo o cĂłdigo contendo o que **muda com mais frequĂȘncia**, o **cache do Docker** nĂŁo serĂĄ usado para esse passo ou para **qualquer passo seguinte** facilmente. + + EntĂŁo, Ă© importante colocar isso **perto do final** do `Dockerfile`, para otimizar o tempo de construção da imagem do contĂȘiner. + +6. Defina o **comando** para rodar o servidor `uvicorn`. + + `CMD` recebe uma lista de strings, cada uma dessas strings Ă© o que vocĂȘ digitaria na linha de comando separado por espaços. + + Esse comando serĂĄ executado a partir do **diretĂłrio de trabalho atual**, o mesmo diretĂłrio `/code` que vocĂȘ definiu acima com `WORKDIR /code`. + + Porque o programa serĂĄ iniciado em `/code` e dentro dele estĂĄ o diretĂłrio `./app` com seu cĂłdigo, o **Uvicorn** serĂĄ capaz de ver e **importar** `app` de `app.main`. + +!!! tip + Revise o que cada linha faz clicando em cada bolha com o nĂșmero no cĂłdigo. 👆 + +Agora vocĂȘ deve ter uma estrutura de diretĂłrio como: + +``` +. +├── app +│   ├── __init__.py +│ └── main.py +├── Dockerfile +└── requirements.txt +``` + +#### Por TrĂĄs de um Proxy de Terminação TLS + +Se vocĂȘ estĂĄ executando seu contĂȘiner atrĂĄs de um Proxy de Terminação TLS (load balancer) como Nginx ou Traefik, adicione a opção `--proxy-headers`, isso farĂĄ com que o Uvicorn confie nos cabeçalhos enviados por esse proxy, informando que o aplicativo estĂĄ sendo executado atrĂĄs do HTTPS, etc. + +```Dockerfile +CMD ["uvicorn", "app.main:app", "--proxy-headers", "--host", "0.0.0.0", "--port", "80"] +``` + +#### Cache Docker + +Existe um truque importante nesse `Dockerfile`, primeiro copiamos o **arquivo com as dependĂȘncias sozinho**, nĂŁo o resto do cĂłdigo. Deixe-me te contar o porquĂȘ disso. + +```Dockerfile +COPY ./requirements.txt /code/requirements.txt +``` + +Docker e outras ferramentas **constrĂłem** essas imagens de contĂȘiner **incrementalmente**, adicionando **uma camada em cima da outra**, começando do topo do `Dockerfile` e adicionando qualquer arquivo criado por cada uma das instruçÔes do `Dockerfile`. + +Docker e ferramentas similares tambĂ©m usam um **cache interno** ao construir a imagem, se um arquivo nĂŁo mudou desde a Ășltima vez que a imagem do contĂȘiner foi construĂ­da, entĂŁo ele irĂĄ **reutilizar a mesma camada** criada na Ășltima vez, ao invĂ©s de copiar o arquivo novamente e criar uma nova camada do zero. + +Somente evitar a cĂłpia de arquivos nĂŁo melhora muito as coisas, mas porque ele usou o cache para esse passo, ele pode **usar o cache para o prĂłximo passo**. Por exemplo, ele pode usar o cache para a instrução que instala as dependĂȘncias com: + +```Dockerfile +RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt +``` + +O arquivo com os requisitos de pacote **nĂŁo muda com frequĂȘncia**. EntĂŁo, ao copiar apenas esse arquivo, o Docker serĂĄ capaz de **usar o cache** para esse passo. + +E entĂŁo, o Docker serĂĄ capaz de **usar o cache para o prĂłximo passo** que baixa e instala essas dependĂȘncias. E Ă© aqui que **salvamos muito tempo**. ✹ ...e evitamos tĂ©dio esperando. đŸ˜Ș😆 + +Baixar e instalar as dependĂȘncias do pacote **pode levar minutos**, mas usando o **cache** leva **segundos** no mĂĄximo. + +E como vocĂȘ estaria construindo a imagem do contĂȘiner novamente e novamente durante o desenvolvimento para verificar se suas alteraçÔes de cĂłdigo estĂŁo funcionando, hĂĄ muito tempo acumulado que isso economizaria. + +A partir daĂ­, perto do final do `Dockerfile`, copiamos todo o cĂłdigo. Como isso Ă© o que **muda com mais frequĂȘncia**, colocamos perto do final, porque quase sempre, qualquer coisa depois desse passo nĂŁo serĂĄ capaz de usar o cache. + +```Dockerfile +COPY ./app /code/app +``` + +### Construindo a Imagem Docker + +Agora que todos os arquivos estĂŁo no lugar, vamos construir a imagem do contĂȘiner. + +* VĂĄ para o diretĂłrio do projeto (onde estĂĄ o seu `Dockerfile`, contendo o diretĂłrio `app`). +* Construa sua imagem FastAPI: + +
+ +```console +$ docker build -t myimage . + +---> 100% +``` + +
+ +!!! tip + Note o `.` no final, Ă© equivalente a `./`, ele diz ao Docker o diretĂłrio a ser usado para construir a imagem do contĂȘiner. + + Nesse caso, Ă© o mesmo diretĂłrio atual (`.`). + +### Inicie o contĂȘiner Docker + +* Execute um contĂȘiner baseado na sua imagem: + +
+ +```console +$ docker run -d --name mycontĂȘiner -p 80:80 myimage +``` + +
+ +## Verifique + +VocĂȘ deve ser capaz de verificar isso no URL do seu contĂȘiner Docker, por exemplo: http://192.168.99.100/items/5?q=somequery ou http://127.0.0.1/items/5?q=somequery (ou equivalente, usando seu host Docker). + +VocĂȘ verĂĄ algo como: + +```JSON +{"item_id": 5, "q": "somequery"} +``` + +## Documentação interativa da API + +Agora vocĂȘ pode ir para http://192.168.99.100/docs ou http://127.0.0.1/docs (ou equivalente, usando seu host Docker). + +VocĂȘ verĂĄ a documentação interativa automĂĄtica da API (fornecida pelo Swagger UI): + +![Swagger UI](https://fastapi.tiangolo.com/img/index/index-01-swagger-ui-simple.png) + +## Documentação alternativa da API + +E vocĂȘ tambĂ©m pode ir para http://192.168.99.100/redoc ou http://127.0.0.1/redoc (ou equivalente, usando seu host Docker). + +VocĂȘ verĂĄ a documentação alternativa automĂĄtica (fornecida pela ReDoc): + +![ReDoc](https://fastapi.tiangolo.com/img/index/index-02-redoc-simple.png) + +## Construindo uma Imagem Docker com um Arquivo Único FastAPI + +Se seu FastAPI for um Ășnico arquivo, por exemplo, `main.py` sem um diretĂłrio `./app`, sua estrutura de arquivos poderia ser assim: + +``` +. +├── Dockerfile +├── main.py +└── requirements.txt +``` + +EntĂŁo vocĂȘ sĂł teria que alterar os caminhos correspondentes para copiar o arquivo dentro do `Dockerfile`: + +```{ .dockerfile .annotate hl_lines="10 13" } +FROM python:3.9 + +WORKDIR /code + +COPY ./requirements.txt /code/requirements.txt + +RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt + +# (1) +COPY ./main.py /code/ + +# (2) +CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "80"] +``` + +1. Copie o arquivo `main.py` para o diretĂłrio `/code` diretamente (sem nenhum diretĂłrio `./app`). + +2. Execute o Uvicorn e diga a ele para importar o objeto `app` de `main` (em vez de importar de `app.main`). + +EntĂŁo ajuste o comando Uvicorn para usar o novo mĂłdulo `main` em vez de `app.main` para importar o objeto FastAPI `app`. + +## Conceitos de Implantação + +Vamos falar novamente sobre alguns dos mesmos [Conceitos de Implantação](./concepts.md){.internal-link target=_blank} em termos de contĂȘineres. + +ContĂȘineres sĂŁo principalmente uma ferramenta para simplificar o processo de **construção e implantação** de um aplicativo, mas eles nĂŁo impĂ”em uma abordagem particular para lidar com esses **conceitos de implantação** e existem vĂĄrias estratĂ©gias possĂ­veis. + +A **boa notĂ­cia** Ă© que com cada estratĂ©gia diferente hĂĄ uma maneira de cobrir todos os conceitos de implantação. 🎉 + +Vamos revisar esses **conceitos de implantação** em termos de contĂȘineres: + +* HTTPS +* Executando na inicialização +* ReinicializaçÔes +* Replicação (nĂșmero de processos rodando) +* MemĂłria +* Passos anteriores antes de começar + +## HTTPS + +Se nos concentrarmos apenas na **imagem do contĂȘiner** para um aplicativo FastAPI (e posteriormente no **contĂȘiner** em execução), o HTTPS normalmente seria tratado **externamente** por outra ferramenta. + +Isso poderia ser outro contĂȘiner, por exemplo, com Traefik, lidando com **HTTPS** e aquisição **automĂĄtica** de **certificados**. + +!!! tip + Traefik tem integraçÔes com Docker, Kubernetes e outros, portanto, Ă© muito fĂĄcil configurar e configurar o HTTPS para seus contĂȘineres com ele. + +Alternativamente, o HTTPS poderia ser tratado por um provedor de nuvem como um de seus serviços (enquanto ainda executasse o aplicativo em um contĂȘiner). + +## Executando na inicialização e reinicializaçÔes + +Normalmente, outra ferramenta Ă© responsĂĄvel por **iniciar e executar** seu contĂȘiner. + +Ela poderia ser o **Docker** diretamente, **Docker Compose**, **Kubernetes**, um **serviço de nuvem**, etc. + +Na maioria (ou em todos) os casos, hĂĄ uma opção simples para habilitar a execução do contĂȘiner na inicialização e habilitar reinicializaçÔes em falhas. Por exemplo, no Docker, Ă© a opção de linha de comando `--restart`. + +Sem usar contĂȘineres, fazer aplicativos executarem na inicialização e com reinicializaçÔes pode ser trabalhoso e difĂ­cil. Mas quando **trabalhando com contĂȘineres** em muitos casos essa funcionalidade Ă© incluĂ­da por padrĂŁo. ✹ + +## Replicação - NĂșmero de Processos + +Se vocĂȘ tiver um cluster de mĂĄquinas com **Kubernetes**, Docker Swarm Mode, Nomad ou outro sistema complexo semelhante para gerenciar contĂȘineres distribuĂ­dos em vĂĄrias mĂĄquinas, entĂŁo provavelmente desejarĂĄ **lidar com a replicação** no **nĂ­vel do cluster** em vez de usar um **gerenciador de processos** (como o Gunicorn com workers) em cada contĂȘiner. + +Um desses sistemas de gerenciamento de contĂȘineres distribuĂ­dos como o Kubernetes normalmente tem alguma maneira integrada de lidar com a **replicação de contĂȘineres** enquanto ainda oferece **balanceamento de carga** para as solicitaçÔes recebidas. Tudo no **nĂ­vel do cluster**. + +Nesses casos, vocĂȘ provavelmente desejarĂĄ criar uma **imagem do contĂȘiner do zero** como [explicado acima](#dockerfile), instalando suas dependĂȘncias e executando **um Ășnico processo Uvicorn** em vez de executar algo como Gunicorn com trabalhadores Uvicorn. + +### Balanceamento de Carga + +Quando usando contĂȘineres, normalmente vocĂȘ terĂĄ algum componente **escutando na porta principal**. Poderia ser outro contĂȘiner que tambĂ©m Ă© um **Proxy de Terminação TLS** para lidar com **HTTPS** ou alguma ferramenta semelhante. + +Como esse componente assumiria a **carga** de solicitaçÔes e distribuiria isso entre os trabalhadores de uma maneira (esperançosamente) **balanceada**, ele tambĂ©m Ă© comumente chamado de **Balanceador de Carga**. + +!!! tip + O mesmo componente **Proxy de Terminação TLS** usado para HTTPS provavelmente tambĂ©m seria um **Balanceador de Carga**. + +E quando trabalhar com contĂȘineres, o mesmo sistema que vocĂȘ usa para iniciar e gerenciĂĄ-los jĂĄ terĂĄ ferramentas internas para transmitir a **comunicação de rede** (por exemplo, solicitaçÔes HTTP) do **balanceador de carga** (que tambĂ©m pode ser um **Proxy de Terminação TLS**) para o(s) contĂȘiner(es) com seu aplicativo. + +### Um Balanceador de Carga - MĂșltiplos ContĂȘineres de Workers + +Quando trabalhando com **Kubernetes** ou sistemas similares de gerenciamento de contĂȘiner distribuĂ­do, usando seus mecanismos de rede internos permitiria que o Ășnico **balanceador de carga** que estivesse escutando na **porta principal** transmitisse comunicação (solicitaçÔes) para possivelmente **mĂșltiplos contĂȘineres** executando seu aplicativo. + +Cada um desses contĂȘineres executando seu aplicativo normalmente teria **apenas um processo** (ex.: um processo Uvicorn executando seu aplicativo FastAPI). Todos seriam **contĂȘineres idĂȘnticos**, executando a mesma coisa, mas cada um com seu prĂłprio processo, memĂłria, etc. Dessa forma, vocĂȘ aproveitaria a **paralelização** em **nĂșcleos diferentes** da CPU, ou atĂ© mesmo em **mĂĄquinas diferentes**. + +E o sistema de contĂȘiner com o **balanceador de carga** iria **distribuir as solicitaçÔes** para cada um dos contĂȘineres com seu aplicativo **em turnos**. Portanto, cada solicitação poderia ser tratada por um dos mĂșltiplos **contĂȘineres replicados** executando seu aplicativo. + +E normalmente esse **balanceador de carga** seria capaz de lidar com solicitaçÔes que vĂŁo para *outros* aplicativos em seu cluster (por exemplo, para um domĂ­nio diferente, ou sob um prefixo de URL diferente), e transmitiria essa comunicação para os contĂȘineres certos para *esse outro* aplicativo em execução em seu cluster. + +### Um Processo por ContĂȘiner + +Nesse tipo de cenĂĄrio, provavelmente vocĂȘ desejarĂĄ ter **um Ășnico processo (Uvicorn) por contĂȘiner**, pois jĂĄ estaria lidando com a replicação no nĂ­vel do cluster. + +EntĂŁo, nesse caso, vocĂȘ **nĂŁo** desejarĂĄ ter um gerenciador de processos como o Gunicorn com trabalhadores Uvicorn, ou o Uvicorn usando seus prĂłprios trabalhadores Uvicorn. VocĂȘ desejarĂĄ ter apenas um **Ășnico processo Uvicorn** por contĂȘiner (mas provavelmente vĂĄrios contĂȘineres). + +Tendo outro gerenciador de processos dentro do contĂȘiner (como seria com o Gunicorn ou o Uvicorn gerenciando trabalhadores Uvicorn) sĂł adicionaria **complexidade desnecessĂĄria** que vocĂȘ provavelmente jĂĄ estĂĄ cuidando com seu sistema de cluster. + +### ContĂȘineres com MĂșltiplos Processos e Casos Especiais + +Claro, existem **casos especiais** em que vocĂȘ pode querer ter um **contĂȘiner** com um **gerenciador de processos Gunicorn** iniciando vĂĄrios **processos trabalhadores Uvicorn** dentro. + +Nesses casos, vocĂȘ pode usar a **imagem oficial do Docker** que inclui o **Gunicorn** como um gerenciador de processos executando vĂĄrios **processos trabalhadores Uvicorn**, e algumas configuraçÔes padrĂŁo para ajustar o nĂșmero de trabalhadores com base nos atuais nĂșcleos da CPU automaticamente. Eu vou te contar mais sobre isso abaixo em [Imagem Oficial do Docker com Gunicorn - Uvicorn](#imagem-oficial-do-docker-com-gunicorn-uvicorn). + +Aqui estĂŁo alguns exemplos de quando isso pode fazer sentido: + +#### Um Aplicativo Simples + +VocĂȘ pode querer um gerenciador de processos no contĂȘiner se seu aplicativo for **simples o suficiente** para que vocĂȘ nĂŁo precise (pelo menos nĂŁo agora) ajustar muito o nĂșmero de processos, e vocĂȘ pode simplesmente usar um padrĂŁo automatizado (com a imagem oficial do Docker), e vocĂȘ estĂĄ executando em um **Ășnico servidor**, nĂŁo em um cluster. + +#### Docker Compose + +VocĂȘ pode estar implantando em um **Ășnico servidor** (nĂŁo em um cluster) com o **Docker Compose**, entĂŁo vocĂȘ nĂŁo teria uma maneira fĂĄcil de gerenciar a replicação de contĂȘineres (com o Docker Compose) enquanto preserva a rede compartilhada e o **balanceamento de carga**. + +EntĂŁo vocĂȘ pode querer ter **um Ășnico contĂȘiner** com um **gerenciador de processos** iniciando **vĂĄrios processos trabalhadores** dentro. + +#### Prometheus and Outros Motivos + +VocĂȘ tambĂ©m pode ter **outros motivos** que tornariam mais fĂĄcil ter um **Ășnico contĂȘiner** com **mĂșltiplos processos** em vez de ter **mĂșltiplos contĂȘineres** com **um Ășnico processo** em cada um deles. + +Por exemplo (dependendo de sua configuração), vocĂȘ poderia ter alguma ferramenta como um exportador do Prometheus no mesmo contĂȘiner que deve ter acesso a **cada uma das solicitaçÔes** que chegam. + +Nesse caso, se vocĂȘ tivesse **mĂșltiplos contĂȘineres**, por padrĂŁo, quando o Prometheus fosse **ler as mĂ©tricas**, ele receberia as mĂ©tricas de **um Ășnico contĂȘiner cada vez** (para o contĂȘiner que tratou essa solicitação especĂ­fica), em vez de receber as **mĂ©tricas acumuladas** de todos os contĂȘineres replicados. + +EntĂŁo, nesse caso, poderia ser mais simples ter **um Ășnico contĂȘiner** com **mĂșltiplos processos**, e uma ferramenta local (por exemplo, um exportador do Prometheus) no mesmo contĂȘiner coletando mĂ©tricas do Prometheus para todos os processos internos e expor essas mĂ©tricas no Ășnico contĂȘiner. + +--- + +O ponto principal Ă© que **nenhum** desses sĂŁo **regras escritas em pedra** que vocĂȘ deve seguir cegamente. VocĂȘ pode usar essas idĂ©ias para **avaliar seu prĂłprio caso de uso** e decidir qual Ă© a melhor abordagem para seu sistema, verificando como gerenciar os conceitos de: + +* Segurança - HTTPS +* Executando na inicialização +* ReinicializaçÔes +* Replicação (o nĂșmero de processos em execução) +* MemĂłria +* Passos anteriores antes de inicializar + +## MemĂłria + +Se vocĂȘ executar **um Ășnico processo por contĂȘiner**, terĂĄ uma quantidade mais ou menos bem definida, estĂĄvel e limitada de memĂłria consumida por cada um desses contĂȘineres (mais de um se eles forem replicados). + +E entĂŁo vocĂȘ pode definir esses mesmos limites e requisitos de memĂłria em suas configuraçÔes para seu sistema de gerenciamento de contĂȘineres (por exemplo, no **Kubernetes**). Dessa forma, ele poderĂĄ **replicar os contĂȘineres** nas **mĂĄquinas disponĂ­veis** levando em consideração a quantidade de memĂłria necessĂĄria por eles e a quantidade disponĂ­vel nas mĂĄquinas no cluster. + +Se sua aplicação for **simples**, isso provavelmente **nĂŁo serĂĄ um problema**, e vocĂȘ pode nĂŁo precisar especificar limites de memĂłria rĂ­gidos. Mas se vocĂȘ estiver **usando muita memĂłria** (por exemplo, com **modelos de aprendizado de mĂĄquina**), deve verificar quanta memĂłria estĂĄ consumindo e ajustar o **nĂșmero de contĂȘineres** que executa em **cada mĂĄquina** (e talvez adicionar mais mĂĄquinas ao seu cluster). + +Se vocĂȘ executar **mĂșltiplos processos por contĂȘiner** (por exemplo, com a imagem oficial do Docker), deve garantir que o nĂșmero de processos iniciados nĂŁo **consuma mais memĂłria** do que o disponĂ­vel. + +## Passos anteriores antes de inicializar e contĂȘineres + +Se vocĂȘ estiver usando contĂȘineres (por exemplo, Docker, Kubernetes), existem duas abordagens principais que vocĂȘ pode usar. + +### ContĂȘineres MĂșltiplos + +Se vocĂȘ tiver **mĂșltiplos contĂȘineres**, provavelmente cada um executando um **Ășnico processo** (por exemplo, em um cluster do **Kubernetes**), entĂŁo provavelmente vocĂȘ gostaria de ter um **contĂȘiner separado** fazendo o trabalho dos **passos anteriores** em um Ășnico contĂȘiner, executando um Ășnico processo, **antes** de executar os contĂȘineres trabalhadores replicados. + +!!! info + Se vocĂȘ estiver usando o Kubernetes, provavelmente serĂĄ um Init Container. + +Se no seu caso de uso nĂŁo houver problema em executar esses passos anteriores **em paralelo vĂĄrias vezes** (por exemplo, se vocĂȘ nĂŁo estiver executando migraçÔes de banco de dados, mas apenas verificando se o banco de dados estĂĄ pronto), entĂŁo vocĂȘ tambĂ©m pode colocĂĄ-los em cada contĂȘiner logo antes de iniciar o processo principal. + +### ContĂȘiner Único + +Se vocĂȘ tiver uma configuração simples, com um **Ășnico contĂȘiner** que entĂŁo inicia vĂĄrios **processos trabalhadores** (ou tambĂ©m apenas um processo), entĂŁo poderia executar esses passos anteriores no mesmo contĂȘiner, logo antes de iniciar o processo com o aplicativo. A imagem oficial do Docker suporta isso internamente. + +## Imagem Oficial do Docker com Gunicorn - Uvicorn + +HĂĄ uma imagem oficial do Docker que inclui o Gunicorn executando com trabalhadores Uvicorn, conforme detalhado em um capĂ­tulo anterior: [Server Workers - Gunicorn com Uvicorn](./server-workers.md){.internal-link target=_blank}. + +Essa imagem seria Ăștil principalmente nas situaçÔes descritas acima em: [ContĂȘineres com MĂșltiplos Processos e Casos Especiais](#contĂȘineres-com-mĂșltiplos-processos-e-casos-Especiais). + +* tiangolo/uvicorn-gunicorn-fastapi. + +!!! warning + Existe uma grande chance de que vocĂȘ **nĂŁo** precise dessa imagem base ou de qualquer outra semelhante, e seria melhor construir a imagem do zero, como [descrito acima em: Construa uma Imagem Docker para o FastAPI](#construa-uma-imagem-docker-para-o-fastapi). + +Essa imagem tem um mecanismo de **auto-ajuste** incluĂ­do para definir o **nĂșmero de processos trabalhadores** com base nos nĂșcleos de CPU disponĂ­veis. + +Isso tem **padrĂ”es sensĂ­veis**, mas vocĂȘ ainda pode alterar e atualizar todas as configuraçÔes com **variĂĄveis de ambiente** ou arquivos de configuração. + +HĂĄ tambĂ©m suporte para executar **passos anteriores antes de iniciar** com um script. + +!!! tip + Para ver todas as configuraçÔes e opçÔes, vĂĄ para a pĂĄgina da imagem Docker: tiangolo/uvicorn-gunicorn-fastapi. + +### NĂșmero de Processos na Imagem Oficial do Docker + +O **nĂșmero de processos** nesta imagem Ă© **calculado automaticamente** a partir dos **nĂșcleos de CPU** disponĂ­veis. + +Isso significa que ele tentarĂĄ **aproveitar** o mĂĄximo de **desempenho** da CPU possĂ­vel. + +VocĂȘ tambĂ©m pode ajustĂĄ-lo com as configuraçÔes usando **variĂĄveis de ambiente**, etc. + +Mas isso tambĂ©m significa que, como o nĂșmero de processos depende da CPU do contĂȘiner em execução, a **quantidade de memĂłria consumida** tambĂ©m dependerĂĄ disso. + +EntĂŁo, se seu aplicativo consumir muito memĂłria (por exemplo, com modelos de aprendizado de mĂĄquina), e seu servidor tiver muitos nĂșcleos de CPU **mas pouca memĂłria**, entĂŁo seu contĂȘiner pode acabar tentando usar mais memĂłria do que estĂĄ disponĂ­vel e degradar o desempenho muito (ou atĂ© mesmo travar). 🚹 + +### Criando um `Dockerfile` + +Aqui estĂĄ como vocĂȘ criaria um `Dockerfile` baseado nessa imagem: + +```Dockerfile +FROM tiangolo/uvicorn-gunicorn-fastapi:python3.9 + +COPY ./requirements.txt /app/requirements.txt + +RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt + +COPY ./app /app +``` + +### AplicaçÔes Maiores + +Se vocĂȘ seguiu a seção sobre a criação de [AplicaçÔes Maiores com MĂșltiplos Arquivos](../tutorial/bigger-applications.md){.internal-link target=_blank}, seu `Dockerfile` pode parecer com isso: + +```Dockerfile + +```Dockerfile hl_lines="7" +FROM tiangolo/uvicorn-gunicorn-fastapi:python3.9 + +COPY ./requirements.txt /app/requirements.txt + +RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt + +COPY ./app /app/app +``` + +### Quando Usar + +VocĂȘ provavelmente **nĂŁo** deve usar essa imagem base oficial (ou qualquer outra semelhante) se estiver usando **Kubernetes** (ou outros) e jĂĄ estiver definindo **replicação** no nĂ­vel do cluster, com vĂĄrios **contĂȘineres**. Nesses casos, Ă© melhor **construir uma imagem do zero** conforme descrito acima: [Construindo uma Imagem Docker para FastAPI](#construindo-uma-imagem-docker-para-fastapi). + +Essa imagem seria Ăștil principalmente nos casos especiais descritos acima em [ContĂȘineres com MĂșltiplos Processos e Casos Especiais](#contĂȘineres-com-mĂșltiplos-processos-e-casos-Especiais). Por exemplo, se sua aplicação for **simples o suficiente** para que a configuração padrĂŁo de nĂșmero de processos com base na CPU funcione bem, vocĂȘ nĂŁo quer se preocupar com a configuração manual da replicação no nĂ­vel do cluster e nĂŁo estĂĄ executando mais de um contĂȘiner com seu aplicativo. Ou se vocĂȘ estiver implantando com **Docker Compose**, executando em um Ășnico servidor, etc. + +## Deploy da Imagem do ContĂȘiner + +Depois de ter uma imagem de contĂȘiner (Docker), existem vĂĄrias maneiras de implantĂĄ-la. + +Por exemplo: + +* Com **Docker Compose** em um Ășnico servidor +* Com um cluster **Kubernetes** +* Com um cluster Docker Swarm Mode +* Com outra ferramenta como o Nomad +* Com um serviço de nuvem que pega sua imagem de contĂȘiner e a implanta + +## Imagem Docker com Poetry + +Se vocĂȘ usa Poetry para gerenciar as dependĂȘncias do seu projeto, pode usar a construção multi-estĂĄgio do Docker: + +```{ .dockerfile .annotate } +# (1) +FROM python:3.9 as requirements-stage + +# (2) +WORKDIR /tmp + +# (3) +RUN pip install poetry + +# (4) +COPY ./pyproject.toml ./poetry.lock* /tmp/ + +# (5) +RUN poetry export -f requirements.txt --output requirements.txt --without-hashes + +# (6) +FROM python:3.9 + +# (7) +WORKDIR /code + +# (8) +COPY --from=requirements-stage /tmp/requirements.txt /code/requirements.txt + +# (9) +RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt + +# (10) +COPY ./app /code/app + +# (11) +CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"] +``` + +1. Esse Ă© o primeiro estĂĄgio, ele Ă© chamado `requirements-stage`. + +2. Defina `/tmp` como o diretĂłrio de trabalho atual. + + Aqui Ă© onde geraremos o arquivo `requirements.txt` + +3. Instale o Poetry nesse estĂĄgio do Docker. + +4. Copie os arquivos `pyproject.toml` e `poetry.lock` para o diretĂłrio `/tmp`. + + Porque estĂĄ usando `./poetry.lock*` (terminando com um `*`), nĂŁo irĂĄ falhar se esse arquivo ainda nĂŁo estiver disponĂ­vel. + +5. Gere o arquivo `requirements.txt`. + +6. Este Ă© o estĂĄgio final, tudo aqui serĂĄ preservado na imagem final do contĂȘiner. + +7. Defina o diretĂłrio de trabalho atual como `/code`. + +8. Copie o arquivo `requirements.txt` para o diretĂłrio `/code`. + + Essse arquivo sĂł existe no estĂĄgio anterior do Docker, Ă© por isso que usamos `--from-requirements-stage` para copiĂĄ-lo. + +9. Instale as dependĂȘncias de pacote do arquivo `requirements.txt` gerado. + +10. Copie o diretĂłrio `app` para o diretĂłrio `/code`. + +11. Execute o comando `uvicorn`, informando-o para usar o objeto `app` importado de `app.main`. + +!!! tip + Clique nos nĂșmeros das bolhas para ver o que cada linha faz. + +Um **estĂĄgio do Docker** Ă© uma parte de um `Dockerfile` que funciona como uma **imagem temporĂĄria do contĂȘiner** que sĂł Ă© usada para gerar alguns arquivos para serem usados posteriormente. + +O primeiro estĂĄgio serĂĄ usado apenas para **instalar Poetry** e para **gerar o `requirements.txt`** com as dependĂȘncias do seu projeto a partir do arquivo `pyproject.toml` do Poetry. + +Esse arquivo `requirements.txt` serĂĄ usado com `pip` mais tarde no **prĂłximo estĂĄgio**. + +Na imagem final do contĂȘiner, **somente o estĂĄgio final** Ă© preservado. Os estĂĄgios anteriores serĂŁo descartados. + +Quando usar Poetry, faz sentido usar **construçÔes multi-estĂĄgio do Docker** porque vocĂȘ realmente nĂŁo precisa ter o Poetry e suas dependĂȘncias instaladas na imagem final do contĂȘiner, vocĂȘ **apenas precisa** ter o arquivo `requirements.txt` gerado para instalar as dependĂȘncias do seu projeto. + +EntĂŁo, no prĂłximo (e Ășltimo) estĂĄgio, vocĂȘ construiria a imagem mais ou menos da mesma maneira descrita anteriormente. + +### Por trĂĄs de um proxy de terminação TLS - Poetry + +Novamente, se vocĂȘ estiver executando seu contĂȘiner atrĂĄs de um proxy de terminação TLS (balanceador de carga) como Nginx ou Traefik, adicione a opção `--proxy-headers` ao comando: + +```Dockerfile +CMD ["uvicorn", "app.main:app", "--proxy-headers", "--host", "0.0.0.0", "--port", "80"] +``` + +## Recapitulando + +Usando sistemas de contĂȘiner (por exemplo, com **Docker** e **Kubernetes**), torna-se bastante simples lidar com todos os **conceitos de implantação**: + +* HTTPS +* Executando na inicialização +* ReinĂ­cios +* Replicação (o nĂșmero de processos rodando) +* MemĂłria +* Passos anteriores antes de inicializar + +Na maioria dos casos, vocĂȘ provavelmente nĂŁo desejarĂĄ usar nenhuma imagem base e, em vez disso, **construir uma imagem de contĂȘiner do zero** baseada na imagem oficial do Docker Python. + +Tendo cuidado com a **ordem** das instruçÔes no `Dockerfile` e o **cache do Docker**, vocĂȘ pode **minimizar os tempos de construção**, para maximizar sua produtividade (e evitar a tĂ©dio). 😎 + +Em alguns casos especiais, vocĂȘ pode querer usar a imagem oficial do Docker para o FastAPI. đŸ€“ diff --git a/docs/pt/mkdocs.yml b/docs/pt/mkdocs.yml index fdef810fa..0858de062 100644 --- a/docs/pt/mkdocs.yml +++ b/docs/pt/mkdocs.yml @@ -87,6 +87,7 @@ nav: - deployment/versions.md - deployment/https.md - deployment/deta.md + - deployment/docker.md - alternatives.md - history-design-future.md - external-links.md diff --git a/fastapi/__init__.py b/fastapi/__init__.py index afdc94874..037d9804b 100644 --- a/fastapi/__init__.py +++ b/fastapi/__init__.py @@ -1,6 +1,6 @@ """FastAPI framework, high performance, easy to learn, fast to code, ready for production""" -__version__ = "0.87.0" +__version__ = "0.88.0" from starlette import status as status diff --git a/fastapi/dependencies/utils.py b/fastapi/dependencies/utils.py index 67c2dab7f..b2aff9438 100644 --- a/fastapi/dependencies/utils.py +++ b/fastapi/dependencies/utils.py @@ -105,10 +105,10 @@ def check_file_field(field: ModelField) -> None: assert parse_options_header except ImportError: logger.error(multipart_incorrect_install_error) - raise RuntimeError(multipart_incorrect_install_error) + raise RuntimeError(multipart_incorrect_install_error) from None except ImportError: logger.error(multipart_not_installed_error) - raise RuntimeError(multipart_not_installed_error) + raise RuntimeError(multipart_not_installed_error) from None def get_param_sub_dependant( diff --git a/fastapi/encoders.py b/fastapi/encoders.py index 6bde9f4ab..2f95bcbf6 100644 --- a/fastapi/encoders.py +++ b/fastapi/encoders.py @@ -157,7 +157,7 @@ def jsonable_encoder( data = vars(obj) except Exception as e: errors.append(e) - raise ValueError(errors) + raise ValueError(errors) from e return jsonable_encoder( data, include=include, diff --git a/fastapi/utils.py b/fastapi/utils.py index b94dacecc..b15f6a2cf 100644 --- a/fastapi/utils.py +++ b/fastapi/utils.py @@ -89,7 +89,7 @@ def create_response_field( except RuntimeError: raise fastapi.exceptions.FastAPIError( f"Invalid args for response field! Hint: check that {type_} is a valid pydantic field type" - ) + ) from None def create_cloned_field( diff --git a/pyproject.toml b/pyproject.toml index 9549cc47d..be3080ae8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,7 +39,7 @@ classifiers = [ "Topic :: Internet :: WWW/HTTP", ] dependencies = [ - "starlette==0.21.0", + "starlette==0.22.0", "pydantic >=1.6.2,!=1.7,!=1.7.1,!=1.7.2,!=1.7.3,!=1.8,!=1.8.1,<2.0.0", ] dynamic = ["version"] @@ -53,7 +53,7 @@ test = [ "pytest >=7.1.3,<8.0.0", "coverage[toml] >= 6.5.0,<7.0", "mypy ==0.982", - "ruff ==0.0.114", + "ruff ==0.0.138", "black == 22.10.0", "isort >=5.0.6,<6.0.0", "httpx >=0.23.0,<0.24.0", @@ -83,11 +83,11 @@ doc = [ "mkdocs-markdownextradata-plugin >=0.1.7,<0.3.0", # TODO: upgrade and enable typer-cli once it supports Click 8.x.x # "typer-cli >=0.0.12,<0.0.13", - "typer[all] >=0.6.1,<0.7.0", + "typer[all] >=0.6.1,<0.8.0", "pyyaml >=5.3.1,<7.0.0", ] dev = [ - "ruff ==0.0.114", + "ruff ==0.0.138", "uvicorn[standard] >=0.12.0,<0.19.0", "pre-commit >=2.17.0,<3.0.0", ] @@ -168,6 +168,7 @@ select = [ ignore = [ "E501", # line too long, handled by black "B008", # do not perform function calls in argument defaults + "C901", # too complex ] [tool.ruff.per-file-ignores] @@ -178,7 +179,8 @@ ignore = [ "docs_src/dependencies/tutorial010.py" = ["F821"] "docs_src/custom_response/tutorial007.py" = ["B007"] "docs_src/dataclasses/tutorial003.py" = ["I001"] - +"docs_src/path_operation_advanced_configuration/tutorial007.py" = ["B904"] +"docs_src/custom_request_and_route/tutorial002.py" = ["B904"] [tool.ruff.isort] known-third-party = ["fastapi", "pydantic", "starlette"] diff --git a/scripts/docs.py b/scripts/docs.py index 622ba9202..e0953b8ed 100644 --- a/scripts/docs.py +++ b/scripts/docs.py @@ -284,7 +284,9 @@ def build_all(): continue langs.append(lang.name) cpu_count = os.cpu_count() or 1 - with Pool(cpu_count * 2) as p: + process_pool_size = cpu_count * 4 + typer.echo(f"Using process pool size: {process_pool_size}") + with Pool(process_pool_size) as p: p.map(build_lang, langs) diff --git a/scripts/zip-docs.sh b/scripts/zip-docs.sh index f2b7ba3be..69315f5dd 100644 --- a/scripts/zip-docs.sh +++ b/scripts/zip-docs.sh @@ -3,7 +3,9 @@ set -x set -e +cd ./site + if [ -f docs.zip ]; then rm -rf docs.zip fi -zip -r docs.zip ./site +zip -r docs.zip ./ diff --git a/tests/test_starlette_urlconvertors.py b/tests/test_starlette_urlconvertors.py index 5a980cbf6..5ef1b819c 100644 --- a/tests/test_starlette_urlconvertors.py +++ b/tests/test_starlette_urlconvertors.py @@ -1,4 +1,4 @@ -from fastapi import FastAPI, Path +from fastapi import FastAPI, Path, Query from fastapi.testclient import TestClient app = FastAPI() @@ -19,6 +19,11 @@ def path_convertor(param: str = Path()): return {"path": param} +@app.get("/query/") +def query_convertor(param: str = Query()): + return {"query": param} + + client = TestClient(app) @@ -45,6 +50,13 @@ def test_route_converters_path(): assert response.json() == {"path": "some/example"} +def test_route_converters_query(): + # Test query conversion + response = client.get("/query", params={"param": "QuĂ© tal!"}) + assert response.status_code == 200, response.text + assert response.json() == {"query": "QuĂ© tal!"} + + def test_url_path_for_path_convertor(): assert ( app.url_path_for("path_convertor", param="some/example") == "/path/some/example"