├── .editorconfig ├── .github ├── .stale.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── config.yml │ ├── feature_request.md │ └── question.md ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yml ├── release-drafter.yml └── workflows │ ├── build.yml │ ├── publish-to-pypi.yml │ └── release-drafter.yml ├── .gitignore ├── .pre-commit-config.yaml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── Makefile ├── README.md ├── SECURITY.md ├── cookiecutter-config-file.yml ├── poetry.lock ├── pyproject.toml ├── pytest_fastapi_deps └── __init__.py ├── setup.cfg └── tests ├── conftest.py ├── examples ├── __init__.py └── test_public_api.py └── test_pytest_fastapi_deps.py /.editorconfig: -------------------------------------------------------------------------------- 1 | # Check http://editorconfig.org for more information 2 | # This is the main config file for this project: 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | end_of_line = lf 8 | insert_final_newline = true 9 | indent_style = space 10 | indent_size = 2 11 | trim_trailing_whitespace = true 12 | 13 | [*.{py, pyi}] 14 | indent_style = space 15 | indent_size = 4 16 | 17 | [Makefile] 18 | indent_style = tab 19 | 20 | [*.md] 21 | trim_trailing_whitespace = false 22 | 23 | [*.{diff,patch}] 24 | trim_trailing_whitespace = false 25 | -------------------------------------------------------------------------------- /.github/.stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 60 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 7 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - pinned 8 | - security 9 | # Label to use when marking an issue as stale 10 | staleLabel: wontfix 11 | # Comment to post when marking an issue as stale. Set to `false` to disable 12 | markComment: > 13 | This issue has been automatically marked as stale because it has not had 14 | recent activity. It will be closed if no further activity occurs. Thank you 15 | for your contributions. 16 | # Comment to post when closing a stale issue. Set to `false` to disable 17 | closeComment: false 18 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🐛 Bug report 3 | about: If something isn't working 🔧 4 | title: '' 5 | labels: bug 6 | assignees: 7 | --- 8 | 9 | ## 🐛 Bug Report 10 | 11 | 12 | 13 | ## 🔬 How To Reproduce 14 | 15 | Steps to reproduce the behavior: 16 | 17 | 1. ... 18 | 19 | ### Code sample 20 | 21 | 22 | 23 | ### Environment 24 | 25 | * OS: [e.g. Linux / Windows / macOS] 26 | * Python version, get it with: 27 | 28 | ```bash 29 | python --version 30 | ``` 31 | 32 | ### Screenshots 33 | 34 | 35 | 36 | ## 📈 Expected behavior 37 | 38 | 39 | 40 | ## 📎 Additional context 41 | 42 | 43 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | # Configuration: https://help.github.com/en/github/building-a-strong-community/configuring-issue-templates-for-your-repository 2 | 3 | blank_issues_enabled: false 4 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🚀 Feature request 3 | about: Suggest an idea for this project 🏖 4 | title: '' 5 | labels: enhancement 6 | assignees: 7 | --- 8 | 9 | ## 🚀 Feature Request 10 | 11 | 12 | 13 | ## 🔈 Motivation 14 | 15 | 16 | 17 | ## 🛰 Alternatives 18 | 19 | 20 | 21 | ## 📎 Additional context 22 | 23 | 24 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: ❓ Question 3 | about: Ask a question about this project 🎓 4 | title: '' 5 | labels: question 6 | assignees: 7 | --- 8 | 9 | ## Checklist 10 | 11 | 12 | 13 | - [ ] I've searched the project's [`issues`](https://github.com/pksol/pytest-fastapi-deps/issues?q=is%3Aissue). 14 | 15 | ## ❓ Question 16 | 17 | 18 | 19 | How can I [...]? 20 | 21 | Is it possible to [...]? 22 | 23 | ## 📎 Additional context 24 | 25 | 26 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | 4 | 5 | ## Related Issue 6 | 7 | 8 | 9 | ## Type of Change 10 | 11 | 12 | 13 | - [ ] 📚 Examples / docs / tutorials / dependencies update 14 | - [ ] 🔧 Bug fix (non-breaking change which fixes an issue) 15 | - [ ] 🥂 Improvement (non-breaking change which improves an existing feature) 16 | - [ ] 🚀 New feature (non-breaking change which adds functionality) 17 | - [ ] 💥 Breaking change (fix or feature that would cause existing functionality to change) 18 | - [ ] 🔐 Security fix 19 | 20 | ## Checklist 21 | 22 | 23 | 24 | - [ ] I've read the [`CODE_OF_CONDUCT.md`](https://github.com/pksol/pytest-fastapi-deps/blob/master/CODE_OF_CONDUCT.md) document. 25 | - [ ] I've read the [`CONTRIBUTING.md`](https://github.com/pksol/pytest-fastapi-deps/blob/master/CONTRIBUTING.md) guide. 26 | - [ ] I've updated the code style using `make codestyle`. 27 | - [ ] I've written tests for all new methods and classes that I created. 28 | - [ ] I've written the docstring in Google format for all the methods and classes that I used. 29 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Configuration: https://dependabot.com/docs/config-file/ 2 | # Docs: https://docs.github.com/en/github/administering-a-repository/keeping-your-dependencies-updated-automatically 3 | 4 | version: 2 5 | 6 | updates: 7 | - package-ecosystem: "pip" 8 | directory: "/" 9 | schedule: 10 | interval: "monthly" 11 | allow: 12 | - dependency-type: "all" 13 | commit-message: 14 | prefix: ":arrow_up:" 15 | open-pull-requests-limit: 50 16 | 17 | - package-ecosystem: "github-actions" 18 | directory: "/" 19 | schedule: 20 | interval: "monthly" 21 | allow: 22 | - dependency-type: "all" 23 | commit-message: 24 | prefix: ":arrow_up:" 25 | open-pull-requests-limit: 50 26 | -------------------------------------------------------------------------------- /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | # Release drafter configuration https://github.com/release-drafter/release-drafter#configuration 2 | # Emojis were chosen to match the https://gitmoji.carloscuesta.me/ 3 | 4 | name-template: "v$NEXT_PATCH_VERSION" 5 | tag-template: "v$NEXT_PATCH_VERSION" 6 | 7 | categories: 8 | - title: ":rocket: Features" 9 | labels: [enhancement, feature] 10 | - title: ":wrench: Fixes & Refactoring" 11 | labels: [bug, refactoring, bugfix, fix] 12 | - title: ":package: Build System & CI/CD" 13 | labels: [build, ci, testing] 14 | - title: ":boom: Breaking Changes" 15 | labels: [breaking] 16 | - title: ":pencil: Documentation" 17 | labels: [documentation] 18 | - title: ":arrow_up: Dependencies updates" 19 | labels: [dependencies] 20 | 21 | template: | 22 | ## What’s Changed 23 | 24 | $CHANGES 25 | 26 | ## :busts_in_silhouette: List of contributors 27 | 28 | $CONTRIBUTORS 29 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | strategy: 9 | matrix: 10 | python-version: ["3.7", "3.8", "3.9", "3.10"] 11 | 12 | steps: 13 | - uses: actions/checkout@v3 14 | - name: Set up Python ${{ matrix.python-version }} 15 | uses: actions/setup-python@v4 16 | with: 17 | python-version: ${{ matrix.python-version }} 18 | 19 | - name: Install poetry 20 | run: make poetry-download 21 | 22 | - name: Set up cache 23 | uses: actions/cache@v3.0.8 24 | with: 25 | path: .venv 26 | key: venv-${{ matrix.python-version }}-${{ hashFiles('pyproject.toml') }}-${{ hashFiles('poetry.lock') }} 27 | - name: Install dependencies 28 | run: | 29 | poetry config virtualenvs.in-project true 30 | poetry install 31 | 32 | - name: Run style checks 33 | run: | 34 | make check-codestyle 35 | 36 | - name: Run tests 37 | run: | 38 | make test 39 | 40 | - name: Run safety checks 41 | run: | 42 | make check-safety 43 | -------------------------------------------------------------------------------- /.github/workflows/publish-to-pypi.yml: -------------------------------------------------------------------------------- 1 | name: Publish python distribution to PyPI 2 | on: push 3 | 4 | jobs: 5 | build-and-publish-test: 6 | name: Build and publish python distribution to test PyPI 7 | runs-on: ubuntu-18.04 8 | if: "!(startsWith(github.event.ref, 'refs/tags') || github.ref == 'refs/heads/main')" 9 | continue-on-error: true 10 | steps: 11 | - name: Checkout Code 12 | uses: actions/checkout@v3 13 | - name: Set up Python 3.8 14 | uses: actions/setup-python@v4 15 | with: 16 | python-version: 3.8 17 | - name: Setup pip 18 | run: python -m pip install pip==21.0.1 19 | - name: Build and publish to pypi 20 | uses: JRubics/poetry-publish@v1.10 21 | with: 22 | python_version: '3.8.10' 23 | poetry_version: "==1.1.4" 24 | repository_name: 'pytest_fastapi_deps' 25 | repository_url: 'https://test.pypi.org/legacy/' 26 | pypi_token: ${{ secrets.test_pypi_password }} 27 | continue-on-error: true 28 | - name: Sleep to allow pypi index to update with the new version 29 | run: sleep 5m 30 | - name: Install poetry 31 | run: make poetry-download 32 | - name: Cleanup 33 | run: | 34 | rm -r pytest_fastapi_deps 35 | pip uninstall pytest-fastapi-deps 36 | - name: Perform System tests 37 | run: | 38 | poetry export --dev --without-hashes -f requirements.txt --output test-requirements.txt 39 | pip install -r test-requirements.txt 40 | pip install -i https://test.pypi.org/simple/ pytest-fastapi-deps 41 | python -m pytest -vvv -k "test_public_api" 42 | export INSTALLED=`pip freeze | grep pytest-fastapi-deps` 43 | echo "installed is: $INSTALLED, and we care about ${INSTALLED:21}" 44 | export EXPECTED=`poetry version --short` 45 | echo "expected is: $EXPECTED" 46 | if [ "${INSTALLED:21}" != "$EXPECTED" ]; then exit 1; fi 47 | 48 | build-and-publish-production: 49 | name: Build and publish python distribution to production PyPI 50 | runs-on: ubuntu-18.04 51 | if: startsWith(github.event.ref, 'refs/tags') 52 | steps: 53 | - name: Checkout Code 54 | uses: actions/checkout@v3 55 | - name: Set up Python 3.8 56 | uses: actions/setup-python@v4 57 | with: 58 | python-version: 3.8 59 | - name: Setup pip 60 | run: python -m pip install pip==21.0.1 61 | - name: Build and publish to pypi 62 | uses: JRubics/poetry-publish@v1.10 63 | with: 64 | python_version: '3.8.10' 65 | poetry_version: "==1.1.4" 66 | repository_name: 'pytest_fastapi_deps' 67 | pypi_token: ${{ secrets.prod_pypi_password }} 68 | continue-on-error: true 69 | - name: Sleep to allow pypi index to update with the new version 70 | run: sleep 5m 71 | - name: Install poetry 72 | run: make poetry-download 73 | - name: Cleanup 74 | run: | 75 | rm -r pytest_fastapi_deps 76 | pip uninstall pytest-fastapi-deps 77 | - name: Perform System tests 78 | run: | 79 | poetry export --dev --without-hashes -f requirements.txt --output test-requirements.txt 80 | pip install -r test-requirements.txt 81 | pip install pytest-fastapi-deps 82 | python -m pytest -vvv -k "test_public_api" 83 | export INSTALLED=`pip freeze | grep pytest-fastapi-deps` 84 | echo "installed is: $INSTALLED, and we care about ${INSTALLED:21}" 85 | export EXPECTED=`poetry version --short` 86 | echo "expected is: $EXPECTED" 87 | if [ "${INSTALLED:21}" != "$EXPECTED" ]; then exit 1; fi 88 | -------------------------------------------------------------------------------- /.github/workflows/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name: Release Drafter 2 | 3 | on: 4 | push: 5 | # branches to consider in the event; optional, defaults to all 6 | branches: 7 | - main 8 | 9 | jobs: 10 | update_release_draft: 11 | runs-on: ubuntu-latest 12 | steps: 13 | # Drafts your next Release notes as Pull Requests are merged into "master" 14 | - uses: release-drafter/release-drafter@v5.20.1 15 | env: 16 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/osx,python,pycharm,windows,visualstudio,visualstudiocode 3 | # Edit at https://www.gitignore.io/?templates=osx,python,pycharm,windows,visualstudio,visualstudiocode 4 | 5 | ### OSX ### 6 | # General 7 | .DS_Store 8 | .AppleDouble 9 | .LSOverride 10 | 11 | # Icon must end with two \r 12 | Icon 13 | 14 | # Thumbnails 15 | ._* 16 | 17 | # Files that might appear in the root of a volume 18 | .DocumentRevisions-V100 19 | .fseventsd 20 | .Spotlight-V100 21 | .TemporaryItems 22 | .Trashes 23 | .VolumeIcon.icns 24 | .com.apple.timemachine.donotpresent 25 | 26 | # Directories potentially created on remote AFP share 27 | .AppleDB 28 | .AppleDesktop 29 | Network Trash Folder 30 | Temporary Items 31 | .apdisk 32 | 33 | ### PyCharm ### 34 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 35 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 36 | 37 | # Pycharm 38 | .idea/ 39 | 40 | # User-specific stuff 41 | .idea/**/workspace.xml 42 | .idea/**/tasks.xml 43 | .idea/**/usage.statistics.xml 44 | .idea/**/dictionaries 45 | .idea/**/shelf 46 | 47 | # Generated files 48 | .idea/**/contentModel.xml 49 | 50 | # Sensitive or high-churn files 51 | .idea/**/dataSources/ 52 | .idea/**/dataSources.ids 53 | .idea/**/dataSources.local.xml 54 | .idea/**/sqlDataSources.xml 55 | .idea/**/dynamic.xml 56 | .idea/**/uiDesigner.xml 57 | .idea/**/dbnavigator.xml 58 | 59 | # Gradle 60 | .idea/**/gradle.xml 61 | .idea/**/libraries 62 | 63 | # Gradle and Maven with auto-import 64 | # When using Gradle or Maven with auto-import, you should exclude module files, 65 | # since they will be recreated, and may cause churn. Uncomment if using 66 | # auto-import. 67 | # .idea/modules.xml 68 | # .idea/*.iml 69 | # .idea/modules 70 | # *.iml 71 | # *.ipr 72 | 73 | # CMake 74 | cmake-build-*/ 75 | 76 | # Mongo Explorer plugin 77 | .idea/**/mongoSettings.xml 78 | 79 | # File-based project format 80 | *.iws 81 | 82 | # IntelliJ 83 | out/ 84 | 85 | # mpeltonen/sbt-idea plugin 86 | .idea_modules/ 87 | 88 | # JIRA plugin 89 | atlassian-ide-plugin.xml 90 | 91 | # Cursive Clojure plugin 92 | .idea/replstate.xml 93 | 94 | # Crashlytics plugin (for Android Studio and IntelliJ) 95 | com_crashlytics_export_strings.xml 96 | crashlytics.properties 97 | crashlytics-build.properties 98 | fabric.properties 99 | 100 | # Editor-based Rest Client 101 | .idea/httpRequests 102 | 103 | # Android studio 3.1+ serialized cache file 104 | .idea/caches/build_file_checksums.ser 105 | 106 | ### PyCharm Patch ### 107 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 108 | 109 | # *.iml 110 | # modules.xml 111 | # .idea/misc.xml 112 | # *.ipr 113 | 114 | # Sonarlint plugin 115 | .idea/**/sonarlint/ 116 | 117 | # SonarQube Plugin 118 | .idea/**/sonarIssues.xml 119 | 120 | # Markdown Navigator plugin 121 | .idea/**/markdown-navigator.xml 122 | .idea/**/markdown-navigator/ 123 | 124 | ### Python ### 125 | # Byte-compiled / optimized / DLL files 126 | __pycache__/ 127 | *.py[cod] 128 | *$py.class 129 | requirements.txt 130 | 131 | # C extensions 132 | *.so 133 | 134 | # Distribution / packaging 135 | .Python 136 | build/ 137 | develop-eggs/ 138 | dist/ 139 | downloads/ 140 | eggs/ 141 | .eggs/ 142 | lib/ 143 | lib64/ 144 | parts/ 145 | sdist/ 146 | var/ 147 | wheels/ 148 | pip-wheel-metadata/ 149 | share/python-wheels/ 150 | *.egg-info/ 151 | .installed.cfg 152 | *.egg 153 | MANIFEST 154 | 155 | # PyInstaller 156 | # Usually these files are written by a python script from a template 157 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 158 | *.manifest 159 | *.spec 160 | 161 | # Installer logs 162 | pip-log.txt 163 | pip-delete-this-directory.txt 164 | 165 | # Unit test / coverage reports 166 | htmlcov/ 167 | .tox/ 168 | .nox/ 169 | .coverage 170 | .coverage.* 171 | .cache 172 | nosetests.xml 173 | coverage.xml 174 | *.cover 175 | .hypothesis/ 176 | .pytest_cache/ 177 | 178 | # Translations 179 | *.mo 180 | *.pot 181 | 182 | # Scrapy stuff: 183 | .scrapy 184 | 185 | # Sphinx documentation 186 | docs/_build/ 187 | 188 | # PyBuilder 189 | target/ 190 | 191 | # pyenv 192 | .python-version 193 | 194 | # poetry 195 | .venv 196 | 197 | # pipenv 198 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 199 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 200 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 201 | # install all needed dependencies. 202 | #Pipfile.lock 203 | 204 | # celery beat schedule file 205 | celerybeat-schedule 206 | 207 | # SageMath parsed files 208 | *.sage.py 209 | 210 | # Spyder project settings 211 | .spyderproject 212 | .spyproject 213 | 214 | # Rope project settings 215 | .ropeproject 216 | 217 | # Mr Developer 218 | .mr.developer.cfg 219 | .project 220 | .pydevproject 221 | 222 | # mkdocs documentation 223 | /site 224 | 225 | # mypy 226 | .mypy_cache/ 227 | .dmypy.json 228 | dmypy.json 229 | 230 | # Pyre type checker 231 | .pyre/ 232 | 233 | # Plugins 234 | .secrets.baseline 235 | 236 | ### VisualStudioCode ### 237 | .vscode/* 238 | !.vscode/tasks.json 239 | !.vscode/launch.json 240 | !.vscode/extensions.json 241 | 242 | ### VisualStudioCode Patch ### 243 | # Ignore all local history of files 244 | .history 245 | 246 | ### Windows ### 247 | # Windows thumbnail cache files 248 | Thumbs.db 249 | Thumbs.db:encryptable 250 | ehthumbs.db 251 | ehthumbs_vista.db 252 | 253 | # Dump file 254 | *.stackdump 255 | 256 | # Folder config file 257 | [Dd]esktop.ini 258 | 259 | # Recycle Bin used on file shares 260 | $RECYCLE.BIN/ 261 | 262 | # Windows Installer files 263 | *.cab 264 | *.msi 265 | *.msix 266 | *.msm 267 | *.msp 268 | 269 | # Windows shortcuts 270 | *.lnk 271 | 272 | ### VisualStudio ### 273 | ## Ignore Visual Studio temporary files, build results, and 274 | ## files generated by popular Visual Studio add-ons. 275 | ## 276 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 277 | 278 | # User-specific files 279 | *.rsuser 280 | *.suo 281 | *.user 282 | *.userosscache 283 | *.sln.docstates 284 | 285 | # User-specific files (MonoDevelop/Xamarin Studio) 286 | *.userprefs 287 | 288 | # Mono auto generated files 289 | mono_crash.* 290 | 291 | # Build results 292 | [Dd]ebug/ 293 | [Dd]ebugPublic/ 294 | [Rr]elease/ 295 | [Rr]eleases/ 296 | x64/ 297 | x86/ 298 | [Aa][Rr][Mm]/ 299 | [Aa][Rr][Mm]64/ 300 | bld/ 301 | [Bb]in/ 302 | [Oo]bj/ 303 | [Ll]og/ 304 | 305 | # Visual Studio 2015/2017 cache/options directory 306 | .vs/ 307 | # Uncomment if you have tasks that create the project's static files in wwwroot 308 | #wwwroot/ 309 | 310 | # Visual Studio 2017 auto generated files 311 | Generated\ Files/ 312 | 313 | # MSTest test Results 314 | [Tt]est[Rr]esult*/ 315 | [Bb]uild[Ll]og.* 316 | 317 | # NUnit 318 | *.VisualState.xml 319 | TestResult.xml 320 | nunit-*.xml 321 | 322 | # Build Results of an ATL Project 323 | [Dd]ebugPS/ 324 | [Rr]eleasePS/ 325 | dlldata.c 326 | 327 | # Benchmark Results 328 | BenchmarkDotNet.Artifacts/ 329 | 330 | # .NET Core 331 | project.lock.json 332 | project.fragment.lock.json 333 | artifacts/ 334 | 335 | # StyleCop 336 | StyleCopReport.xml 337 | 338 | # Files built by Visual Studio 339 | *_i.c 340 | *_p.c 341 | *_h.h 342 | *.ilk 343 | *.obj 344 | *.iobj 345 | *.pch 346 | *.pdb 347 | *.ipdb 348 | *.pgc 349 | *.pgd 350 | *.rsp 351 | *.sbr 352 | *.tlb 353 | *.tli 354 | *.tlh 355 | *.tmp 356 | *.tmp_proj 357 | *_wpftmp.csproj 358 | *.log 359 | *.vspscc 360 | *.vssscc 361 | .builds 362 | *.pidb 363 | *.svclog 364 | *.scc 365 | 366 | # Chutzpah Test files 367 | _Chutzpah* 368 | 369 | # Visual C++ cache files 370 | ipch/ 371 | *.aps 372 | *.ncb 373 | *.opendb 374 | *.opensdf 375 | *.sdf 376 | *.cachefile 377 | *.VC.db 378 | *.VC.VC.opendb 379 | 380 | # Visual Studio profiler 381 | *.psess 382 | *.vsp 383 | *.vspx 384 | *.sap 385 | 386 | # Visual Studio Trace Files 387 | *.e2e 388 | 389 | # TFS 2012 Local Workspace 390 | $tf/ 391 | 392 | # Guidance Automation Toolkit 393 | *.gpState 394 | 395 | # ReSharper is a .NET coding add-in 396 | _ReSharper*/ 397 | *.[Rr]e[Ss]harper 398 | *.DotSettings.user 399 | 400 | # JustCode is a .NET coding add-in 401 | .JustCode 402 | 403 | # TeamCity is a build add-in 404 | _TeamCity* 405 | 406 | # DotCover is a Code Coverage Tool 407 | *.dotCover 408 | 409 | # AxoCover is a Code Coverage Tool 410 | .axoCover/* 411 | !.axoCover/settings.json 412 | 413 | # Visual Studio code coverage results 414 | *.coverage 415 | *.coveragexml 416 | 417 | # NCrunch 418 | _NCrunch_* 419 | .*crunch*.local.xml 420 | nCrunchTemp_* 421 | 422 | # MightyMoose 423 | *.mm.* 424 | AutoTest.Net/ 425 | 426 | # Web workbench (sass) 427 | .sass-cache/ 428 | 429 | # Installshield output folder 430 | [Ee]xpress/ 431 | 432 | # DocProject is a documentation generator add-in 433 | DocProject/buildhelp/ 434 | DocProject/Help/*.HxT 435 | DocProject/Help/*.HxC 436 | DocProject/Help/*.hhc 437 | DocProject/Help/*.hhk 438 | DocProject/Help/*.hhp 439 | DocProject/Help/Html2 440 | DocProject/Help/html 441 | 442 | # Click-Once directory 443 | publish/ 444 | 445 | # Publish Web Output 446 | *.[Pp]ublish.xml 447 | *.azurePubxml 448 | # Note: Comment the next line if you want to checkin your web deploy settings, 449 | # but database connection strings (with potential passwords) will be unencrypted 450 | *.pubxml 451 | *.publishproj 452 | 453 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 454 | # checkin your Azure Web App publish settings, but sensitive information contained 455 | # in these scripts will be unencrypted 456 | PublishScripts/ 457 | 458 | # NuGet Packages 459 | *.nupkg 460 | # NuGet Symbol Packages 461 | *.snupkg 462 | # The packages folder can be ignored because of Package Restore 463 | **/[Pp]ackages/* 464 | # except build/, which is used as an MSBuild target. 465 | !**/[Pp]ackages/build/ 466 | # Uncomment if necessary however generally it will be regenerated when needed 467 | #!**/[Pp]ackages/repositories.config 468 | # NuGet v3's project.json files produces more ignorable files 469 | *.nuget.props 470 | *.nuget.targets 471 | 472 | # Microsoft Azure Build Output 473 | csx/ 474 | *.build.csdef 475 | 476 | # Microsoft Azure Emulator 477 | ecf/ 478 | rcf/ 479 | 480 | # Windows Store app package directories and files 481 | AppPackages/ 482 | BundleArtifacts/ 483 | Package.StoreAssociation.xml 484 | _pkginfo.txt 485 | *.appx 486 | *.appxbundle 487 | *.appxupload 488 | 489 | # Visual Studio cache files 490 | # files ending in .cache can be ignored 491 | *.[Cc]ache 492 | # but keep track of directories ending in .cache 493 | !?*.[Cc]ache/ 494 | 495 | # Others 496 | ClientBin/ 497 | ~$* 498 | *~ 499 | *.dbmdl 500 | *.dbproj.schemaview 501 | *.jfm 502 | *.pfx 503 | *.publishsettings 504 | orleans.codegen.cs 505 | 506 | # Including strong name files can present a security risk 507 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 508 | #*.snk 509 | 510 | # Since there are multiple workflows, uncomment next line to ignore bower_components 511 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 512 | #bower_components/ 513 | 514 | # RIA/Silverlight projects 515 | Generated_Code/ 516 | 517 | # Backup & report files from converting an old project file 518 | # to a newer Visual Studio version. Backup files are not needed, 519 | # because we have git ;-) 520 | _UpgradeReport_Files/ 521 | Backup*/ 522 | UpgradeLog*.XML 523 | UpgradeLog*.htm 524 | ServiceFabricBackup/ 525 | *.rptproj.bak 526 | 527 | # SQL Server files 528 | *.mdf 529 | *.ldf 530 | *.ndf 531 | 532 | # Business Intelligence projects 533 | *.rdl.data 534 | *.bim.layout 535 | *.bim_*.settings 536 | *.rptproj.rsuser 537 | *- [Bb]ackup.rdl 538 | *- [Bb]ackup ([0-9]).rdl 539 | *- [Bb]ackup ([0-9][0-9]).rdl 540 | 541 | # Microsoft Fakes 542 | FakesAssemblies/ 543 | 544 | # GhostDoc plugin setting file 545 | *.GhostDoc.xml 546 | 547 | # Node.js Tools for Visual Studio 548 | .ntvs_analysis.dat 549 | node_modules/ 550 | 551 | # Visual Studio 6 build log 552 | *.plg 553 | 554 | # Visual Studio 6 workspace options file 555 | *.opt 556 | 557 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 558 | *.vbw 559 | 560 | # Visual Studio LightSwitch build output 561 | **/*.HTMLClient/GeneratedArtifacts 562 | **/*.DesktopClient/GeneratedArtifacts 563 | **/*.DesktopClient/ModelManifest.xml 564 | **/*.Server/GeneratedArtifacts 565 | **/*.Server/ModelManifest.xml 566 | _Pvt_Extensions 567 | 568 | # Paket dependency manager 569 | .paket/paket.exe 570 | paket-files/ 571 | 572 | # FAKE - F# Make 573 | .fake/ 574 | 575 | # CodeRush personal settings 576 | .cr/personal 577 | 578 | # Python Tools for Visual Studio (PTVS) 579 | *.pyc 580 | 581 | # Cake - Uncomment if you are using it 582 | # tools/** 583 | # !tools/packages.config 584 | 585 | # Tabs Studio 586 | *.tss 587 | 588 | # Telerik's JustMock configuration file 589 | *.jmconfig 590 | 591 | # BizTalk build output 592 | *.btp.cs 593 | *.btm.cs 594 | *.odx.cs 595 | *.xsd.cs 596 | 597 | # OpenCover UI analysis results 598 | OpenCover/ 599 | 600 | # Azure Stream Analytics local run output 601 | ASALocalRun/ 602 | 603 | # MSBuild Binary and Structured Log 604 | *.binlog 605 | 606 | # NVidia Nsight GPU debugger configuration file 607 | *.nvuser 608 | 609 | # MFractors (Xamarin productivity tool) working folder 610 | .mfractor/ 611 | 612 | # Local History for Visual Studio 613 | .localhistory/ 614 | 615 | # BeatPulse healthcheck temp database 616 | healthchecksdb 617 | 618 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 619 | MigrationBackup/ 620 | 621 | # End of https://www.gitignore.io/api/osx,python,pycharm,windows,visualstudio,visualstudiocode 622 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | default_language_version: 2 | python: python3.7 3 | 4 | default_stages: [commit, push] 5 | 6 | repos: 7 | - repo: https://github.com/pre-commit/pre-commit-hooks 8 | rev: v2.5.0 9 | hooks: 10 | - id: check-yaml 11 | - id: end-of-file-fixer 12 | exclude: LICENSE 13 | 14 | - repo: local 15 | hooks: 16 | - id: pyupgrade 17 | name: pyupgrade 18 | entry: poetry run pyupgrade --py37-plus 19 | types: [python] 20 | language: system 21 | 22 | - repo: local 23 | hooks: 24 | - id: isort 25 | name: isort 26 | entry: poetry run isort --settings-path pyproject.toml 27 | types: [python] 28 | language: system 29 | 30 | - repo: local 31 | hooks: 32 | - id: black 33 | name: black 34 | entry: poetry run black --config pyproject.toml 35 | types: [python] 36 | language: system 37 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at kogan.peter@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to contribute 2 | 3 | ## Dependencies 4 | 5 | We use `poetry` to manage the [dependencies](https://github.com/python-poetry/poetry). 6 | If you don't have `poetry`, you should install with `make poetry-download`. 7 | 8 | To install dependencies and prepare [`pre-commit`](https://pre-commit.com/) hooks you would need to run `install` command: 9 | 10 | ```bash 11 | make install 12 | make pre-commit-install 13 | ``` 14 | 15 | To activate your `virtualenv` run `poetry shell`. 16 | 17 | ## Codestyle 18 | 19 | After installation you may execute code formatting. 20 | 21 | ```bash 22 | make codestyle 23 | ``` 24 | 25 | ### Checks 26 | 27 | Many checks are configured for this project. Command `make check-codestyle` will check black, isort and darglint. 28 | The `make check-safety` command will look at the security of your code. 29 | 30 | Command `make lint` applies all checks. 31 | 32 | ### Before submitting 33 | 34 | Before submitting your code please do the following steps: 35 | 36 | 1. Add any changes you want 37 | 1. Add tests for the new changes 38 | 1. Edit documentation if you have changed something significant 39 | 1. Run `make codestyle` to format your changes. 40 | 1. Run `make lint` to ensure that types, security and docstrings are okay. 41 | 42 | 43 | 44 | ### Makefile usage 45 | 46 | [`Makefile`](https://github.com/pksol/pytest-fastapi-deps/blob/master/Makefile) contains a lot of functions for faster development. 47 | 48 |
49 | 1. Download and remove Poetry 50 |

51 | 52 | To download and install Poetry run: 53 | 54 | ```bash 55 | make poetry-download 56 | ``` 57 | 58 | To uninstall 59 | 60 | ```bash 61 | make poetry-remove 62 | ``` 63 | 64 |

65 |
66 | 67 |
68 | 2. Install all dependencies and pre-commit hooks 69 |

70 | 71 | Install requirements: 72 | 73 | ```bash 74 | make install 75 | ``` 76 | 77 | Pre-commit hooks could be installed after `git init` via 78 | 79 | ```bash 80 | make pre-commit-install 81 | ``` 82 | 83 |

84 |
85 | 86 |
87 | 3. Codestyle 88 |

89 | 90 | Automatic formatting uses `pyupgrade`, `isort` and `black`. 91 | 92 | ```bash 93 | make codestyle 94 | 95 | # or use synonym 96 | make formatting 97 | ``` 98 | 99 | Codestyle checks only, without rewriting files: 100 | 101 | ```bash 102 | make check-codestyle 103 | ``` 104 | 105 | > Note: `check-codestyle` uses `isort`, `black` and `darglint` library 106 | 107 | Update all dev libraries to the latest version using one command 108 | 109 | ```bash 110 | make update-dev-deps 111 | ``` 112 | 113 |

114 |
115 | 116 |
117 | 4. Code security 118 |

119 | 120 | ```bash 121 | make check-safety 122 | ``` 123 | 124 | This command launches `Poetry` integrity checks as well as identifies security issues with `Safety` and `Bandit`. 125 | 126 | ```bash 127 | make check-safety 128 | ``` 129 | 130 |

131 |
132 | 133 |
134 | 5. Type checks 135 |

136 | 137 | Run `mypy` static type checker 138 | 139 | ```bash 140 | make mypy 141 | ``` 142 | 143 |

144 |
145 | 146 |
147 | 6. Tests with coverage badges 148 |

149 | 150 | Run `pytest` 151 | 152 | ```bash 153 | make test 154 | ``` 155 | 156 |

157 |
158 | 159 |
160 | 7. All linters 161 |

162 | 163 | Of course there is a command to ~~rule~~ run all linters in one: 164 | 165 | ```bash 166 | make lint 167 | ``` 168 | 169 | the same as: 170 | 171 | ```bash 172 | make test && make check-codestyle && make mypy && make check-safety 173 | ``` 174 | 175 |

176 |
177 | 178 |
179 | 8. Cleanup 180 |

181 | Delete pycache files 182 | 183 | ```bash 184 | make pycache-remove 185 | ``` 186 | 187 | Remove package build 188 | 189 | ```bash 190 | make build-remove 191 | ``` 192 | 193 | Delete .DS_STORE files 194 | 195 | ```bash 196 | make dsstore-remove 197 | ``` 198 | 199 | Remove .mypycache 200 | 201 | ```bash 202 | make mypycache-remove 203 | ``` 204 | 205 | Or to remove all above run: 206 | 207 | ```bash 208 | make cleanup 209 | ``` 210 | 211 |

212 |
213 | 214 | ## Other help 215 | 216 | You can contribute by spreading a word about this library. 217 | It would also be a huge contribution to write 218 | a short article on how you are using this project. 219 | You can also share your best practices with us. 220 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2022 Peter Kogan 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | #* Variables 2 | SHELL := /usr/bin/env bash 3 | PYTHON := python 4 | PYTHONPATH := `pwd` 5 | 6 | #* Poetry 7 | .PHONY: poetry-download 8 | poetry-download: 9 | curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/install-poetry.py | $(PYTHON) - 10 | 11 | .PHONY: poetry-remove 12 | poetry-remove: 13 | curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/install-poetry.py | $(PYTHON) - --uninstall 14 | 15 | #* Installation 16 | .PHONY: install 17 | install: 18 | poetry lock -n && poetry export --without-hashes > requirements.txt 19 | poetry install -n 20 | -poetry run mypy --install-types --non-interactive ./ 21 | 22 | .PHONY: pre-commit-install 23 | pre-commit-install: 24 | poetry run pre-commit install 25 | 26 | #* Formatters 27 | .PHONY: codestyle 28 | codestyle: 29 | poetry run pyupgrade --exit-zero-even-if-changed --py37-plus **/*.py 30 | poetry run isort --settings-path pyproject.toml ./ 31 | poetry run black --config pyproject.toml ./ 32 | 33 | .PHONY: formatting 34 | formatting: codestyle 35 | 36 | #* Linting 37 | .PHONY: test 38 | test: 39 | PYTHONPATH=$(PYTHONPATH) poetry run pytest 40 | 41 | .PHONY: check-codestyle 42 | check-codestyle: 43 | poetry run isort --diff --check-only --settings-path pyproject.toml ./ 44 | poetry run black --diff --check --config pyproject.toml ./ 45 | poetry run darglint --verbosity 2 pytest_fastapi_deps tests 46 | 47 | .PHONY: mypy 48 | mypy: 49 | poetry run mypy --config-file pyproject.toml ./ 50 | 51 | .PHONY: check-safety 52 | check-safety: 53 | poetry check 54 | poetry run safety check --full-report 55 | poetry run bandit -ll --recursive pytest_fastapi_deps tests 56 | 57 | .PHONY: lint 58 | lint: test check-codestyle mypy check-safety 59 | 60 | .PHONY: update-dev-deps 61 | update-dev-deps: 62 | poetry add -D bandit@latest darglint@latest "isort[colors]@latest" mypy@latest pre-commit@latest pydocstyle@latest pylint@latest pytest@latest pyupgrade@latest safety@latest coverage@latest coverage-badge@latest pytest-html@latest pytest-cov@latest 63 | poetry add -D --allow-prereleases black@latest 64 | 65 | #* Cleaning 66 | .PHONY: pycache-remove 67 | pycache-remove: 68 | find . | grep -E "(__pycache__|\.pyc|\.pyo$$)" | xargs rm -rf 69 | 70 | .PHONY: dsstore-remove 71 | dsstore-remove: 72 | find . | grep -E ".DS_Store" | xargs rm -rf 73 | 74 | .PHONY: mypycache-remove 75 | mypycache-remove: 76 | find . | grep -E ".mypy_cache" | xargs rm -rf 77 | 78 | .PHONY: ipynbcheckpoints-remove 79 | ipynbcheckpoints-remove: 80 | find . | grep -E ".ipynb_checkpoints" | xargs rm -rf 81 | 82 | .PHONY: pytestcache-remove 83 | pytestcache-remove: 84 | find . | grep -E ".pytest_cache" | xargs rm -rf 85 | 86 | .PHONY: build-remove 87 | build-remove: 88 | rm -rf build/ 89 | 90 | .PHONY: cleanup 91 | cleanup: pycache-remove dsstore-remove mypycache-remove ipynbcheckpoints-remove pytestcache-remove 92 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pytest-fastapi-deps 2 | 3 |
4 | 5 | [![Build status](https://github.com/pksol/pytest-fastapi-deps/workflows/build/badge.svg?branch=master&event=push)](https://github.com/pksol/pytest-fastapi-deps/actions?query=workflow%3Abuild) 6 | [![Python Version](https://img.shields.io/pypi/pyversions/pytest-fastapi-deps.svg)](https://pypi.org/project/pytest-fastapi-deps/) 7 | [![Dependencies Status](https://img.shields.io/badge/dependencies-up%20to%20date-brightgreen.svg)](https://github.com/pksol/pytest-fastapi-deps/pulls?utf8=%E2%9C%93&q=is%3Apr%20author%3Aapp%2Fdependabot) 8 | 9 | [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) 10 | [![Security: bandit](https://img.shields.io/badge/security-bandit-green.svg)](https://github.com/PyCQA/bandit) 11 | [![Pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white)](https://github.com/pksol/pytest-fastapi-deps/blob/master/.pre-commit-config.yaml) 12 | [![Semantic Versions](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--versions-e10079.svg)](https://github.com/pksol/pytest-fastapi-deps/releases) 13 | [![License](https://img.shields.io/github/license/pksol/pytest-fastapi-deps)](https://github.com/pksol/pytest-fastapi-deps/blob/master/LICENSE) 14 | 15 | A fixture which allows easy replacement of fastapi dependencies for testing 16 | 17 |
18 | 19 | ## Installation 20 | 21 | ```bash 22 | pip install pytest-fastapi-deps 23 | ``` 24 | 25 | or install with `Poetry` 26 | 27 | ```bash 28 | poetry add pytest-fastapi-deps 29 | ``` 30 | 31 | ## Use case 32 | Suppose that you have this fastapi endpoint which has a couple of dependencies: 33 | ```python 34 | from fastapi import Depends, FastAPI 35 | 36 | app = FastAPI() 37 | 38 | 39 | async def first_dep(): 40 | return {"skip": 0, "limit": 100} 41 | 42 | 43 | def second_dep(): 44 | return {"skip": 20, "limit": 50} 45 | 46 | 47 | @app.get("/depends/") 48 | async def get_depends( 49 | first_dep: dict = Depends(first_dep), second_dep: dict = Depends(second_dep) 50 | ): 51 | return {"first_dep": first_dep, "second_dep": second_dep} 52 | ``` 53 | 54 | For simplicity, this example holds static dictionaries, but in reality these 55 | dependencies can be anything: dynamic configuration, database information, the 56 | current user's information, etc. 57 | 58 | If you want to test your fastapi endpoint you might wish to mock or replace these 59 | dependencies with your test code. 60 | 61 | This is where the `fastapi_dep` fixture comes to play. 62 | 63 | ## Usage 64 | The most basic usage is to replace a dependency with a context manager: 65 | 66 | ```python 67 | from my_project.main import app, first_dep, second_dep 68 | from fastapi.testclient import TestClient 69 | 70 | client = TestClient(app) 71 | 72 | def my_second_override(): 73 | return {"another": "override"} 74 | 75 | 76 | def test_get_override_two_dep(fastapi_dep): 77 | with fastapi_dep(app).override( 78 | { 79 | first_dep: "plain_override_object", 80 | second_dep: my_second_override, 81 | } 82 | ): 83 | response = client.get("/depends") 84 | assert response.status_code == 200 85 | assert response.json() == { 86 | "first_dep": "plain_override_object", 87 | "second_dep": {"another": "override"}, 88 | } 89 | ``` 90 | 91 | Note how easy it is: you add the `fastapi_dep` fixture, initialize it with the fastapi 92 | `app` and send a dictionary of overrides: the keys are the original functions while the 93 | values are plain objects that would be returned or replacement functions that would be 94 | called. 95 | 96 | If your use case is to replace the dependencies for the entire duration of your test, 97 | you can use pytest [indirect parameters](https://docs.pytest.org/en/latest/example/parametrize.html#indirect-parametrization) to simplify the body of your test: 98 | 99 | ```python 100 | import pytest 101 | 102 | from my_project.main import app, first_dep, second_dep 103 | from fastapi.testclient import TestClient 104 | 105 | client = TestClient(app) 106 | 107 | @pytest.mark.parametrize( 108 | "fastapi_dep", 109 | [ 110 | ( 111 | app, 112 | {first_dep: lambda: {"my": "override"}}, 113 | ) 114 | ], 115 | indirect=True, 116 | ) 117 | def test_get_override_indirect_dep_param(fastapi_dep): 118 | response = client.get("/depends") 119 | assert response.status_code == 200 120 | assert response.json() == { 121 | "first_dep": {"my": "override"}, 122 | "second_dep": {"skip": 20, "limit": 50}, 123 | } 124 | ``` 125 | You must use `indirect=True` and pass a tuple where the first item is the `app` and the 126 | second item is the dictionary with replacement functions. 127 | 128 | You can do more fancy stuff and utilize the nature of nested python context managers: 129 | 130 | ```python 131 | from my_project.main import app, first_dep, second_dep 132 | from fastapi.testclient import TestClient 133 | 134 | client = TestClient(app) 135 | 136 | 137 | def test_get_override_dep_inner_context(fastapi_dep): 138 | with fastapi_dep(app).override({first_dep: lambda: {"my": "override"}}): 139 | response = client.get("/depends") 140 | assert response.status_code == 200 141 | assert response.json() == { 142 | "first_dep": {"my": "override"}, # overridden 143 | "second_dep": {"skip": 20, "limit": 50}, # stayed the same 144 | } 145 | 146 | # add another override 147 | with fastapi_dep(app).override({second_dep: lambda: {"another": "override"}}): 148 | response = client.get("/depends") 149 | assert response.status_code == 200 150 | assert response.json() == { 151 | "first_dep": {"my": "override"}, # overridden 152 | "second_dep": {"another": "override"}, # overridden 153 | } 154 | 155 | # second override is gone - expect that only the first is overridden 156 | response = client.get("/depends") 157 | assert response.status_code == 200 158 | assert response.json() == { 159 | "first_dep": {"my": "override"}, # overridden 160 | "second_dep": {"skip": 20, "limit": 50}, # returned to normal behaviour 161 | } 162 | 163 | # back to normal behaviour 164 | response = client.get("/depends") 165 | assert response.status_code == 200 166 | assert response.json() == { 167 | "first_dep": {"skip": 0, "limit": 100}, 168 | "second_dep": {"skip": 20, "limit": 50}, 169 | } 170 | ``` 171 | 172 | ## 📈 Releases 173 | 174 | You can see the list of available releases on the [GitHub Releases](https://github.com/pksol/pytest-fastapi-deps/releases) page. 175 | 176 | We follow [Semantic Versions](https://semver.org/) specification. 177 | 178 | ## 🛡 License 179 | 180 | [![License](https://img.shields.io/github/license/pksol/pytest-fastapi-deps)](https://github.com/pksol/pytest-fastapi-deps/blob/master/LICENSE) 181 | 182 | This project is licensed under the terms of the `MIT` license. See [LICENSE](https://github.com/pksol/pytest-fastapi-deps/blob/master/LICENSE) for more details. 183 | 184 | ## 📃 Citation 185 | 186 | ```bibtex 187 | @misc{pytest-fastapi-deps, 188 | author = {Peter Kogan}, 189 | title = {A fixture which allows easy replacement of fastapi dependencies for testing}, 190 | year = {2022}, 191 | publisher = {GitHub}, 192 | journal = {GitHub repository}, 193 | howpublished = {\url{https://github.com/pksol/pytest-fastapi-deps}} 194 | } 195 | ``` 196 | 197 | ## Credits [![🚀 Your next Python package needs a bleeding-edge project structure.](https://img.shields.io/badge/python--package--template-%F0%9F%9A%80-brightgreen)](https://github.com/TezRomacH/python-package-template) 198 | 199 | This project was generated with [`python-package-template`](https://github.com/TezRomacH/python-package-template) 200 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security 2 | 3 | ## 🔐 Reporting Security Issues 4 | 5 | > Do not open issues that might have security implications! 6 | > It is critical that security related issues are reported privately so we have time to address them before they become public knowledge. 7 | 8 | Vulnerabilities can be reported by emailing core members: 9 | 10 | - Peter Kogan [kogan.peter@gmail.com](mailto:kogan.peter@gmail.com) 11 | 12 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 13 | 14 | - Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 15 | - Full paths of source file(s) related to the manifestation of the issue 16 | - The location of the affected source code (tag/branch/commit or direct URL) 17 | - Any special configuration required to reproduce the issue 18 | - Environment (e.g. Linux / Windows / macOS) 19 | - Step-by-step instructions to reproduce the issue 20 | - Proof-of-concept or exploit code (if possible) 21 | - Impact of the issue, including how an attacker might exploit the issue 22 | 23 | This information will help us triage your report more quickly. 24 | 25 | ## Preferred Languages 26 | 27 | We prefer all communications to be in English. 28 | -------------------------------------------------------------------------------- /cookiecutter-config-file.yml: -------------------------------------------------------------------------------- 1 | # This file contains values from Cookiecutter 2 | 3 | default_context: 4 | project_name: "pytest-fastapi-deps" 5 | project_description: "A fixture which allows easy replacement of fastapi dependencies for testing" 6 | organization: "Peter Kogan" 7 | license: "MIT" 8 | minimal_python_version: 3.7 9 | github_name: "pksol" 10 | email: "kogan.peter@gmail.com" 11 | version: "0.1.0" 12 | line_length: "88" 13 | create_example_template: "none" 14 | -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "anyio" 3 | version = "3.6.1" 4 | description = "High level compatibility layer for multiple asynchronous event loop implementations" 5 | category = "main" 6 | optional = false 7 | python-versions = ">=3.6.2" 8 | 9 | [package.dependencies] 10 | idna = ">=2.8" 11 | sniffio = ">=1.1" 12 | typing-extensions = {version = "*", markers = "python_version < \"3.8\""} 13 | 14 | [package.extras] 15 | doc = ["packaging", "sphinx-rtd-theme", "sphinx-autodoc-typehints (>=1.2.0)"] 16 | test = ["coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "contextlib2", "uvloop (<0.15)", "mock (>=4)", "uvloop (>=0.15)"] 17 | trio = ["trio (>=0.16)"] 18 | 19 | [[package]] 20 | name = "astroid" 21 | version = "2.11.7" 22 | description = "An abstract syntax tree for Python with inference support." 23 | category = "dev" 24 | optional = false 25 | python-versions = ">=3.6.2" 26 | 27 | [package.dependencies] 28 | lazy-object-proxy = ">=1.4.0" 29 | typed-ast = {version = ">=1.4.0,<2.0", markers = "implementation_name == \"cpython\" and python_version < \"3.8\""} 30 | typing-extensions = {version = ">=3.10", markers = "python_version < \"3.10\""} 31 | wrapt = ">=1.11,<2" 32 | 33 | [[package]] 34 | name = "atomicwrites" 35 | version = "1.4.1" 36 | description = "Atomic file writes." 37 | category = "main" 38 | optional = false 39 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 40 | 41 | [[package]] 42 | name = "attrs" 43 | version = "22.1.0" 44 | description = "Classes Without Boilerplate" 45 | category = "main" 46 | optional = false 47 | python-versions = ">=3.5" 48 | 49 | [package.extras] 50 | dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"] 51 | docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] 52 | tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "zope.interface", "cloudpickle"] 53 | tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "cloudpickle"] 54 | 55 | [[package]] 56 | name = "bandit" 57 | version = "1.7.4" 58 | description = "Security oriented static analyser for python code." 59 | category = "dev" 60 | optional = false 61 | python-versions = ">=3.7" 62 | 63 | [package.dependencies] 64 | colorama = {version = ">=0.3.9", markers = "platform_system == \"Windows\""} 65 | GitPython = ">=1.0.1" 66 | PyYAML = ">=5.3.1" 67 | stevedore = ">=1.20.0" 68 | 69 | [package.extras] 70 | test = ["coverage (>=4.5.4)", "fixtures (>=3.0.0)", "flake8 (>=4.0.0)", "stestr (>=2.5.0)", "testscenarios (>=0.5.0)", "testtools (>=2.3.0)", "toml", "beautifulsoup4 (>=4.8.0)", "pylint (==1.9.4)"] 71 | toml = ["toml"] 72 | yaml = ["pyyaml"] 73 | 74 | [[package]] 75 | name = "black" 76 | version = "22.6.0" 77 | description = "The uncompromising code formatter." 78 | category = "dev" 79 | optional = false 80 | python-versions = ">=3.6.2" 81 | 82 | [package.dependencies] 83 | click = ">=8.0.0" 84 | mypy-extensions = ">=0.4.3" 85 | pathspec = ">=0.9.0" 86 | platformdirs = ">=2" 87 | tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""} 88 | typed-ast = {version = ">=1.4.2", markers = "python_version < \"3.8\" and implementation_name == \"cpython\""} 89 | typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} 90 | 91 | [package.extras] 92 | colorama = ["colorama (>=0.4.3)"] 93 | d = ["aiohttp (>=3.7.4)"] 94 | jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] 95 | uvloop = ["uvloop (>=0.15.2)"] 96 | 97 | [[package]] 98 | name = "certifi" 99 | version = "2022.6.15" 100 | description = "Python package for providing Mozilla's CA Bundle." 101 | category = "dev" 102 | optional = false 103 | python-versions = ">=3.6" 104 | 105 | [[package]] 106 | name = "cfgv" 107 | version = "3.3.1" 108 | description = "Validate configuration and produce human readable error messages." 109 | category = "dev" 110 | optional = false 111 | python-versions = ">=3.6.1" 112 | 113 | [[package]] 114 | name = "charset-normalizer" 115 | version = "2.1.0" 116 | description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." 117 | category = "dev" 118 | optional = false 119 | python-versions = ">=3.6.0" 120 | 121 | [package.extras] 122 | unicode_backport = ["unicodedata2"] 123 | 124 | [[package]] 125 | name = "click" 126 | version = "8.1.3" 127 | description = "Composable command line interface toolkit" 128 | category = "dev" 129 | optional = false 130 | python-versions = ">=3.7" 131 | 132 | [package.dependencies] 133 | colorama = {version = "*", markers = "platform_system == \"Windows\""} 134 | importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} 135 | 136 | [[package]] 137 | name = "colorama" 138 | version = "0.4.5" 139 | description = "Cross-platform colored terminal text." 140 | category = "main" 141 | optional = false 142 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 143 | 144 | [[package]] 145 | name = "coverage" 146 | version = "6.4.2" 147 | description = "Code coverage measurement for Python" 148 | category = "dev" 149 | optional = false 150 | python-versions = ">=3.7" 151 | 152 | [package.dependencies] 153 | tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} 154 | 155 | [package.extras] 156 | toml = ["tomli"] 157 | 158 | [[package]] 159 | name = "coverage-badge" 160 | version = "1.1.0" 161 | description = "Generate coverage badges for Coverage.py." 162 | category = "dev" 163 | optional = false 164 | python-versions = "*" 165 | 166 | [package.dependencies] 167 | coverage = "*" 168 | 169 | [[package]] 170 | name = "darglint" 171 | version = "1.8.1" 172 | description = "A utility for ensuring Google-style docstrings stay up to date with the source code." 173 | category = "dev" 174 | optional = false 175 | python-versions = ">=3.6,<4.0" 176 | 177 | [[package]] 178 | name = "dill" 179 | version = "0.3.5.1" 180 | description = "serialize all of python" 181 | category = "dev" 182 | optional = false 183 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*" 184 | 185 | [package.extras] 186 | graph = ["objgraph (>=1.7.2)"] 187 | 188 | [[package]] 189 | name = "distlib" 190 | version = "0.3.5" 191 | description = "Distribution utilities" 192 | category = "dev" 193 | optional = false 194 | python-versions = "*" 195 | 196 | [[package]] 197 | name = "dparse" 198 | version = "0.5.1" 199 | description = "A parser for Python dependency files" 200 | category = "dev" 201 | optional = false 202 | python-versions = ">=3.5" 203 | 204 | [package.dependencies] 205 | packaging = "*" 206 | pyyaml = "*" 207 | toml = "*" 208 | 209 | [package.extras] 210 | pipenv = ["pipenv"] 211 | 212 | [[package]] 213 | name = "fastapi" 214 | version = "0.79.0" 215 | description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" 216 | category = "main" 217 | optional = false 218 | python-versions = ">=3.6.1" 219 | 220 | [package.dependencies] 221 | pydantic = ">=1.6.2,<1.7 || >1.7,<1.7.1 || >1.7.1,<1.7.2 || >1.7.2,<1.7.3 || >1.7.3,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0" 222 | starlette = "0.19.1" 223 | 224 | [package.extras] 225 | all = ["requests (>=2.24.0,<3.0.0)", "jinja2 (>=2.11.2,<4.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "itsdangerous (>=1.1.0,<3.0.0)", "pyyaml (>=5.3.1,<7.0.0)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0,<6.0.0)", "orjson (>=3.2.1,<4.0.0)", "email_validator (>=1.1.1,<2.0.0)", "uvicorn[standard] (>=0.12.0,<0.18.0)"] 226 | dev = ["python-jose[cryptography] (>=3.3.0,<4.0.0)", "passlib[bcrypt] (>=1.7.2,<2.0.0)", "autoflake (>=1.4.0,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "uvicorn[standard] (>=0.12.0,<0.18.0)", "pre-commit (>=2.17.0,<3.0.0)"] 227 | doc = ["mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "mdx-include (>=1.4.1,<2.0.0)", "mkdocs-markdownextradata-plugin (>=0.1.7,<0.3.0)", "typer (>=0.4.1,<0.5.0)", "pyyaml (>=5.3.1,<7.0.0)"] 228 | test = ["pytest (>=6.2.4,<7.0.0)", "pytest-cov (>=2.12.0,<4.0.0)", "mypy (==0.910)", "flake8 (>=3.8.3,<4.0.0)", "black (==22.3.0)", "isort (>=5.0.6,<6.0.0)", "requests (>=2.24.0,<3.0.0)", "httpx (>=0.14.0,<0.19.0)", "email_validator (>=1.1.1,<2.0.0)", "sqlalchemy (>=1.3.18,<1.5.0)", "peewee (>=3.13.3,<4.0.0)", "databases[sqlite] (>=0.3.2,<0.6.0)", "orjson (>=3.2.1,<4.0.0)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0,<6.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "flask (>=1.1.2,<3.0.0)", "anyio[trio] (>=3.2.1,<4.0.0)", "types-ujson (==4.2.1)", "types-orjson (==3.6.2)", "types-dataclasses (==0.6.5)"] 229 | 230 | [[package]] 231 | name = "filelock" 232 | version = "3.7.1" 233 | description = "A platform independent file lock." 234 | category = "dev" 235 | optional = false 236 | python-versions = ">=3.7" 237 | 238 | [package.extras] 239 | docs = ["furo (>=2021.8.17b43)", "sphinx (>=4.1)", "sphinx-autodoc-typehints (>=1.12)"] 240 | testing = ["covdefaults (>=1.2.0)", "coverage (>=4)", "pytest (>=4)", "pytest-cov", "pytest-timeout (>=1.4.2)"] 241 | 242 | [[package]] 243 | name = "gitdb" 244 | version = "4.0.9" 245 | description = "Git Object Database" 246 | category = "dev" 247 | optional = false 248 | python-versions = ">=3.6" 249 | 250 | [package.dependencies] 251 | smmap = ">=3.0.1,<6" 252 | 253 | [[package]] 254 | name = "gitpython" 255 | version = "3.1.27" 256 | description = "GitPython is a python library used to interact with Git repositories" 257 | category = "dev" 258 | optional = false 259 | python-versions = ">=3.7" 260 | 261 | [package.dependencies] 262 | gitdb = ">=4.0.1,<5" 263 | typing-extensions = {version = ">=3.7.4.3", markers = "python_version < \"3.8\""} 264 | 265 | [[package]] 266 | name = "identify" 267 | version = "2.5.2" 268 | description = "File identification library for Python" 269 | category = "dev" 270 | optional = false 271 | python-versions = ">=3.7" 272 | 273 | [package.extras] 274 | license = ["ukkonen"] 275 | 276 | [[package]] 277 | name = "idna" 278 | version = "3.3" 279 | description = "Internationalized Domain Names in Applications (IDNA)" 280 | category = "main" 281 | optional = false 282 | python-versions = ">=3.5" 283 | 284 | [[package]] 285 | name = "importlib-metadata" 286 | version = "4.12.0" 287 | description = "Read metadata from Python packages" 288 | category = "main" 289 | optional = false 290 | python-versions = ">=3.7" 291 | 292 | [package.dependencies] 293 | typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} 294 | zipp = ">=0.5" 295 | 296 | [package.extras] 297 | docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)"] 298 | perf = ["ipython"] 299 | testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.3)", "packaging", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)", "importlib-resources (>=1.3)"] 300 | 301 | [[package]] 302 | name = "iniconfig" 303 | version = "1.1.1" 304 | description = "iniconfig: brain-dead simple config-ini parsing" 305 | category = "main" 306 | optional = false 307 | python-versions = "*" 308 | 309 | [[package]] 310 | name = "isort" 311 | version = "5.10.1" 312 | description = "A Python utility / library to sort Python imports." 313 | category = "dev" 314 | optional = false 315 | python-versions = ">=3.6.1,<4.0" 316 | 317 | [package.dependencies] 318 | colorama = {version = ">=0.4.3,<0.5.0", optional = true, markers = "extra == \"colors\""} 319 | 320 | [package.extras] 321 | pipfile_deprecated_finder = ["pipreqs", "requirementslib"] 322 | requirements_deprecated_finder = ["pipreqs", "pip-api"] 323 | colors = ["colorama (>=0.4.3,<0.5.0)"] 324 | plugins = ["setuptools"] 325 | 326 | [[package]] 327 | name = "lazy-object-proxy" 328 | version = "1.7.1" 329 | description = "A fast and thorough lazy object proxy." 330 | category = "dev" 331 | optional = false 332 | python-versions = ">=3.6" 333 | 334 | [[package]] 335 | name = "mccabe" 336 | version = "0.7.0" 337 | description = "McCabe checker, plugin for flake8" 338 | category = "dev" 339 | optional = false 340 | python-versions = ">=3.6" 341 | 342 | [[package]] 343 | name = "mypy" 344 | version = "0.961" 345 | description = "Optional static typing for Python" 346 | category = "dev" 347 | optional = false 348 | python-versions = ">=3.6" 349 | 350 | [package.dependencies] 351 | mypy-extensions = ">=0.4.3" 352 | tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} 353 | typed-ast = {version = ">=1.4.0,<2", markers = "python_version < \"3.8\""} 354 | typing-extensions = ">=3.10" 355 | 356 | [package.extras] 357 | dmypy = ["psutil (>=4.0)"] 358 | python2 = ["typed-ast (>=1.4.0,<2)"] 359 | reports = ["lxml"] 360 | 361 | [[package]] 362 | name = "mypy-extensions" 363 | version = "0.4.3" 364 | description = "Experimental type system extensions for programs checked with the mypy typechecker." 365 | category = "dev" 366 | optional = false 367 | python-versions = "*" 368 | 369 | [[package]] 370 | name = "nodeenv" 371 | version = "1.7.0" 372 | description = "Node.js virtual environment builder" 373 | category = "dev" 374 | optional = false 375 | python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" 376 | 377 | [[package]] 378 | name = "packaging" 379 | version = "21.3" 380 | description = "Core utilities for Python packages" 381 | category = "main" 382 | optional = false 383 | python-versions = ">=3.6" 384 | 385 | [package.dependencies] 386 | pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" 387 | 388 | [[package]] 389 | name = "pathspec" 390 | version = "0.9.0" 391 | description = "Utility library for gitignore style pattern matching of file paths." 392 | category = "dev" 393 | optional = false 394 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" 395 | 396 | [[package]] 397 | name = "pbr" 398 | version = "5.9.0" 399 | description = "Python Build Reasonableness" 400 | category = "dev" 401 | optional = false 402 | python-versions = ">=2.6" 403 | 404 | [[package]] 405 | name = "platformdirs" 406 | version = "2.5.2" 407 | description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." 408 | category = "dev" 409 | optional = false 410 | python-versions = ">=3.7" 411 | 412 | [package.extras] 413 | docs = ["furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)", "sphinx (>=4)"] 414 | test = ["appdirs (==1.4.4)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)", "pytest (>=6)"] 415 | 416 | [[package]] 417 | name = "pluggy" 418 | version = "1.0.0" 419 | description = "plugin and hook calling mechanisms for python" 420 | category = "main" 421 | optional = false 422 | python-versions = ">=3.6" 423 | 424 | [package.dependencies] 425 | importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} 426 | 427 | [package.extras] 428 | dev = ["pre-commit", "tox"] 429 | testing = ["pytest", "pytest-benchmark"] 430 | 431 | [[package]] 432 | name = "pre-commit" 433 | version = "2.20.0" 434 | description = "A framework for managing and maintaining multi-language pre-commit hooks." 435 | category = "dev" 436 | optional = false 437 | python-versions = ">=3.7" 438 | 439 | [package.dependencies] 440 | cfgv = ">=2.0.0" 441 | identify = ">=1.0.0" 442 | importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} 443 | nodeenv = ">=0.11.1" 444 | pyyaml = ">=5.1" 445 | toml = "*" 446 | virtualenv = ">=20.0.8" 447 | 448 | [[package]] 449 | name = "py" 450 | version = "1.11.0" 451 | description = "library with cross-python path, ini-parsing, io, code, log facilities" 452 | category = "main" 453 | optional = false 454 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 455 | 456 | [[package]] 457 | name = "pydantic" 458 | version = "1.9.1" 459 | description = "Data validation and settings management using python type hints" 460 | category = "main" 461 | optional = false 462 | python-versions = ">=3.6.1" 463 | 464 | [package.dependencies] 465 | typing-extensions = ">=3.7.4.3" 466 | 467 | [package.extras] 468 | dotenv = ["python-dotenv (>=0.10.4)"] 469 | email = ["email-validator (>=1.0.3)"] 470 | 471 | [[package]] 472 | name = "pydocstyle" 473 | version = "6.1.1" 474 | description = "Python docstring style checker" 475 | category = "dev" 476 | optional = false 477 | python-versions = ">=3.6" 478 | 479 | [package.dependencies] 480 | snowballstemmer = "*" 481 | 482 | [package.extras] 483 | toml = ["toml"] 484 | 485 | [[package]] 486 | name = "pylint" 487 | version = "2.13.9" 488 | description = "python code static checker" 489 | category = "dev" 490 | optional = false 491 | python-versions = ">=3.6.2" 492 | 493 | [package.dependencies] 494 | astroid = ">=2.11.5,<=2.12.0-dev0" 495 | colorama = {version = "*", markers = "sys_platform == \"win32\""} 496 | dill = ">=0.2" 497 | isort = ">=4.2.5,<6" 498 | mccabe = ">=0.6,<0.8" 499 | platformdirs = ">=2.2.0" 500 | tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} 501 | typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""} 502 | 503 | [package.extras] 504 | testutil = ["gitpython (>3)"] 505 | 506 | [[package]] 507 | name = "pyparsing" 508 | version = "3.0.9" 509 | description = "pyparsing module - Classes and methods to define and execute parsing grammars" 510 | category = "main" 511 | optional = false 512 | python-versions = ">=3.6.8" 513 | 514 | [package.extras] 515 | diagrams = ["railroad-diagrams", "jinja2"] 516 | 517 | [[package]] 518 | name = "pytest" 519 | version = "7.1.2" 520 | description = "pytest: simple powerful testing with Python" 521 | category = "main" 522 | optional = false 523 | python-versions = ">=3.7" 524 | 525 | [package.dependencies] 526 | atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} 527 | attrs = ">=19.2.0" 528 | colorama = {version = "*", markers = "sys_platform == \"win32\""} 529 | importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} 530 | iniconfig = "*" 531 | packaging = "*" 532 | pluggy = ">=0.12,<2.0" 533 | py = ">=1.8.2" 534 | tomli = ">=1.0.0" 535 | 536 | [package.extras] 537 | testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] 538 | 539 | [[package]] 540 | name = "pytest-cov" 541 | version = "3.0.0" 542 | description = "Pytest plugin for measuring coverage." 543 | category = "dev" 544 | optional = false 545 | python-versions = ">=3.6" 546 | 547 | [package.dependencies] 548 | coverage = {version = ">=5.2.1", extras = ["toml"]} 549 | pytest = ">=4.6" 550 | 551 | [package.extras] 552 | testing = ["fields", "hunter", "process-tests", "six", "pytest-xdist", "virtualenv"] 553 | 554 | [[package]] 555 | name = "pytest-html" 556 | version = "3.1.1" 557 | description = "pytest plugin for generating HTML reports" 558 | category = "dev" 559 | optional = false 560 | python-versions = ">=3.6" 561 | 562 | [package.dependencies] 563 | pytest = ">=5.0,<6.0.0 || >6.0.0" 564 | pytest-metadata = "*" 565 | 566 | [[package]] 567 | name = "pytest-metadata" 568 | version = "2.0.2" 569 | description = "pytest plugin for test session metadata" 570 | category = "dev" 571 | optional = false 572 | python-versions = ">=3.7,<4.0" 573 | 574 | [package.dependencies] 575 | pytest = ">=3.0.0,<8.0.0" 576 | 577 | [[package]] 578 | name = "pyupgrade" 579 | version = "2.37.3" 580 | description = "A tool to automatically upgrade syntax for newer versions." 581 | category = "dev" 582 | optional = false 583 | python-versions = ">=3.7" 584 | 585 | [package.dependencies] 586 | tokenize-rt = ">=3.2.0" 587 | 588 | [[package]] 589 | name = "pyyaml" 590 | version = "6.0" 591 | description = "YAML parser and emitter for Python" 592 | category = "dev" 593 | optional = false 594 | python-versions = ">=3.6" 595 | 596 | [[package]] 597 | name = "requests" 598 | version = "2.28.1" 599 | description = "Python HTTP for Humans." 600 | category = "dev" 601 | optional = false 602 | python-versions = ">=3.7, <4" 603 | 604 | [package.dependencies] 605 | certifi = ">=2017.4.17" 606 | charset-normalizer = ">=2,<3" 607 | idna = ">=2.5,<4" 608 | urllib3 = ">=1.21.1,<1.27" 609 | 610 | [package.extras] 611 | socks = ["PySocks (>=1.5.6,!=1.5.7)"] 612 | use_chardet_on_py3 = ["chardet (>=3.0.2,<6)"] 613 | 614 | [[package]] 615 | name = "ruamel.yaml" 616 | version = "0.17.21" 617 | description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order" 618 | category = "dev" 619 | optional = false 620 | python-versions = ">=3" 621 | 622 | [package.dependencies] 623 | "ruamel.yaml.clib" = {version = ">=0.2.6", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.11\""} 624 | 625 | [package.extras] 626 | docs = ["ryd"] 627 | jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"] 628 | 629 | [[package]] 630 | name = "ruamel.yaml.clib" 631 | version = "0.2.6" 632 | description = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml" 633 | category = "dev" 634 | optional = false 635 | python-versions = ">=3.5" 636 | 637 | [[package]] 638 | name = "safety" 639 | version = "2.1.1" 640 | description = "Checks installed dependencies for known vulnerabilities and licenses." 641 | category = "dev" 642 | optional = false 643 | python-versions = "*" 644 | 645 | [package.dependencies] 646 | Click = ">=8.0.2" 647 | dparse = ">=0.5.1" 648 | packaging = ">=21.0" 649 | requests = "*" 650 | "ruamel.yaml" = ">=0.17.21" 651 | 652 | [[package]] 653 | name = "smmap" 654 | version = "5.0.0" 655 | description = "A pure Python implementation of a sliding window memory map manager" 656 | category = "dev" 657 | optional = false 658 | python-versions = ">=3.6" 659 | 660 | [[package]] 661 | name = "sniffio" 662 | version = "1.2.0" 663 | description = "Sniff out which async library your code is running under" 664 | category = "main" 665 | optional = false 666 | python-versions = ">=3.5" 667 | 668 | [[package]] 669 | name = "snowballstemmer" 670 | version = "2.2.0" 671 | description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." 672 | category = "dev" 673 | optional = false 674 | python-versions = "*" 675 | 676 | [[package]] 677 | name = "starlette" 678 | version = "0.19.1" 679 | description = "The little ASGI library that shines." 680 | category = "main" 681 | optional = false 682 | python-versions = ">=3.6" 683 | 684 | [package.dependencies] 685 | anyio = ">=3.4.0,<5" 686 | typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""} 687 | 688 | [package.extras] 689 | full = ["itsdangerous", "jinja2", "python-multipart", "pyyaml", "requests"] 690 | 691 | [[package]] 692 | name = "stevedore" 693 | version = "3.5.0" 694 | description = "Manage dynamic plugins for Python applications" 695 | category = "dev" 696 | optional = false 697 | python-versions = ">=3.6" 698 | 699 | [package.dependencies] 700 | importlib-metadata = {version = ">=1.7.0", markers = "python_version < \"3.8\""} 701 | pbr = ">=2.0.0,<2.1.0 || >2.1.0" 702 | 703 | [[package]] 704 | name = "tokenize-rt" 705 | version = "4.2.1" 706 | description = "A wrapper around the stdlib `tokenize` which roundtrips." 707 | category = "dev" 708 | optional = false 709 | python-versions = ">=3.6.1" 710 | 711 | [[package]] 712 | name = "toml" 713 | version = "0.10.2" 714 | description = "Python Library for Tom's Obvious, Minimal Language" 715 | category = "dev" 716 | optional = false 717 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" 718 | 719 | [[package]] 720 | name = "tomli" 721 | version = "2.0.1" 722 | description = "A lil' TOML parser" 723 | category = "main" 724 | optional = false 725 | python-versions = ">=3.7" 726 | 727 | [[package]] 728 | name = "typed-ast" 729 | version = "1.5.4" 730 | description = "a fork of Python 2 and 3 ast modules with type comment support" 731 | category = "dev" 732 | optional = false 733 | python-versions = ">=3.6" 734 | 735 | [[package]] 736 | name = "typing-extensions" 737 | version = "4.3.0" 738 | description = "Backported and Experimental Type Hints for Python 3.7+" 739 | category = "main" 740 | optional = false 741 | python-versions = ">=3.7" 742 | 743 | [[package]] 744 | name = "urllib3" 745 | version = "1.26.11" 746 | description = "HTTP library with thread-safe connection pooling, file post, and more." 747 | category = "dev" 748 | optional = false 749 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4" 750 | 751 | [package.extras] 752 | brotli = ["brotlicffi (>=0.8.0)", "brotli (>=1.0.9)", "brotlipy (>=0.6.0)"] 753 | secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] 754 | socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] 755 | 756 | [[package]] 757 | name = "virtualenv" 758 | version = "20.16.2" 759 | description = "Virtual Python Environment builder" 760 | category = "dev" 761 | optional = false 762 | python-versions = ">=3.6" 763 | 764 | [package.dependencies] 765 | distlib = ">=0.3.1,<1" 766 | filelock = ">=3.2,<4" 767 | importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} 768 | platformdirs = ">=2,<3" 769 | 770 | [package.extras] 771 | docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=21.3)"] 772 | testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "packaging (>=20.0)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)"] 773 | 774 | [[package]] 775 | name = "wrapt" 776 | version = "1.14.1" 777 | description = "Module for decorators, wrappers and monkey patching." 778 | category = "dev" 779 | optional = false 780 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" 781 | 782 | [[package]] 783 | name = "zipp" 784 | version = "3.8.1" 785 | description = "Backport of pathlib-compatible object wrapper for zip files" 786 | category = "main" 787 | optional = false 788 | python-versions = ">=3.7" 789 | 790 | [package.extras] 791 | docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)", "jaraco.tidelift (>=1.4)"] 792 | testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.3)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)"] 793 | 794 | [metadata] 795 | lock-version = "1.1" 796 | python-versions = "^3.7" 797 | content-hash = "22647b38d68a8c359af8cf8e2f9a049b36151f26930abe8817189ddc435f3515" 798 | 799 | [metadata.files] 800 | anyio = [ 801 | {file = "anyio-3.6.1-py3-none-any.whl", hash = "sha256:cb29b9c70620506a9a8f87a309591713446953302d7d995344d0d7c6c0c9a7be"}, 802 | {file = "anyio-3.6.1.tar.gz", hash = "sha256:413adf95f93886e442aea925f3ee43baa5a765a64a0f52c6081894f9992fdd0b"}, 803 | ] 804 | astroid = [] 805 | atomicwrites = [] 806 | attrs = [] 807 | bandit = [ 808 | {file = "bandit-1.7.4-py3-none-any.whl", hash = "sha256:412d3f259dab4077d0e7f0c11f50f650cc7d10db905d98f6520a95a18049658a"}, 809 | {file = "bandit-1.7.4.tar.gz", hash = "sha256:2d63a8c573417bae338962d4b9b06fbc6080f74ecd955a092849e1e65c717bd2"}, 810 | ] 811 | black = [ 812 | {file = "black-22.6.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f586c26118bc6e714ec58c09df0157fe2d9ee195c764f630eb0d8e7ccce72e69"}, 813 | {file = "black-22.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b270a168d69edb8b7ed32c193ef10fd27844e5c60852039599f9184460ce0807"}, 814 | {file = "black-22.6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6797f58943fceb1c461fb572edbe828d811e719c24e03375fd25170ada53825e"}, 815 | {file = "black-22.6.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c85928b9d5f83b23cee7d0efcb310172412fbf7cb9d9ce963bd67fd141781def"}, 816 | {file = "black-22.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:f6fe02afde060bbeef044af7996f335fbe90b039ccf3f5eb8f16df8b20f77666"}, 817 | {file = "black-22.6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:cfaf3895a9634e882bf9d2363fed5af8888802d670f58b279b0bece00e9a872d"}, 818 | {file = "black-22.6.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94783f636bca89f11eb5d50437e8e17fbc6a929a628d82304c80fa9cd945f256"}, 819 | {file = "black-22.6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:2ea29072e954a4d55a2ff58971b83365eba5d3d357352a07a7a4df0d95f51c78"}, 820 | {file = "black-22.6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e439798f819d49ba1c0bd9664427a05aab79bfba777a6db94fd4e56fae0cb849"}, 821 | {file = "black-22.6.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:187d96c5e713f441a5829e77120c269b6514418f4513a390b0499b0987f2ff1c"}, 822 | {file = "black-22.6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:074458dc2f6e0d3dab7928d4417bb6957bb834434516f21514138437accdbe90"}, 823 | {file = "black-22.6.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a218d7e5856f91d20f04e931b6f16d15356db1c846ee55f01bac297a705ca24f"}, 824 | {file = "black-22.6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:568ac3c465b1c8b34b61cd7a4e349e93f91abf0f9371eda1cf87194663ab684e"}, 825 | {file = "black-22.6.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6c1734ab264b8f7929cef8ae5f900b85d579e6cbfde09d7387da8f04771b51c6"}, 826 | {file = "black-22.6.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9a3ac16efe9ec7d7381ddebcc022119794872abce99475345c5a61aa18c45ad"}, 827 | {file = "black-22.6.0-cp38-cp38-win_amd64.whl", hash = "sha256:b9fd45787ba8aa3f5e0a0a98920c1012c884622c6c920dbe98dbd05bc7c70fbf"}, 828 | {file = "black-22.6.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7ba9be198ecca5031cd78745780d65a3f75a34b2ff9be5837045dce55db83d1c"}, 829 | {file = "black-22.6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a3db5b6409b96d9bd543323b23ef32a1a2b06416d525d27e0f67e74f1446c8f2"}, 830 | {file = "black-22.6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:560558527e52ce8afba936fcce93a7411ab40c7d5fe8c2463e279e843c0328ee"}, 831 | {file = "black-22.6.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b154e6bbde1e79ea3260c4b40c0b7b3109ffcdf7bc4ebf8859169a6af72cd70b"}, 832 | {file = "black-22.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:4af5bc0e1f96be5ae9bd7aaec219c901a94d6caa2484c21983d043371c733fc4"}, 833 | {file = "black-22.6.0-py3-none-any.whl", hash = "sha256:ac609cf8ef5e7115ddd07d85d988d074ed00e10fbc3445aee393e70164a2219c"}, 834 | {file = "black-22.6.0.tar.gz", hash = "sha256:6c6d39e28aed379aec40da1c65434c77d75e65bb59a1e1c283de545fb4e7c6c9"}, 835 | ] 836 | certifi = [ 837 | {file = "certifi-2022.6.15-py3-none-any.whl", hash = "sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412"}, 838 | {file = "certifi-2022.6.15.tar.gz", hash = "sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d"}, 839 | ] 840 | cfgv = [ 841 | {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"}, 842 | {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, 843 | ] 844 | charset-normalizer = [ 845 | {file = "charset-normalizer-2.1.0.tar.gz", hash = "sha256:575e708016ff3a5e3681541cb9d79312c416835686d054a23accb873b254f413"}, 846 | {file = "charset_normalizer-2.1.0-py3-none-any.whl", hash = "sha256:5189b6f22b01957427f35b6a08d9a0bc45b46d3788ef5a92e978433c7a35f8a5"}, 847 | ] 848 | click = [ 849 | {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, 850 | {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, 851 | ] 852 | colorama = [ 853 | {file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"}, 854 | {file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"}, 855 | ] 856 | coverage = [] 857 | coverage-badge = [ 858 | {file = "coverage-badge-1.1.0.tar.gz", hash = "sha256:c824a106503e981c02821e7d32f008fb3984b2338aa8c3800ec9357e33345b78"}, 859 | {file = "coverage_badge-1.1.0-py2.py3-none-any.whl", hash = "sha256:e365d56e5202e923d1b237f82defd628a02d1d645a147f867ac85c58c81d7997"}, 860 | ] 861 | darglint = [ 862 | {file = "darglint-1.8.1-py3-none-any.whl", hash = "sha256:5ae11c259c17b0701618a20c3da343a3eb98b3bc4b5a83d31cdd94f5ebdced8d"}, 863 | {file = "darglint-1.8.1.tar.gz", hash = "sha256:080d5106df149b199822e7ee7deb9c012b49891538f14a11be681044f0bb20da"}, 864 | ] 865 | dill = [ 866 | {file = "dill-0.3.5.1-py2.py3-none-any.whl", hash = "sha256:33501d03270bbe410c72639b350e941882a8b0fd55357580fbc873fba0c59302"}, 867 | {file = "dill-0.3.5.1.tar.gz", hash = "sha256:d75e41f3eff1eee599d738e76ba8f4ad98ea229db8b085318aa2b3333a208c86"}, 868 | ] 869 | distlib = [] 870 | dparse = [ 871 | {file = "dparse-0.5.1-py3-none-any.whl", hash = "sha256:e953a25e44ebb60a5c6efc2add4420c177f1d8404509da88da9729202f306994"}, 872 | {file = "dparse-0.5.1.tar.gz", hash = "sha256:a1b5f169102e1c894f9a7d5ccf6f9402a836a5d24be80a986c7ce9eaed78f367"}, 873 | ] 874 | fastapi = [] 875 | filelock = [ 876 | {file = "filelock-3.7.1-py3-none-any.whl", hash = "sha256:37def7b658813cda163b56fc564cdc75e86d338246458c4c28ae84cabefa2404"}, 877 | {file = "filelock-3.7.1.tar.gz", hash = "sha256:3a0fd85166ad9dbab54c9aec96737b744106dc5f15c0b09a6744a445299fcf04"}, 878 | ] 879 | gitdb = [ 880 | {file = "gitdb-4.0.9-py3-none-any.whl", hash = "sha256:8033ad4e853066ba6ca92050b9df2f89301b8fc8bf7e9324d412a63f8bf1a8fd"}, 881 | {file = "gitdb-4.0.9.tar.gz", hash = "sha256:bac2fd45c0a1c9cf619e63a90d62bdc63892ef92387424b855792a6cabe789aa"}, 882 | ] 883 | gitpython = [ 884 | {file = "GitPython-3.1.27-py3-none-any.whl", hash = "sha256:5b68b000463593e05ff2b261acff0ff0972df8ab1b70d3cdbd41b546c8b8fc3d"}, 885 | {file = "GitPython-3.1.27.tar.gz", hash = "sha256:1c885ce809e8ba2d88a29befeb385fcea06338d3640712b59ca623c220bb5704"}, 886 | ] 887 | identify = [] 888 | idna = [ 889 | {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, 890 | {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, 891 | ] 892 | importlib-metadata = [ 893 | {file = "importlib_metadata-4.12.0-py3-none-any.whl", hash = "sha256:7401a975809ea1fdc658c3aa4f78cc2195a0e019c5cbc4c06122884e9ae80c23"}, 894 | {file = "importlib_metadata-4.12.0.tar.gz", hash = "sha256:637245b8bab2b6502fcbc752cc4b7a6f6243bb02b31c5c26156ad103d3d45670"}, 895 | ] 896 | iniconfig = [ 897 | {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, 898 | {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, 899 | ] 900 | isort = [ 901 | {file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"}, 902 | {file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"}, 903 | ] 904 | lazy-object-proxy = [ 905 | {file = "lazy-object-proxy-1.7.1.tar.gz", hash = "sha256:d609c75b986def706743cdebe5e47553f4a5a1da9c5ff66d76013ef396b5a8a4"}, 906 | {file = "lazy_object_proxy-1.7.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bb8c5fd1684d60a9902c60ebe276da1f2281a318ca16c1d0a96db28f62e9166b"}, 907 | {file = "lazy_object_proxy-1.7.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a57d51ed2997e97f3b8e3500c984db50a554bb5db56c50b5dab1b41339b37e36"}, 908 | {file = "lazy_object_proxy-1.7.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd45683c3caddf83abbb1249b653a266e7069a09f486daa8863fb0e7496a9fdb"}, 909 | {file = "lazy_object_proxy-1.7.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:8561da8b3dd22d696244d6d0d5330618c993a215070f473b699e00cf1f3f6443"}, 910 | {file = "lazy_object_proxy-1.7.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fccdf7c2c5821a8cbd0a9440a456f5050492f2270bd54e94360cac663398739b"}, 911 | {file = "lazy_object_proxy-1.7.1-cp310-cp310-win32.whl", hash = "sha256:898322f8d078f2654d275124a8dd19b079080ae977033b713f677afcfc88e2b9"}, 912 | {file = "lazy_object_proxy-1.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:85b232e791f2229a4f55840ed54706110c80c0a210d076eee093f2b2e33e1bfd"}, 913 | {file = "lazy_object_proxy-1.7.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:46ff647e76f106bb444b4533bb4153c7370cdf52efc62ccfc1a28bdb3cc95442"}, 914 | {file = "lazy_object_proxy-1.7.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:12f3bb77efe1367b2515f8cb4790a11cffae889148ad33adad07b9b55e0ab22c"}, 915 | {file = "lazy_object_proxy-1.7.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c19814163728941bb871240d45c4c30d33b8a2e85972c44d4e63dd7107faba44"}, 916 | {file = "lazy_object_proxy-1.7.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:e40f2013d96d30217a51eeb1db28c9ac41e9d0ee915ef9d00da639c5b63f01a1"}, 917 | {file = "lazy_object_proxy-1.7.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:2052837718516a94940867e16b1bb10edb069ab475c3ad84fd1e1a6dd2c0fcfc"}, 918 | {file = "lazy_object_proxy-1.7.1-cp36-cp36m-win32.whl", hash = "sha256:6a24357267aa976abab660b1d47a34aaf07259a0c3859a34e536f1ee6e76b5bb"}, 919 | {file = "lazy_object_proxy-1.7.1-cp36-cp36m-win_amd64.whl", hash = "sha256:6aff3fe5de0831867092e017cf67e2750c6a1c7d88d84d2481bd84a2e019ec35"}, 920 | {file = "lazy_object_proxy-1.7.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6a6e94c7b02641d1311228a102607ecd576f70734dc3d5e22610111aeacba8a0"}, 921 | {file = "lazy_object_proxy-1.7.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4ce15276a1a14549d7e81c243b887293904ad2d94ad767f42df91e75fd7b5b6"}, 922 | {file = "lazy_object_proxy-1.7.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e368b7f7eac182a59ff1f81d5f3802161932a41dc1b1cc45c1f757dc876b5d2c"}, 923 | {file = "lazy_object_proxy-1.7.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6ecbb350991d6434e1388bee761ece3260e5228952b1f0c46ffc800eb313ff42"}, 924 | {file = "lazy_object_proxy-1.7.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:553b0f0d8dbf21890dd66edd771f9b1b5f51bd912fa5f26de4449bfc5af5e029"}, 925 | {file = "lazy_object_proxy-1.7.1-cp37-cp37m-win32.whl", hash = "sha256:c7a683c37a8a24f6428c28c561c80d5f4fd316ddcf0c7cab999b15ab3f5c5c69"}, 926 | {file = "lazy_object_proxy-1.7.1-cp37-cp37m-win_amd64.whl", hash = "sha256:df2631f9d67259dc9620d831384ed7732a198eb434eadf69aea95ad18c587a28"}, 927 | {file = "lazy_object_proxy-1.7.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:07fa44286cda977bd4803b656ffc1c9b7e3bc7dff7d34263446aec8f8c96f88a"}, 928 | {file = "lazy_object_proxy-1.7.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4dca6244e4121c74cc20542c2ca39e5c4a5027c81d112bfb893cf0790f96f57e"}, 929 | {file = "lazy_object_proxy-1.7.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91ba172fc5b03978764d1df5144b4ba4ab13290d7bab7a50f12d8117f8630c38"}, 930 | {file = "lazy_object_proxy-1.7.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:043651b6cb706eee4f91854da4a089816a6606c1428fd391573ef8cb642ae4f7"}, 931 | {file = "lazy_object_proxy-1.7.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b9e89b87c707dd769c4ea91f7a31538888aad05c116a59820f28d59b3ebfe25a"}, 932 | {file = "lazy_object_proxy-1.7.1-cp38-cp38-win32.whl", hash = "sha256:9d166602b525bf54ac994cf833c385bfcc341b364e3ee71e3bf5a1336e677b55"}, 933 | {file = "lazy_object_proxy-1.7.1-cp38-cp38-win_amd64.whl", hash = "sha256:8f3953eb575b45480db6568306893f0bd9d8dfeeebd46812aa09ca9579595148"}, 934 | {file = "lazy_object_proxy-1.7.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dd7ed7429dbb6c494aa9bc4e09d94b778a3579be699f9d67da7e6804c422d3de"}, 935 | {file = "lazy_object_proxy-1.7.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70ed0c2b380eb6248abdef3cd425fc52f0abd92d2b07ce26359fcbc399f636ad"}, 936 | {file = "lazy_object_proxy-1.7.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7096a5e0c1115ec82641afbdd70451a144558ea5cf564a896294e346eb611be1"}, 937 | {file = "lazy_object_proxy-1.7.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f769457a639403073968d118bc70110e7dce294688009f5c24ab78800ae56dc8"}, 938 | {file = "lazy_object_proxy-1.7.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:39b0e26725c5023757fc1ab2a89ef9d7ab23b84f9251e28f9cc114d5b59c1b09"}, 939 | {file = "lazy_object_proxy-1.7.1-cp39-cp39-win32.whl", hash = "sha256:2130db8ed69a48a3440103d4a520b89d8a9405f1b06e2cc81640509e8bf6548f"}, 940 | {file = "lazy_object_proxy-1.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:677ea950bef409b47e51e733283544ac3d660b709cfce7b187f5ace137960d61"}, 941 | {file = "lazy_object_proxy-1.7.1-pp37.pp38-none-any.whl", hash = "sha256:d66906d5785da8e0be7360912e99c9188b70f52c422f9fc18223347235691a84"}, 942 | ] 943 | mccabe = [ 944 | {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, 945 | {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, 946 | ] 947 | mypy = [ 948 | {file = "mypy-0.961-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:697540876638ce349b01b6786bc6094ccdaba88af446a9abb967293ce6eaa2b0"}, 949 | {file = "mypy-0.961-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b117650592e1782819829605a193360a08aa99f1fc23d1d71e1a75a142dc7e15"}, 950 | {file = "mypy-0.961-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bdd5ca340beffb8c44cb9dc26697628d1b88c6bddf5c2f6eb308c46f269bb6f3"}, 951 | {file = "mypy-0.961-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3e09f1f983a71d0672bbc97ae33ee3709d10c779beb613febc36805a6e28bb4e"}, 952 | {file = "mypy-0.961-cp310-cp310-win_amd64.whl", hash = "sha256:e999229b9f3198c0c880d5e269f9f8129c8862451ce53a011326cad38b9ccd24"}, 953 | {file = "mypy-0.961-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b24be97351084b11582fef18d79004b3e4db572219deee0212078f7cf6352723"}, 954 | {file = "mypy-0.961-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f4a21d01fc0ba4e31d82f0fff195682e29f9401a8bdb7173891070eb260aeb3b"}, 955 | {file = "mypy-0.961-cp36-cp36m-win_amd64.whl", hash = "sha256:439c726a3b3da7ca84a0199a8ab444cd8896d95012c4a6c4a0d808e3147abf5d"}, 956 | {file = "mypy-0.961-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5a0b53747f713f490affdceef835d8f0cb7285187a6a44c33821b6d1f46ed813"}, 957 | {file = "mypy-0.961-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:0e9f70df36405c25cc530a86eeda1e0867863d9471fe76d1273c783df3d35c2e"}, 958 | {file = "mypy-0.961-cp37-cp37m-win_amd64.whl", hash = "sha256:b88f784e9e35dcaa075519096dc947a388319cb86811b6af621e3523980f1c8a"}, 959 | {file = "mypy-0.961-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:d5aaf1edaa7692490f72bdb9fbd941fbf2e201713523bdb3f4038be0af8846c6"}, 960 | {file = "mypy-0.961-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9f5f5a74085d9a81a1f9c78081d60a0040c3efb3f28e5c9912b900adf59a16e6"}, 961 | {file = "mypy-0.961-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f4b794db44168a4fc886e3450201365c9526a522c46ba089b55e1f11c163750d"}, 962 | {file = "mypy-0.961-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:64759a273d590040a592e0f4186539858c948302c653c2eac840c7a3cd29e51b"}, 963 | {file = "mypy-0.961-cp38-cp38-win_amd64.whl", hash = "sha256:63e85a03770ebf403291ec50097954cc5caf2a9205c888ce3a61bd3f82e17569"}, 964 | {file = "mypy-0.961-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5f1332964963d4832a94bebc10f13d3279be3ce8f6c64da563d6ee6e2eeda932"}, 965 | {file = "mypy-0.961-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:006be38474216b833eca29ff6b73e143386f352e10e9c2fbe76aa8549e5554f5"}, 966 | {file = "mypy-0.961-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9940e6916ed9371809b35b2154baf1f684acba935cd09928952310fbddaba648"}, 967 | {file = "mypy-0.961-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a5ea0875a049de1b63b972456542f04643daf320d27dc592d7c3d9cd5d9bf950"}, 968 | {file = "mypy-0.961-cp39-cp39-win_amd64.whl", hash = "sha256:1ece702f29270ec6af25db8cf6185c04c02311c6bb21a69f423d40e527b75c56"}, 969 | {file = "mypy-0.961-py3-none-any.whl", hash = "sha256:03c6cc893e7563e7b2949b969e63f02c000b32502a1b4d1314cabe391aa87d66"}, 970 | {file = "mypy-0.961.tar.gz", hash = "sha256:f730d56cb924d371c26b8eaddeea3cc07d78ff51c521c6d04899ac6904b75492"}, 971 | ] 972 | mypy-extensions = [ 973 | {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, 974 | {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, 975 | ] 976 | nodeenv = [ 977 | {file = "nodeenv-1.7.0-py2.py3-none-any.whl", hash = "sha256:27083a7b96a25f2f5e1d8cb4b6317ee8aeda3bdd121394e5ac54e498028a042e"}, 978 | {file = "nodeenv-1.7.0.tar.gz", hash = "sha256:e0e7f7dfb85fc5394c6fe1e8fa98131a2473e04311a45afb6508f7cf1836fa2b"}, 979 | ] 980 | packaging = [ 981 | {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, 982 | {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, 983 | ] 984 | pathspec = [ 985 | {file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"}, 986 | {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"}, 987 | ] 988 | pbr = [ 989 | {file = "pbr-5.9.0-py2.py3-none-any.whl", hash = "sha256:e547125940bcc052856ded43be8e101f63828c2d94239ffbe2b327ba3d5ccf0a"}, 990 | {file = "pbr-5.9.0.tar.gz", hash = "sha256:e8dca2f4b43560edef58813969f52a56cef023146cbb8931626db80e6c1c4308"}, 991 | ] 992 | platformdirs = [ 993 | {file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"}, 994 | {file = "platformdirs-2.5.2.tar.gz", hash = "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"}, 995 | ] 996 | pluggy = [ 997 | {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, 998 | {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, 999 | ] 1000 | pre-commit = [] 1001 | py = [ 1002 | {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, 1003 | {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, 1004 | ] 1005 | pydantic = [ 1006 | {file = "pydantic-1.9.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c8098a724c2784bf03e8070993f6d46aa2eeca031f8d8a048dff277703e6e193"}, 1007 | {file = "pydantic-1.9.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c320c64dd876e45254bdd350f0179da737463eea41c43bacbee9d8c9d1021f11"}, 1008 | {file = "pydantic-1.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18f3e912f9ad1bdec27fb06b8198a2ccc32f201e24174cec1b3424dda605a310"}, 1009 | {file = "pydantic-1.9.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c11951b404e08b01b151222a1cb1a9f0a860a8153ce8334149ab9199cd198131"}, 1010 | {file = "pydantic-1.9.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8bc541a405423ce0e51c19f637050acdbdf8feca34150e0d17f675e72d119580"}, 1011 | {file = "pydantic-1.9.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e565a785233c2d03724c4dc55464559639b1ba9ecf091288dd47ad9c629433bd"}, 1012 | {file = "pydantic-1.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:a4a88dcd6ff8fd47c18b3a3709a89adb39a6373f4482e04c1b765045c7e282fd"}, 1013 | {file = "pydantic-1.9.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:447d5521575f18e18240906beadc58551e97ec98142266e521c34968c76c8761"}, 1014 | {file = "pydantic-1.9.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:985ceb5d0a86fcaa61e45781e567a59baa0da292d5ed2e490d612d0de5796918"}, 1015 | {file = "pydantic-1.9.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:059b6c1795170809103a1538255883e1983e5b831faea6558ef873d4955b4a74"}, 1016 | {file = "pydantic-1.9.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:d12f96b5b64bec3f43c8e82b4aab7599d0157f11c798c9f9c528a72b9e0b339a"}, 1017 | {file = "pydantic-1.9.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:ae72f8098acb368d877b210ebe02ba12585e77bd0db78ac04a1ee9b9f5dd2166"}, 1018 | {file = "pydantic-1.9.1-cp36-cp36m-win_amd64.whl", hash = "sha256:79b485767c13788ee314669008d01f9ef3bc05db9ea3298f6a50d3ef596a154b"}, 1019 | {file = "pydantic-1.9.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:494f7c8537f0c02b740c229af4cb47c0d39840b829ecdcfc93d91dcbb0779892"}, 1020 | {file = "pydantic-1.9.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0f047e11febe5c3198ed346b507e1d010330d56ad615a7e0a89fae604065a0e"}, 1021 | {file = "pydantic-1.9.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:969dd06110cb780da01336b281f53e2e7eb3a482831df441fb65dd30403f4608"}, 1022 | {file = "pydantic-1.9.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:177071dfc0df6248fd22b43036f936cfe2508077a72af0933d0c1fa269b18537"}, 1023 | {file = "pydantic-1.9.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:9bcf8b6e011be08fb729d110f3e22e654a50f8a826b0575c7196616780683380"}, 1024 | {file = "pydantic-1.9.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a955260d47f03df08acf45689bd163ed9df82c0e0124beb4251b1290fa7ae728"}, 1025 | {file = "pydantic-1.9.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9ce157d979f742a915b75f792dbd6aa63b8eccaf46a1005ba03aa8a986bde34a"}, 1026 | {file = "pydantic-1.9.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0bf07cab5b279859c253d26a9194a8906e6f4a210063b84b433cf90a569de0c1"}, 1027 | {file = "pydantic-1.9.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d93d4e95eacd313d2c765ebe40d49ca9dd2ed90e5b37d0d421c597af830c195"}, 1028 | {file = "pydantic-1.9.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1542636a39c4892c4f4fa6270696902acb186a9aaeac6f6cf92ce6ae2e88564b"}, 1029 | {file = "pydantic-1.9.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a9af62e9b5b9bc67b2a195ebc2c2662fdf498a822d62f902bf27cccb52dbbf49"}, 1030 | {file = "pydantic-1.9.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fe4670cb32ea98ffbf5a1262f14c3e102cccd92b1869df3bb09538158ba90fe6"}, 1031 | {file = "pydantic-1.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:9f659a5ee95c8baa2436d392267988fd0f43eb774e5eb8739252e5a7e9cf07e0"}, 1032 | {file = "pydantic-1.9.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b83ba3825bc91dfa989d4eed76865e71aea3a6ca1388b59fc801ee04c4d8d0d6"}, 1033 | {file = "pydantic-1.9.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1dd8fecbad028cd89d04a46688d2fcc14423e8a196d5b0a5c65105664901f810"}, 1034 | {file = "pydantic-1.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02eefd7087268b711a3ff4db528e9916ac9aa18616da7bca69c1871d0b7a091f"}, 1035 | {file = "pydantic-1.9.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7eb57ba90929bac0b6cc2af2373893d80ac559adda6933e562dcfb375029acee"}, 1036 | {file = "pydantic-1.9.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:4ce9ae9e91f46c344bec3b03d6ee9612802682c1551aaf627ad24045ce090761"}, 1037 | {file = "pydantic-1.9.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:72ccb318bf0c9ab97fc04c10c37683d9eea952ed526707fabf9ac5ae59b701fd"}, 1038 | {file = "pydantic-1.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:61b6760b08b7c395975d893e0b814a11cf011ebb24f7d869e7118f5a339a82e1"}, 1039 | {file = "pydantic-1.9.1-py3-none-any.whl", hash = "sha256:4988c0f13c42bfa9ddd2fe2f569c9d54646ce84adc5de84228cfe83396f3bd58"}, 1040 | {file = "pydantic-1.9.1.tar.gz", hash = "sha256:1ed987c3ff29fff7fd8c3ea3a3ea877ad310aae2ef9889a119e22d3f2db0691a"}, 1041 | ] 1042 | pydocstyle = [ 1043 | {file = "pydocstyle-6.1.1-py3-none-any.whl", hash = "sha256:6987826d6775056839940041beef5c08cc7e3d71d63149b48e36727f70144dc4"}, 1044 | {file = "pydocstyle-6.1.1.tar.gz", hash = "sha256:1d41b7c459ba0ee6c345f2eb9ae827cab14a7533a88c5c6f7e94923f72df92dc"}, 1045 | ] 1046 | pylint = [ 1047 | {file = "pylint-2.13.9-py3-none-any.whl", hash = "sha256:705c620d388035bdd9ff8b44c5bcdd235bfb49d276d488dd2c8ff1736aa42526"}, 1048 | {file = "pylint-2.13.9.tar.gz", hash = "sha256:095567c96e19e6f57b5b907e67d265ff535e588fe26b12b5ebe1fc5645b2c731"}, 1049 | ] 1050 | pyparsing = [ 1051 | {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, 1052 | {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, 1053 | ] 1054 | pytest = [ 1055 | {file = "pytest-7.1.2-py3-none-any.whl", hash = "sha256:13d0e3ccfc2b6e26be000cb6568c832ba67ba32e719443bfe725814d3c42433c"}, 1056 | {file = "pytest-7.1.2.tar.gz", hash = "sha256:a06a0425453864a270bc45e71f783330a7428defb4230fb5e6a731fde06ecd45"}, 1057 | ] 1058 | pytest-cov = [ 1059 | {file = "pytest-cov-3.0.0.tar.gz", hash = "sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470"}, 1060 | {file = "pytest_cov-3.0.0-py3-none-any.whl", hash = "sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6"}, 1061 | ] 1062 | pytest-html = [ 1063 | {file = "pytest-html-3.1.1.tar.gz", hash = "sha256:3ee1cf319c913d19fe53aeb0bc400e7b0bc2dbeb477553733db1dad12eb75ee3"}, 1064 | {file = "pytest_html-3.1.1-py3-none-any.whl", hash = "sha256:b7f82f123936a3f4d2950bc993c2c1ca09ce262c9ae12f9ac763a2401380b455"}, 1065 | ] 1066 | pytest-metadata = [] 1067 | pyupgrade = [] 1068 | pyyaml = [ 1069 | {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, 1070 | {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, 1071 | {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, 1072 | {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, 1073 | {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, 1074 | {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, 1075 | {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, 1076 | {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, 1077 | {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, 1078 | {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, 1079 | {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"}, 1080 | {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"}, 1081 | {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"}, 1082 | {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"}, 1083 | {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"}, 1084 | {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"}, 1085 | {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"}, 1086 | {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"}, 1087 | {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"}, 1088 | {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, 1089 | {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, 1090 | {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, 1091 | {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"}, 1092 | {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, 1093 | {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, 1094 | {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, 1095 | {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, 1096 | {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, 1097 | {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, 1098 | {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"}, 1099 | {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, 1100 | {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, 1101 | {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, 1102 | ] 1103 | requests = [ 1104 | {file = "requests-2.28.1-py3-none-any.whl", hash = "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"}, 1105 | {file = "requests-2.28.1.tar.gz", hash = "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983"}, 1106 | ] 1107 | "ruamel.yaml" = [ 1108 | {file = "ruamel.yaml-0.17.21-py3-none-any.whl", hash = "sha256:742b35d3d665023981bd6d16b3d24248ce5df75fdb4e2924e93a05c1f8b61ca7"}, 1109 | {file = "ruamel.yaml-0.17.21.tar.gz", hash = "sha256:8b7ce697a2f212752a35c1ac414471dc16c424c9573be4926b56ff3f5d23b7af"}, 1110 | ] 1111 | "ruamel.yaml.clib" = [ 1112 | {file = "ruamel.yaml.clib-0.2.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6e7be2c5bcb297f5b82fee9c665eb2eb7001d1050deaba8471842979293a80b0"}, 1113 | {file = "ruamel.yaml.clib-0.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:221eca6f35076c6ae472a531afa1c223b9c29377e62936f61bc8e6e8bdc5f9e7"}, 1114 | {file = "ruamel.yaml.clib-0.2.6-cp310-cp310-win32.whl", hash = "sha256:1070ba9dd7f9370d0513d649420c3b362ac2d687fe78c6e888f5b12bf8bc7bee"}, 1115 | {file = "ruamel.yaml.clib-0.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:77df077d32921ad46f34816a9a16e6356d8100374579bc35e15bab5d4e9377de"}, 1116 | {file = "ruamel.yaml.clib-0.2.6-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:cfdb9389d888c5b74af297e51ce357b800dd844898af9d4a547ffc143fa56751"}, 1117 | {file = "ruamel.yaml.clib-0.2.6-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:7b2927e92feb51d830f531de4ccb11b320255ee95e791022555971c466af4527"}, 1118 | {file = "ruamel.yaml.clib-0.2.6-cp35-cp35m-win32.whl", hash = "sha256:ada3f400d9923a190ea8b59c8f60680c4ef8a4b0dfae134d2f2ff68429adfab5"}, 1119 | {file = "ruamel.yaml.clib-0.2.6-cp35-cp35m-win_amd64.whl", hash = "sha256:de9c6b8a1ba52919ae919f3ae96abb72b994dd0350226e28f3686cb4f142165c"}, 1120 | {file = "ruamel.yaml.clib-0.2.6-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d67f273097c368265a7b81e152e07fb90ed395df6e552b9fa858c6d2c9f42502"}, 1121 | {file = "ruamel.yaml.clib-0.2.6-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:72a2b8b2ff0a627496aad76f37a652bcef400fd861721744201ef1b45199ab78"}, 1122 | {file = "ruamel.yaml.clib-0.2.6-cp36-cp36m-win32.whl", hash = "sha256:9efef4aab5353387b07f6b22ace0867032b900d8e91674b5d8ea9150db5cae94"}, 1123 | {file = "ruamel.yaml.clib-0.2.6-cp36-cp36m-win_amd64.whl", hash = "sha256:846fc8336443106fe23f9b6d6b8c14a53d38cef9a375149d61f99d78782ea468"}, 1124 | {file = "ruamel.yaml.clib-0.2.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0847201b767447fc33b9c235780d3aa90357d20dd6108b92be544427bea197dd"}, 1125 | {file = "ruamel.yaml.clib-0.2.6-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:78988ed190206672da0f5d50c61afef8f67daa718d614377dcd5e3ed85ab4a99"}, 1126 | {file = "ruamel.yaml.clib-0.2.6-cp37-cp37m-win32.whl", hash = "sha256:a49e0161897901d1ac9c4a79984b8410f450565bbad64dbfcbf76152743a0cdb"}, 1127 | {file = "ruamel.yaml.clib-0.2.6-cp37-cp37m-win_amd64.whl", hash = "sha256:bf75d28fa071645c529b5474a550a44686821decebdd00e21127ef1fd566eabe"}, 1128 | {file = "ruamel.yaml.clib-0.2.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a32f8d81ea0c6173ab1b3da956869114cae53ba1e9f72374032e33ba3118c233"}, 1129 | {file = "ruamel.yaml.clib-0.2.6-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7f7ecb53ae6848f959db6ae93bdff1740e651809780822270eab111500842a84"}, 1130 | {file = "ruamel.yaml.clib-0.2.6-cp38-cp38-win32.whl", hash = "sha256:89221ec6d6026f8ae859c09b9718799fea22c0e8da8b766b0b2c9a9ba2db326b"}, 1131 | {file = "ruamel.yaml.clib-0.2.6-cp38-cp38-win_amd64.whl", hash = "sha256:31ea73e564a7b5fbbe8188ab8b334393e06d997914a4e184975348f204790277"}, 1132 | {file = "ruamel.yaml.clib-0.2.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dc6a613d6c74eef5a14a214d433d06291526145431c3b964f5e16529b1842bed"}, 1133 | {file = "ruamel.yaml.clib-0.2.6-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:1866cf2c284a03b9524a5cc00daca56d80057c5ce3cdc86a52020f4c720856f0"}, 1134 | {file = "ruamel.yaml.clib-0.2.6-cp39-cp39-win32.whl", hash = "sha256:3fb9575a5acd13031c57a62cc7823e5d2ff8bc3835ba4d94b921b4e6ee664104"}, 1135 | {file = "ruamel.yaml.clib-0.2.6-cp39-cp39-win_amd64.whl", hash = "sha256:825d5fccef6da42f3c8eccd4281af399f21c02b32d98e113dbc631ea6a6ecbc7"}, 1136 | {file = "ruamel.yaml.clib-0.2.6.tar.gz", hash = "sha256:4ff604ce439abb20794f05613c374759ce10e3595d1867764dd1ae675b85acbd"}, 1137 | ] 1138 | safety = [] 1139 | smmap = [ 1140 | {file = "smmap-5.0.0-py3-none-any.whl", hash = "sha256:2aba19d6a040e78d8b09de5c57e96207b09ed71d8e55ce0959eeee6c8e190d94"}, 1141 | {file = "smmap-5.0.0.tar.gz", hash = "sha256:c840e62059cd3be204b0c9c9f74be2c09d5648eddd4580d9314c3ecde0b30936"}, 1142 | ] 1143 | sniffio = [ 1144 | {file = "sniffio-1.2.0-py3-none-any.whl", hash = "sha256:471b71698eac1c2112a40ce2752bb2f4a4814c22a54a3eed3676bc0f5ca9f663"}, 1145 | {file = "sniffio-1.2.0.tar.gz", hash = "sha256:c4666eecec1d3f50960c6bdf61ab7bc350648da6c126e3cf6898d8cd4ddcd3de"}, 1146 | ] 1147 | snowballstemmer = [ 1148 | {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, 1149 | {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, 1150 | ] 1151 | starlette = [ 1152 | {file = "starlette-0.19.1-py3-none-any.whl", hash = "sha256:5a60c5c2d051f3a8eb546136aa0c9399773a689595e099e0877704d5888279bf"}, 1153 | {file = "starlette-0.19.1.tar.gz", hash = "sha256:c6d21096774ecb9639acad41b86b7706e52ba3bf1dc13ea4ed9ad593d47e24c7"}, 1154 | ] 1155 | stevedore = [ 1156 | {file = "stevedore-3.5.0-py3-none-any.whl", hash = "sha256:a547de73308fd7e90075bb4d301405bebf705292fa90a90fc3bcf9133f58616c"}, 1157 | {file = "stevedore-3.5.0.tar.gz", hash = "sha256:f40253887d8712eaa2bb0ea3830374416736dc8ec0e22f5a65092c1174c44335"}, 1158 | ] 1159 | tokenize-rt = [ 1160 | {file = "tokenize_rt-4.2.1-py2.py3-none-any.whl", hash = "sha256:08a27fa032a81cf45e8858d0ac706004fcd523e8463415ddf1442be38e204ea8"}, 1161 | {file = "tokenize_rt-4.2.1.tar.gz", hash = "sha256:0d4f69026fed520f8a1e0103aa36c406ef4661417f20ca643f913e33531b3b94"}, 1162 | ] 1163 | toml = [ 1164 | {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, 1165 | {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, 1166 | ] 1167 | tomli = [ 1168 | {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, 1169 | {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, 1170 | ] 1171 | typed-ast = [ 1172 | {file = "typed_ast-1.5.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:669dd0c4167f6f2cd9f57041e03c3c2ebf9063d0757dc89f79ba1daa2bfca9d4"}, 1173 | {file = "typed_ast-1.5.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:211260621ab1cd7324e0798d6be953d00b74e0428382991adfddb352252f1d62"}, 1174 | {file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:267e3f78697a6c00c689c03db4876dd1efdfea2f251a5ad6555e82a26847b4ac"}, 1175 | {file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c542eeda69212fa10a7ada75e668876fdec5f856cd3d06829e6aa64ad17c8dfe"}, 1176 | {file = "typed_ast-1.5.4-cp310-cp310-win_amd64.whl", hash = "sha256:a9916d2bb8865f973824fb47436fa45e1ebf2efd920f2b9f99342cb7fab93f72"}, 1177 | {file = "typed_ast-1.5.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:79b1e0869db7c830ba6a981d58711c88b6677506e648496b1f64ac7d15633aec"}, 1178 | {file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a94d55d142c9265f4ea46fab70977a1944ecae359ae867397757d836ea5a3f47"}, 1179 | {file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:183afdf0ec5b1b211724dfef3d2cad2d767cbefac291f24d69b00546c1837fb6"}, 1180 | {file = "typed_ast-1.5.4-cp36-cp36m-win_amd64.whl", hash = "sha256:639c5f0b21776605dd6c9dbe592d5228f021404dafd377e2b7ac046b0349b1a1"}, 1181 | {file = "typed_ast-1.5.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cf4afcfac006ece570e32d6fa90ab74a17245b83dfd6655a6f68568098345ff6"}, 1182 | {file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed855bbe3eb3715fca349c80174cfcfd699c2f9de574d40527b8429acae23a66"}, 1183 | {file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6778e1b2f81dfc7bc58e4b259363b83d2e509a65198e85d5700dfae4c6c8ff1c"}, 1184 | {file = "typed_ast-1.5.4-cp37-cp37m-win_amd64.whl", hash = "sha256:0261195c2062caf107831e92a76764c81227dae162c4f75192c0d489faf751a2"}, 1185 | {file = "typed_ast-1.5.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2efae9db7a8c05ad5547d522e7dbe62c83d838d3906a3716d1478b6c1d61388d"}, 1186 | {file = "typed_ast-1.5.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7d5d014b7daa8b0bf2eaef684295acae12b036d79f54178b92a2b6a56f92278f"}, 1187 | {file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:370788a63915e82fd6f212865a596a0fefcbb7d408bbbb13dea723d971ed8bdc"}, 1188 | {file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4e964b4ff86550a7a7d56345c7864b18f403f5bd7380edf44a3c1fb4ee7ac6c6"}, 1189 | {file = "typed_ast-1.5.4-cp38-cp38-win_amd64.whl", hash = "sha256:683407d92dc953c8a7347119596f0b0e6c55eb98ebebd9b23437501b28dcbb8e"}, 1190 | {file = "typed_ast-1.5.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4879da6c9b73443f97e731b617184a596ac1235fe91f98d279a7af36c796da35"}, 1191 | {file = "typed_ast-1.5.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3e123d878ba170397916557d31c8f589951e353cc95fb7f24f6bb69adc1a8a97"}, 1192 | {file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebd9d7f80ccf7a82ac5f88c521115cc55d84e35bf8b446fcd7836eb6b98929a3"}, 1193 | {file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98f80dee3c03455e92796b58b98ff6ca0b2a6f652120c263efdba4d6c5e58f72"}, 1194 | {file = "typed_ast-1.5.4-cp39-cp39-win_amd64.whl", hash = "sha256:0fdbcf2fef0ca421a3f5912555804296f0b0960f0418c440f5d6d3abb549f3e1"}, 1195 | {file = "typed_ast-1.5.4.tar.gz", hash = "sha256:39e21ceb7388e4bb37f4c679d72707ed46c2fbf2a5609b8b8ebc4b067d977df2"}, 1196 | ] 1197 | typing-extensions = [ 1198 | {file = "typing_extensions-4.3.0-py3-none-any.whl", hash = "sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02"}, 1199 | {file = "typing_extensions-4.3.0.tar.gz", hash = "sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6"}, 1200 | ] 1201 | urllib3 = [] 1202 | virtualenv = [] 1203 | wrapt = [ 1204 | {file = "wrapt-1.14.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:1b376b3f4896e7930f1f772ac4b064ac12598d1c38d04907e696cc4d794b43d3"}, 1205 | {file = "wrapt-1.14.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:903500616422a40a98a5a3c4ff4ed9d0066f3b4c951fa286018ecdf0750194ef"}, 1206 | {file = "wrapt-1.14.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5a9a0d155deafd9448baff28c08e150d9b24ff010e899311ddd63c45c2445e28"}, 1207 | {file = "wrapt-1.14.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:ddaea91abf8b0d13443f6dac52e89051a5063c7d014710dcb4d4abb2ff811a59"}, 1208 | {file = "wrapt-1.14.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:36f582d0c6bc99d5f39cd3ac2a9062e57f3cf606ade29a0a0d6b323462f4dd87"}, 1209 | {file = "wrapt-1.14.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:7ef58fb89674095bfc57c4069e95d7a31cfdc0939e2a579882ac7d55aadfd2a1"}, 1210 | {file = "wrapt-1.14.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:e2f83e18fe2f4c9e7db597e988f72712c0c3676d337d8b101f6758107c42425b"}, 1211 | {file = "wrapt-1.14.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:ee2b1b1769f6707a8a445162ea16dddf74285c3964f605877a20e38545c3c462"}, 1212 | {file = "wrapt-1.14.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:833b58d5d0b7e5b9832869f039203389ac7cbf01765639c7309fd50ef619e0b1"}, 1213 | {file = "wrapt-1.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:80bb5c256f1415f747011dc3604b59bc1f91c6e7150bd7db03b19170ee06b320"}, 1214 | {file = "wrapt-1.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07f7a7d0f388028b2df1d916e94bbb40624c59b48ecc6cbc232546706fac74c2"}, 1215 | {file = "wrapt-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:02b41b633c6261feff8ddd8d11c711df6842aba629fdd3da10249a53211a72c4"}, 1216 | {file = "wrapt-1.14.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2fe803deacd09a233e4762a1adcea5db5d31e6be577a43352936179d14d90069"}, 1217 | {file = "wrapt-1.14.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:257fd78c513e0fb5cdbe058c27a0624c9884e735bbd131935fd49e9fe719d310"}, 1218 | {file = "wrapt-1.14.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4fcc4649dc762cddacd193e6b55bc02edca674067f5f98166d7713b193932b7f"}, 1219 | {file = "wrapt-1.14.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:11871514607b15cfeb87c547a49bca19fde402f32e2b1c24a632506c0a756656"}, 1220 | {file = "wrapt-1.14.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8ad85f7f4e20964db4daadcab70b47ab05c7c1cf2a7c1e51087bfaa83831854c"}, 1221 | {file = "wrapt-1.14.1-cp310-cp310-win32.whl", hash = "sha256:a9a52172be0b5aae932bef82a79ec0a0ce87288c7d132946d645eba03f0ad8a8"}, 1222 | {file = "wrapt-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:6d323e1554b3d22cfc03cd3243b5bb815a51f5249fdcbb86fda4bf62bab9e164"}, 1223 | {file = "wrapt-1.14.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:43ca3bbbe97af00f49efb06e352eae40434ca9d915906f77def219b88e85d907"}, 1224 | {file = "wrapt-1.14.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:6b1a564e6cb69922c7fe3a678b9f9a3c54e72b469875aa8018f18b4d1dd1adf3"}, 1225 | {file = "wrapt-1.14.1-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:00b6d4ea20a906c0ca56d84f93065b398ab74b927a7a3dbd470f6fc503f95dc3"}, 1226 | {file = "wrapt-1.14.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:a85d2b46be66a71bedde836d9e41859879cc54a2a04fad1191eb50c2066f6e9d"}, 1227 | {file = "wrapt-1.14.1-cp35-cp35m-win32.whl", hash = "sha256:dbcda74c67263139358f4d188ae5faae95c30929281bc6866d00573783c422b7"}, 1228 | {file = "wrapt-1.14.1-cp35-cp35m-win_amd64.whl", hash = "sha256:b21bb4c09ffabfa0e85e3a6b623e19b80e7acd709b9f91452b8297ace2a8ab00"}, 1229 | {file = "wrapt-1.14.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:9e0fd32e0148dd5dea6af5fee42beb949098564cc23211a88d799e434255a1f4"}, 1230 | {file = "wrapt-1.14.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9736af4641846491aedb3c3f56b9bc5568d92b0692303b5a305301a95dfd38b1"}, 1231 | {file = "wrapt-1.14.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b02d65b9ccf0ef6c34cba6cf5bf2aab1bb2f49c6090bafeecc9cd81ad4ea1c1"}, 1232 | {file = "wrapt-1.14.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21ac0156c4b089b330b7666db40feee30a5d52634cc4560e1905d6529a3897ff"}, 1233 | {file = "wrapt-1.14.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:9f3e6f9e05148ff90002b884fbc2a86bd303ae847e472f44ecc06c2cd2fcdb2d"}, 1234 | {file = "wrapt-1.14.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:6e743de5e9c3d1b7185870f480587b75b1cb604832e380d64f9504a0535912d1"}, 1235 | {file = "wrapt-1.14.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:d79d7d5dc8a32b7093e81e97dad755127ff77bcc899e845f41bf71747af0c569"}, 1236 | {file = "wrapt-1.14.1-cp36-cp36m-win32.whl", hash = "sha256:81b19725065dcb43df02b37e03278c011a09e49757287dca60c5aecdd5a0b8ed"}, 1237 | {file = "wrapt-1.14.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b014c23646a467558be7da3d6b9fa409b2c567d2110599b7cf9a0c5992b3b471"}, 1238 | {file = "wrapt-1.14.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:88bd7b6bd70a5b6803c1abf6bca012f7ed963e58c68d76ee20b9d751c74a3248"}, 1239 | {file = "wrapt-1.14.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5901a312f4d14c59918c221323068fad0540e34324925c8475263841dbdfe68"}, 1240 | {file = "wrapt-1.14.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d77c85fedff92cf788face9bfa3ebaa364448ebb1d765302e9af11bf449ca36d"}, 1241 | {file = "wrapt-1.14.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d649d616e5c6a678b26d15ece345354f7c2286acd6db868e65fcc5ff7c24a77"}, 1242 | {file = "wrapt-1.14.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7d2872609603cb35ca513d7404a94d6d608fc13211563571117046c9d2bcc3d7"}, 1243 | {file = "wrapt-1.14.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:ee6acae74a2b91865910eef5e7de37dc6895ad96fa23603d1d27ea69df545015"}, 1244 | {file = "wrapt-1.14.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2b39d38039a1fdad98c87279b48bc5dce2c0ca0d73483b12cb72aa9609278e8a"}, 1245 | {file = "wrapt-1.14.1-cp37-cp37m-win32.whl", hash = "sha256:60db23fa423575eeb65ea430cee741acb7c26a1365d103f7b0f6ec412b893853"}, 1246 | {file = "wrapt-1.14.1-cp37-cp37m-win_amd64.whl", hash = "sha256:709fe01086a55cf79d20f741f39325018f4df051ef39fe921b1ebe780a66184c"}, 1247 | {file = "wrapt-1.14.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8c0ce1e99116d5ab21355d8ebe53d9460366704ea38ae4d9f6933188f327b456"}, 1248 | {file = "wrapt-1.14.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e3fb1677c720409d5f671e39bac6c9e0e422584e5f518bfd50aa4cbbea02433f"}, 1249 | {file = "wrapt-1.14.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:642c2e7a804fcf18c222e1060df25fc210b9c58db7c91416fb055897fc27e8cc"}, 1250 | {file = "wrapt-1.14.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b7c050ae976e286906dd3f26009e117eb000fb2cf3533398c5ad9ccc86867b1"}, 1251 | {file = "wrapt-1.14.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef3f72c9666bba2bab70d2a8b79f2c6d2c1a42a7f7e2b0ec83bb2f9e383950af"}, 1252 | {file = "wrapt-1.14.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:01c205616a89d09827986bc4e859bcabd64f5a0662a7fe95e0d359424e0e071b"}, 1253 | {file = "wrapt-1.14.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5a0f54ce2c092aaf439813735584b9537cad479575a09892b8352fea5e988dc0"}, 1254 | {file = "wrapt-1.14.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2cf71233a0ed05ccdabe209c606fe0bac7379fdcf687f39b944420d2a09fdb57"}, 1255 | {file = "wrapt-1.14.1-cp38-cp38-win32.whl", hash = "sha256:aa31fdcc33fef9eb2552cbcbfee7773d5a6792c137b359e82879c101e98584c5"}, 1256 | {file = "wrapt-1.14.1-cp38-cp38-win_amd64.whl", hash = "sha256:d1967f46ea8f2db647c786e78d8cc7e4313dbd1b0aca360592d8027b8508e24d"}, 1257 | {file = "wrapt-1.14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3232822c7d98d23895ccc443bbdf57c7412c5a65996c30442ebe6ed3df335383"}, 1258 | {file = "wrapt-1.14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:988635d122aaf2bdcef9e795435662bcd65b02f4f4c1ae37fbee7401c440b3a7"}, 1259 | {file = "wrapt-1.14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cca3c2cdadb362116235fdbd411735de4328c61425b0aa9f872fd76d02c4e86"}, 1260 | {file = "wrapt-1.14.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d52a25136894c63de15a35bc0bdc5adb4b0e173b9c0d07a2be9d3ca64a332735"}, 1261 | {file = "wrapt-1.14.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40e7bc81c9e2b2734ea4bc1aceb8a8f0ceaac7c5299bc5d69e37c44d9081d43b"}, 1262 | {file = "wrapt-1.14.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b9b7a708dd92306328117d8c4b62e2194d00c365f18eff11a9b53c6f923b01e3"}, 1263 | {file = "wrapt-1.14.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6a9a25751acb379b466ff6be78a315e2b439d4c94c1e99cb7266d40a537995d3"}, 1264 | {file = "wrapt-1.14.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:34aa51c45f28ba7f12accd624225e2b1e5a3a45206aa191f6f9aac931d9d56fe"}, 1265 | {file = "wrapt-1.14.1-cp39-cp39-win32.whl", hash = "sha256:dee0ce50c6a2dd9056c20db781e9c1cfd33e77d2d569f5d1d9321c641bb903d5"}, 1266 | {file = "wrapt-1.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:dee60e1de1898bde3b238f18340eec6148986da0455d8ba7848d50470a7a32fb"}, 1267 | {file = "wrapt-1.14.1.tar.gz", hash = "sha256:380a85cf89e0e69b7cfbe2ea9f765f004ff419f34194018a6827ac0e3edfed4d"}, 1268 | ] 1269 | zipp = [] 1270 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | # Poetry pyproject.toml: https://python-poetry.org/docs/pyproject/ 2 | [build-system] 3 | requires = ["poetry_core>=1.0.0"] 4 | build-backend = "poetry.core.masonry.api" 5 | 6 | [tool.poetry] 7 | name = "pytest-fastapi-deps" 8 | version = "0.2.3" 9 | description = "A fixture which allows easy replacement of fastapi dependencies for testing" 10 | readme = "README.md" 11 | authors = ["Peter Kogan "] 12 | license = "MIT" 13 | repository = "https://github.com/pksol/pytest-fastapi-deps" 14 | homepage = "https://github.com/pksol/pytest-fastapi-deps" 15 | 16 | # Keywords description https://python-poetry.org/docs/pyproject/#keywords 17 | keywords = [ 18 | "pytest", 19 | "fastapi", 20 | "dependencies", 21 | "testing", 22 | "fixtures", 23 | ] 24 | 25 | # Pypi classifiers: https://pypi.org/classifiers/ 26 | classifiers = [ 27 | "Programming Language :: Python", 28 | "Programming Language :: Python :: 3", 29 | "Programming Language :: Python :: 3.7", 30 | "Programming Language :: Python :: 3.8", 31 | "Programming Language :: Python :: 3.9", 32 | "Programming Language :: Python :: 3.10", 33 | "Topic :: Software Development :: Testing", 34 | "Framework :: Pytest", 35 | "License :: OSI Approved :: MIT License", 36 | "Development Status :: 5 - Production/Stable", 37 | "Intended Audience :: Developers", 38 | "Operating System :: OS Independent", 39 | ] 40 | 41 | [tool.poetry.plugins.pytest11] 42 | pytest_fastapi_deps = "pytest_fastapi_deps" 43 | 44 | [tool.poetry.dependencies] 45 | python = "^3.7" 46 | importlib_metadata = {version = "^4.5.0", python = "<3.8"} 47 | fastapi = "*" 48 | pytest = "*" 49 | 50 | [tool.poetry.dev-dependencies] 51 | bandit = "^1.7.1" 52 | black = {version = "^22.6", allow-prereleases = true} 53 | darglint = "^1.8.1" 54 | isort = {extras = ["colors"], version = "^5.10.1"} 55 | mypy = "^0.961" 56 | mypy-extensions = "^0.4.3" 57 | pre-commit = "^2.15.0" 58 | pydocstyle = "^6.1.1" 59 | pylint = "^2.13.9" 60 | pytest = "^7.1.2" 61 | pyupgrade = "^2.29.1" 62 | safety = "^2.0.0" 63 | coverage = "^6.4" 64 | coverage-badge = "^1.1.0" 65 | pytest-html = "^3.1.1" 66 | pytest-cov = "^3.0.0" 67 | 68 | [tool.black] 69 | # https://github.com/psf/black 70 | target-version = ["py37"] 71 | line-length = 88 72 | color = true 73 | 74 | exclude = ''' 75 | /( 76 | \.git 77 | | \.hg 78 | | \.mypy_cache 79 | | \.tox 80 | | \.venv 81 | | _build 82 | | buck-out 83 | | build 84 | | dist 85 | | env 86 | | venv 87 | )/ 88 | ''' 89 | 90 | [tool.isort] 91 | # https://github.com/timothycrosley/isort/ 92 | py_version = 37 93 | line_length = 88 94 | 95 | known_typing = ["typing", "types", "typing_extensions", "mypy", "mypy_extensions"] 96 | sections = ["FUTURE", "TYPING", "STDLIB", "THIRDPARTY", "FIRSTPARTY", "LOCALFOLDER"] 97 | include_trailing_comma = true 98 | profile = "black" 99 | multi_line_output = 3 100 | indent = 4 101 | color_output = true 102 | 103 | [tool.mypy] 104 | # https://mypy.readthedocs.io/en/latest/config_file.html#using-a-pyproject-toml-file 105 | python_version = 3.7 106 | pretty = true 107 | show_traceback = true 108 | color_output = true 109 | 110 | allow_redefinition = false 111 | check_untyped_defs = true 112 | disallow_any_generics = true 113 | disallow_incomplete_defs = true 114 | ignore_missing_imports = true 115 | implicit_reexport = false 116 | no_implicit_optional = true 117 | show_column_numbers = true 118 | show_error_codes = true 119 | show_error_context = true 120 | strict_equality = true 121 | strict_optional = true 122 | warn_no_return = true 123 | warn_redundant_casts = true 124 | warn_return_any = true 125 | warn_unreachable = true 126 | warn_unused_configs = true 127 | warn_unused_ignores = true 128 | 129 | 130 | [tool.pytest.ini_options] 131 | # https://docs.pytest.org/en/6.2.x/customize.html#pyproject-toml 132 | # Directories that are not visited by pytest collector: 133 | norecursedirs =["hooks", "*.egg", ".eggs", "dist", "build", "docs", ".tox", ".git", "__pycache__"] 134 | doctest_optionflags = ["NUMBER", "NORMALIZE_WHITESPACE", "IGNORE_EXCEPTION_DETAIL"] 135 | pytester_example_dir = "tests/examples" 136 | 137 | # Extra options: 138 | addopts = [ 139 | "--strict-markers", 140 | "--tb=short", 141 | "--doctest-modules", 142 | "--doctest-continue-on-failure", 143 | ] 144 | 145 | [tool.coverage.run] 146 | source = ["tests"] 147 | 148 | [coverage.paths] 149 | source = "pytest-fastapi-deps" 150 | 151 | [coverage.run] 152 | branch = true 153 | 154 | [coverage.report] 155 | fail_under = 50 156 | show_missing = true 157 | -------------------------------------------------------------------------------- /pytest_fastapi_deps/__init__.py: -------------------------------------------------------------------------------- 1 | # type: ignore[attr-defined] 2 | """A fixture which allows easy replacement of fastapi dependencies for testing""" 3 | 4 | import typing 5 | 6 | import contextlib 7 | import sys 8 | 9 | import pytest as pytest 10 | from fastapi import FastAPI 11 | 12 | if sys.version_info >= (3, 8): 13 | from importlib import metadata as importlib_metadata 14 | else: 15 | import importlib_metadata 16 | 17 | 18 | def get_version() -> str: 19 | try: 20 | return importlib_metadata.version(__name__) 21 | except importlib_metadata.PackageNotFoundError: # pragma: no cover 22 | return "unknown" 23 | 24 | 25 | version: str = get_version() 26 | 27 | 28 | class DependencyOverrider: 29 | """ 30 | Allows to override the fastapi dependencies inside a cleanable context. 31 | 32 | 33 | Taken from https://stackoverflow.com/a/68687967/3800552 by 34 | https://stackoverflow.com/users/8944325/mat-win . 35 | 36 | Args: 37 | app: the fastapi app 38 | overrides: a dictionary of the override mappings where the key is the 39 | original function and the value is the replacement function. 40 | """ 41 | 42 | def __init__( 43 | self, app: FastAPI, overrides: typing.Mapping[typing.Callable, typing.Callable] 44 | ) -> None: 45 | self.overrides = overrides 46 | self._app = app 47 | self._old_overrides = {} 48 | 49 | def __enter__(self): 50 | for dep, new_dep in self.overrides.items(): 51 | if dep in self._app.dependency_overrides: 52 | # Save existing overrides 53 | self._old_overrides[dep] = self._app.dependency_overrides[dep] 54 | self._app.dependency_overrides[dep] = self._callable_replacement(new_dep) 55 | return self 56 | 57 | @staticmethod 58 | def _callable_replacement(new_dep): 59 | return new_dep if callable(new_dep) else lambda: new_dep 60 | 61 | def __exit__(self, *args: typing.Any) -> None: 62 | for dep in self.overrides.keys(): 63 | if dep in self._old_overrides: 64 | # Restore previous overrides 65 | self._app.dependency_overrides[dep] = self._old_overrides.pop(dep) 66 | else: 67 | # Just delete the entry 68 | del self._app.dependency_overrides[dep] 69 | 70 | 71 | class FixtureDependencyOverrider: 72 | def __init__(self, app: FastAPI): 73 | self.app = app 74 | 75 | def override(self, overrides: typing.Mapping[typing.Callable, typing.Callable]): 76 | return DependencyOverrider(self.app, overrides) 77 | 78 | 79 | @pytest.fixture 80 | def fastapi_dep(request): 81 | parametrized = getattr(request, "param", None) 82 | context = ( 83 | contextlib.nullcontext() 84 | if not parametrized 85 | else FixtureDependencyOverrider(parametrized[0]).override(parametrized[1]) 86 | ) 87 | with context: 88 | yield FixtureDependencyOverrider 89 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [darglint] 2 | # https://github.com/terrencepreilly/darglint 3 | strictness = long 4 | docstring_style = google 5 | -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | pytest_plugins = "pytester" 2 | -------------------------------------------------------------------------------- /tests/examples/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pksol/pytest-fastapi-deps/25a92f681e371fc0c03caaedf12b86c5b0ba84a7/tests/examples/__init__.py -------------------------------------------------------------------------------- /tests/examples/test_public_api.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from fastapi import Depends, FastAPI 3 | from fastapi.testclient import TestClient 4 | 5 | app = FastAPI() 6 | 7 | 8 | async def first_dep(): 9 | return {"skip": 0, "limit": 100} 10 | 11 | 12 | def second_dep(): 13 | return {"skip": 20, "limit": 50} 14 | 15 | 16 | @app.get("/depends/") 17 | async def get_depends( 18 | first_dep: dict = Depends(first_dep), second_dep: dict = Depends(second_dep) 19 | ): 20 | return {"first_dep": first_dep, "second_dep": second_dep} 21 | 22 | 23 | client = TestClient(app) 24 | 25 | 26 | def test_get_default_dep(): 27 | response = client.get("/depends") 28 | assert response.status_code == 200 29 | assert response.json() == { 30 | "first_dep": {"skip": 0, "limit": 100}, 31 | "second_dep": {"skip": 20, "limit": 50}, 32 | } 33 | 34 | 35 | def test_get_override_single_dep(fastapi_dep): 36 | with fastapi_dep(app).override({first_dep: lambda: {"my": "override"}}): 37 | response = client.get("/depends") 38 | assert response.status_code == 200 39 | assert response.json() == { 40 | "first_dep": {"my": "override"}, 41 | "second_dep": {"skip": 20, "limit": 50}, 42 | } 43 | 44 | 45 | def test_get_override_single_dep_plain_object(fastapi_dep): 46 | with fastapi_dep(app).override({first_dep: "override"}): 47 | response = client.get("/depends") 48 | assert response.status_code == 200 49 | assert response.json() == { 50 | "first_dep": "override", 51 | "second_dep": {"skip": 20, "limit": 50}, 52 | } 53 | 54 | 55 | def test_get_override_unrelated_dep(fastapi_dep): 56 | with fastapi_dep(app).override( 57 | { 58 | first_dep: lambda: {"my": "override"}, 59 | test_get_override_unrelated_dep: lambda: {"another": "override"}, 60 | } 61 | ): 62 | response = client.get("/depends") 63 | assert response.status_code == 200 64 | assert response.json() == { 65 | "first_dep": {"my": "override"}, 66 | "second_dep": {"skip": 20, "limit": 50}, 67 | } 68 | 69 | 70 | def my_second_override(): 71 | return {"another": "override"} 72 | 73 | 74 | def test_get_override_two_dep(fastapi_dep): 75 | with fastapi_dep(app).override( 76 | { 77 | first_dep: lambda: {"my": "override"}, 78 | second_dep: my_second_override, 79 | } 80 | ): 81 | response = client.get("/depends") 82 | assert response.status_code == 200 83 | assert response.json() == { 84 | "first_dep": {"my": "override"}, 85 | "second_dep": {"another": "override"}, 86 | } 87 | 88 | 89 | def test_get_override_dep_inner_context(fastapi_dep): 90 | with fastapi_dep(app).override({first_dep: lambda: {"my": "override"}}): 91 | response = client.get("/depends") 92 | assert response.status_code == 200 93 | assert response.json() == { 94 | "first_dep": {"my": "override"}, 95 | "second_dep": {"skip": 20, "limit": 50}, 96 | } 97 | 98 | # add another override 99 | with fastapi_dep(app).override({second_dep: lambda: {"another": "override"}}): 100 | response = client.get("/depends") 101 | assert response.status_code == 200 102 | assert response.json() == { 103 | "first_dep": {"my": "override"}, 104 | "second_dep": {"another": "override"}, 105 | } 106 | 107 | # second override is gone - expect that only the first is overridden 108 | response = client.get("/depends") 109 | assert response.status_code == 200 110 | assert response.json() == { 111 | "first_dep": {"my": "override"}, 112 | "second_dep": {"skip": 20, "limit": 50}, 113 | } 114 | 115 | # back to normal behaviour 116 | response = client.get("/depends") 117 | assert response.status_code == 200 118 | assert response.json() == { 119 | "first_dep": {"skip": 0, "limit": 100}, 120 | "second_dep": {"skip": 20, "limit": 50}, 121 | } 122 | 123 | 124 | @pytest.mark.parametrize( 125 | "fastapi_dep", 126 | [ 127 | ( 128 | app, 129 | {first_dep: lambda: {"my": "override"}}, 130 | ) 131 | ], 132 | indirect=True, 133 | ) 134 | def test_get_override_indirect_dep_param(fastapi_dep): 135 | response = client.get("/depends") 136 | assert response.status_code == 200 137 | assert response.json() == { 138 | "first_dep": {"my": "override"}, 139 | "second_dep": {"skip": 20, "limit": 50}, 140 | } 141 | -------------------------------------------------------------------------------- /tests/test_pytest_fastapi_deps.py: -------------------------------------------------------------------------------- 1 | def pytest_run_verify(testdir, test_functions): 2 | # run pytest with the following cmd args 3 | result = testdir.runpytest("-v") 4 | 5 | if test_functions: 6 | test_functions = ( 7 | test_functions if isinstance(test_functions, list) else [test_functions] 8 | ) 9 | # fnmatch_lines does an assertion internally 10 | result.stdout.fnmatch_lines( 11 | [f"*::{test_function} PASSED*" for test_function in test_functions] 12 | ) 13 | 14 | # make sure that that we get a '0' exit code for the testsuite 15 | assert result.ret == 0 16 | 17 | 18 | def test_fastapi_dep_fixture_exists(testdir): 19 | """Make sure that pytest accepts our fixture.""" 20 | 21 | # create a temporary pytest test module 22 | testdir.makepyfile( 23 | """ 24 | def test_sth(fastapi_dep): 25 | assert fastapi_dep 26 | """ 27 | ) 28 | 29 | pytest_run_verify(testdir, "test_sth") 30 | 31 | 32 | def test_public_api(testdir): 33 | testdir.copy_example("test_public_api.py") 34 | testdir.runpytest() 35 | pytest_run_verify(testdir, []) 36 | --------------------------------------------------------------------------------