├── .dockerignore ├── .env.test ├── .gcloudignore ├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── README.rst ├── assets └── img │ ├── fastapi-1.svg │ ├── firebase-cloud.png │ ├── google-cloud-run-icon.png │ └── thul-876298A8-C3E1-487D-8AD6920174E16D78.png ├── google_application_credentials.json ├── logging_config.json ├── main.py ├── poetry.lock ├── pyproject.toml ├── python_fastapi_firebase_authentication ├── __init__.py ├── core │ ├── configEnv.py │ ├── custom_logging.py │ ├── data │ │ └── mongodb │ │ │ └── db.py │ ├── env.py │ └── settings.py ├── modules │ ├── dummy │ │ ├── endpoints.py │ │ └── model.py │ └── user │ │ ├── __init__.py │ │ ├── constants.py │ │ ├── controller │ │ ├── __init__.py │ │ ├── authVerify.py │ │ └── player.py │ │ ├── data │ │ └── mongoDb │ │ │ ├── dbConstants.py │ │ │ ├── interface │ │ │ ├── authProvider.py │ │ │ ├── player.py │ │ │ └── user.py │ │ │ └── models │ │ │ ├── authProvider.py │ │ │ ├── player.py │ │ │ └── user.py │ │ ├── model │ │ └── model.py │ │ └── route │ │ └── endpoints.py └── utils │ ├── bitwise.py │ └── dependencies.py ├── serve_uvicorn.py └── tests ├── __init__.py └── test_python_fastapi_firebase_authentication.py /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | .gitignore 3 | .dockerignore 4 | .gcloudignore 5 | 6 | **/__pycache__ 7 | node_modules 8 | .serverless 9 | .env.* 10 | .DS_Store 11 | *.pyc 12 | *.pyo 13 | .venv/ 14 | .vscode 15 | sample.txt 16 | *.md 17 | *.rst 18 | *.yaml 19 | *.yml 20 | 21 | !firebase_credentials.json 22 | !.env -------------------------------------------------------------------------------- /.env.test: -------------------------------------------------------------------------------- 1 | TITLE=python_fastapi_firebase_authentication 2 | UVI_PORT=5000 3 | UVI_SERVER_HOST=0.0.0.0 4 | UVI_LOG_LEVE=info 5 | UVI_ACCESS_LOG=True 6 | UVI_RELOAD=True 7 | FASTAPI_DEBUG=True 8 | ENV=dev 9 | MONGODB_URL= 10 | DB_NAME=auth_dev 11 | GOOGLE_APPLICATION_CREDENTIALS=google_application_credentials.json 12 | CORS_ALLOWED_ORIGINS=localhost:5000,127.0.0.0:5000 13 | WEB_API_KEY= -------------------------------------------------------------------------------- /.gcloudignore: -------------------------------------------------------------------------------- 1 | .gcloudignore 2 | .git 3 | .gitignore 4 | 5 | node_modules 6 | .serverless 7 | 8 | !firebase_credentials.json 9 | !.env 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .aws 106 | .aws/ 107 | .aws/credentials 108 | .env 109 | .env.dev 110 | .venv 111 | env/ 112 | .venv/ 113 | ENV/ 114 | env.bak/ 115 | venv.bak/ 116 | 117 | # Spyder project settings 118 | .spyderproject 119 | .spyproject 120 | 121 | # Rope project settings 122 | .ropeproject 123 | 124 | # mkdocs documentation 125 | /site 126 | 127 | # mypy 128 | .mypy_cache/ 129 | .dmypy.json 130 | dmypy.json 131 | 132 | # Pyre type checker 133 | .pyre/ 134 | 135 | # zappa stuff 136 | zappa_settings.json 137 | 138 | firebase_credentials.json 139 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | FROM python:3.8-slim-buster as requirements-stage 4 | 5 | 6 | ENV PYTHONFAULTHANDLER=1 \ 7 | PYTHONUNBUFFERED=1 \ 8 | PYTHONHASHSEED=random \ 9 | PIP_NO_CACHE_DIR=off \ 10 | PIP_DISABLE_PIP_VERSION_CHECK=on \ 11 | PIP_DEFAULT_TIMEOUT=100 \ 12 | POETRY_VERSION=1.1.13 13 | 14 | # 1st stage 15 | WORKDIR /tmp 16 | 17 | # 18 | RUN pip install "poetry==$POETRY_VERSION" 19 | 20 | # 21 | COPY ./pyproject.toml ./poetry.lock* /tmp/ 22 | 23 | # 24 | RUN poetry export -f requirements.txt --output requirements.txt --without-hashes 25 | 26 | # 2nd stage 27 | FROM python:3.8-slim-buster 28 | 29 | # 30 | WORKDIR /testauthapi-docker 31 | 32 | # 33 | COPY --from=requirements-stage /tmp/requirements.txt ./requirements.txt 34 | 35 | # 36 | RUN pip install --no-cache-dir --upgrade -r ./requirements.txt 37 | 38 | 39 | COPY . . 40 | 41 | 42 | EXPOSE $PORT 43 | 44 | 45 | CMD exec gunicorn --bind :$PORT --workers 2 --worker-class uvicorn.workers.UvicornWorker main:app 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Rahul Prakash 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Python-FastAPI-Firebase-Authentication-Google-Cloud-Run-Deploy 2 | 3 | A user authentication system, implementing Google's Firebase_admin auth module inside python's FastAPI based backend. Furthe we have deployed the code on Google Cloud Run using Docker. 4 | 5 | 6 | ![Python](https://img.shields.io/badge/python-3670A0?style=for-the-badge&logo=python&logoColor=ffdd54) 7 | ![FastAPI](https://img.shields.io/badge/FastAPI-005571?style=for-the-badge&logo=fastapi) 8 | ![Swagger](https://img.shields.io/badge/-Swagger-%23Clojure?style=for-the-badge&logo=swagger&logoColor=black) 9 | ![MongoDB](https://img.shields.io/badge/MongoDB-%234ea94b.svg?style=for-the-badge&logo=mongodb&logoColor=blue) 10 | ![Firebase](https://img.shields.io/badge/firebase-%23039BE5.svg?style=for-the-badge&logo=firebase) 11 | ![Google Cloud](https://img.shields.io/badge/GoogleCloud-%234285F4.svg?style=for-the-badge&logo=google-cloud&logoColor=yellow) 12 |
13 | ![GitHub](https://img.shields.io/github/license/RahulPrakash11/Python-Firebase-Authentication-FastAPI) 14 | ![](https://img.shields.io/badge/Python-3.8-red) 15 | 16 | 17 | 18 | 19 |
20 | 21 | # Technologies used in the project: 22 | 23 | - Google's firebase-admin: 24 | - to create/register dummy user, for token generation 25 | - to authenticate user, for access 26 | - FastAPI: 27 | - implementing the backend logic to access database 28 | - Poetry: 29 | - for dependency management of our python environment 30 | - MongoDb: 31 | - an open source NoSql database storing our user information 32 | - Google Cloud Platform: 33 | - for deployment on Cloud Run 34 |
35 | 36 | # Index 37 | 38 | - **[Installation Instructions](#installation-instructions)**
39 | - **[Using Poetry](#using-poetry)**
40 | - **[Usage Instructions](#usage-instructions)**
41 | - **[Setup FastApi RESTapi](#setup-fastapi-restapi)**
42 | - **[Setup Firebase Auth](#setup-firebase-auth)**
43 | - **[Google Cloud Run Deployment](#google-cloud-run-deployment)**
44 | - **[Further Help](#further-help)**
45 | - **[License](#license)**
46 | 47 |
48 | 49 | # Installation Instructions # 50 | 51 | 52 | We will be using [poetry](https://python-poetry.org/docs/) as our python package manager. you can follow the steps to get this project running on your system. 53 | So we will start by setting up poetry on our local machine. 54 |
55 | 56 | - First clone the repo to your local machine. In your git bash UI enter the commands: 57 | 58 | ```bash 59 | > git clone 60 | > cd this-project 61 | > code . 62 | ``` 63 | ### [Using Poetry](https://python-poetry.org/docs/) 64 | - From the root directory we will install poetry if not already present in your system. Click [here](https://python-poetry.org/docs/#windows-powershell-install-instructions) for the windows install command. 65 | - After poetry gets installed, setup your virtual invironment. 66 | 67 | ```ps 68 | > poetry env use python 69 | > poetry env use 3.8 70 | ``` 71 | 72 | - Next to install dependencies run: 73 | ```ps 74 | > poetry install 75 | ``` 76 | 77 | Note : `Poetry takes care of installing these dependencies in your virtual environment. You don't have to activate your virtual environment manually every time.` 78 | 79 |
80 | 81 | 82 | **Before we could the test the setup we need following to be ready with us :** 83 | 84 | 85 | - **Mongodb Atlas**, to store user information to authenticate against. You can use [free service](https://www.mongodb.com/docs/atlas/tutorial/deploy-free-tier-cluster/) provided by MongoDb for this project. 86 | - [**Firebase Admin**](https://firebase.google.com/docs/admin/setup#set-up-project-and-service-account) credentials to be able to generate tokens and verify against. 87 | - create an empty **`.env`** file at the base directory of your project and update the [`.env.test`](.env.test) file. 88 | - provide [mongodb connection string](https://www.mongodb.com/docs/guides/atlas/connection-string/) to `MONGODB_URL` 89 | - create [firebase credentials.json](google_application_credentials.json) file in the root directory of the project or, otherwise, provide the absolute path to credentials file in .env to `GOOGLE_APPLICATION_CREDENTIALS` 90 | 91 | We are all set to run our test. 92 | 93 |
94 | 95 | # Usage Instructions # 96 | 97 | Poetry provides for custom scripts to be run in cli. You can access/write these scripts in [pyproject.toml](pyproject.toml). 98 | 99 | To run locally run these commands: 100 | 101 | ```ps 102 | > poetry run test (to setup environment test) 103 | > poetry run server (to serve uvicorn) 104 | ``` 105 | FastApi provides Swagger UI: served at /docs. 106 | In your bowser go to : 107 | > http://localhost:5000/docs 108 | 109 | You can register a dummy user using email and password to authenticate further, via : 110 | >http://localhost:5000/docs#/dummy/create_user_dummy_register_post 111 | 112 | Next, login through above credentials to get token from firebase auth provider to authenticate, via : 113 | >http://localhost:5000/docs#/dummy/login 114 | 115 | Use the token obtained above to verify/register a user, via : 116 | > http://localhost:5000/docs#/auth/verify 117 | 118 | In response you get details of new player created in your database which can be acceesed/modified through token-based authentication only, via : 119 | > http://localhost:5000/docs#/player/{id} 120 | 121 | 122 |
123 | 124 | # Setup FastApi RESTapi 125 | 126 | To get more familier with Fastapi you can go through their [docs](https://fastapi.tiangolo.com/). 127 | 128 | Here, we will be using **[uvicorn](https://www.uvicorn.org/deployment/)** to serve our FastApi backend. 129 | 130 | We have also used **Beanie** as an ODM interface to MongoDb, with async capabilities. "Beanie - is an Asynchronous Python object-document mapper (ODM) for MongoDB, based on **Motor** and **Pydantic**."You can follow the Beanie's documentation [**here**](https://roman-right.github.io/beanie/). 131 | 132 |
133 | 134 | # Setup Firebase Auth 135 | 136 | One can use the Firebase Admin SDK to manage your users or to manage authentication tokens. As stated in their [**documentation**](https://firebase.google.com/docs/auth/admin), You can also use the service to identify these users on your own server. This lets you securely perform server-side logic on behalf of users that have signed in with Firebase Authentication. 137 | 138 | To do this, you can retrieve an ID token from a client application signed in with Firebase Authentication and include the token in a request to your server. Your server then [**verifies the ID token**](python_fastapi_firebase_authentication\utils\dependencies.py) and extracts the claims that identify the user (including their uid, the identity provider they logged in with, etc.). This identity information can then be used by your server to carry out actions on behalf of the user. 139 | 140 |
141 | 142 | # Google Cloud Run Deployment 143 | 144 | For this project we have used Google's Cloud Run to deploy my Dockerised FastApi. 145 | You can follow this link for more help: 146 | - https://towardsdatascience.com/deploy-a-dockerized-fastapi-app-to-google-cloud-platform-24f72266c7ef 147 | 148 | I will list the step-wise process involved in deployment: 149 | - **Pre-requisites :** 150 | - A Billing Account on GCP/GOOGLE CLOUD PLATFORM(though this setup will cost $0) 151 | - Create [**Dockerfile**](Dockerfile) to dockerise your fastapi code 152 | - Create [**.dockerignore**](.dockerignore) and [**.gcloudignore**](.gcloudignore) files(to enumerate what you need to push and what not) 153 | - Note : Here we have also used .gitignore to whose restrictions .gcloudignore [automatically inherits](https://cloud.google.com/sdk/gcloud/reference/topic/gcloudignore). 154 | - Make sure you have not ignored [.env](.env) and [google_application_credentials.json](google_application_credentials.json) 155 | - Next follow the instruction in the link provided above. 156 | - Also make sure [**gcloud cli**](https://cloud.google.com/sdk/docs/install) is installed and configured on your system. 157 | - gcloud cli commands to be entered for deployment: 158 | 159 | Replace PROJECT-ID with your GCP project ID 160 | ```ps 161 | 162 | TO view your project ID by running the command 163 | > gcloud config get-value project 164 | 165 | TO SET your project ID: 166 | gcloud config set project PROJECT-ID 167 | > gcloud config set project auth-test 168 | 169 | TO BUILD: 170 | gcloud builds submit --tag gcr.io/PROJECT-ID/container-name 171 | > gcloud builds submit --tag gcr.io/auth-test/auth-api-container 172 | 173 | TO DEPLOY: 174 | gcloud run deploy --image gcr.io/PROJECT-ID/container-name --platform managed 175 | > gcloud run deploy --image gcr.io/auth-test/auth-api-container --platform managed 176 | 177 | TO DELETE: 178 | gcloud deployment-manager deployments delete example-deployment --delete-policy=DELETE 179 | 180 | ``` 181 | 182 |
183 | 184 | 185 | # Further Help 186 | 187 | This project is an open-source initiative by Junkie Labs team. 188 | 189 | For any questions or suggestions send a mail to junkielabs.dev@gmail.com or chat with the core-team on gitter. 190 | 191 | 192 | [![Gitter](https://badges.gitter.im/nubar-api/django-dynamodb-lambda-function.svg)](https://gitter.im/nubar-api/django-dynamodb-lambda-function?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) 193 | 194 |
195 | 196 | # License 197 | 198 | [MIT License](/LICENSE). 199 | 200 | 201 |
202 | 203 | 204 | 205 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | gunicorn main:app --workers 4 --worker-class uvicorn.workers.UvicornWorker --bind 0.0.0.0:80 2 | 3 | poetry export -f requirements.txt --output requirements.txt --without-hashes 4 | 5 | gcloud builds submit --tag gcr.io/zone-india/auth-api-container 6 | 7 | gcloud run deploy --image gcr.io/zone-india/auth-api-container --platform managed -------------------------------------------------------------------------------- /assets/img/fastapi-1.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/img/firebase-cloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rahulprakash11/Python-Firebase-Authentication-FastAPI/7341e6a223eca6388bb22047567777e52b088d9e/assets/img/firebase-cloud.png -------------------------------------------------------------------------------- /assets/img/google-cloud-run-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rahulprakash11/Python-Firebase-Authentication-FastAPI/7341e6a223eca6388bb22047567777e52b088d9e/assets/img/google-cloud-run-icon.png -------------------------------------------------------------------------------- /assets/img/thul-876298A8-C3E1-487D-8AD6920174E16D78.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rahulprakash11/Python-Firebase-Authentication-FastAPI/7341e6a223eca6388bb22047567777e52b088d9e/assets/img/thul-876298A8-C3E1-487D-8AD6920174E16D78.png -------------------------------------------------------------------------------- /google_application_credentials.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "service_account", 3 | "project_id": "xxxxx", 4 | "private_key_id": "xxxxxxxxxxxxxxxxxx", 5 | "private_key": "-----BEGIN PRIVATE KEY-----\nxxxxxxxxxxxxx\n-----END PRIVATE KEY-----\n", 6 | "client_email": "firebase-adminsdk-asdf@.iam.gserviceaccount.com", 7 | "client_id": "xxxxxxxxxxxxxx", 8 | "auth_uri": "https://accounts.google.com/o/oauth2/auth", 9 | "token_uri": "https://oauth2.googleapis.com/token", 10 | "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", 11 | "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-asdf%40.iam.gserviceaccount.com" 12 | } 13 | -------------------------------------------------------------------------------- /logging_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "logger": { 3 | "filename": "access.log", 4 | "level": "debug", 5 | "rotation": "20 days", 6 | "retention": "1 months", 7 | "format": "{level: <8} {time:YYYY-MM-DD HH:mm:ss.SSS} - {name}:{function}:{line} - {message}" 8 | 9 | } 10 | } -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | 2 | from loguru import logger 3 | from firebase_admin import initialize_app 4 | 5 | from fastapi import FastAPI 6 | from fastapi.middleware.cors import CORSMiddleware 7 | 8 | from python_fastapi_firebase_authentication.core.env import TITLE, DEBUG, ALLOWED_ORIGINS, LOGGING_CONFIG_PATH 9 | from python_fastapi_firebase_authentication.core.custom_logging import CustomizeLogger 10 | from python_fastapi_firebase_authentication.core.data.mongodb.db import init_db, mongodb_client 11 | from python_fastapi_firebase_authentication.modules.user.route.endpoints import userRouter 12 | from python_fastapi_firebase_authentication.modules.dummy.endpoints import dummyRouter 13 | 14 | 15 | def get_application(): 16 | _app = FastAPI(title=TITLE, debug=DEBUG) 17 | 18 | logger = CustomizeLogger.make_logger(LOGGING_CONFIG_PATH) 19 | _app.logger = logger 20 | _app.include_router(router=userRouter) 21 | _app.include_router(router=dummyRouter) 22 | _app.add_middleware( 23 | CORSMiddleware, 24 | allow_origins=["*"],#ALLOWED_ORIGINS 25 | allow_credentials=True, 26 | allow_methods=["*"], 27 | allow_headers=["*"], 28 | ) 29 | return _app 30 | 31 | app = get_application() 32 | # ... some code skipped 33 | 34 | @app.get("/") 35 | def read_root(): 36 | return {"Hello App Users": "Welcome to the app!"} 37 | 38 | 39 | @app.on_event("startup") 40 | async def app_init(): 41 | logger.debug("---Initializing---") 42 | await init_db(mongodb_client) 43 | logger.debug("---Initialized the database---") 44 | initialize_app() 45 | logger.debug("---Initialized the firebase admin---") 46 | -------------------------------------------------------------------------------- /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 | 13 | [package.extras] 14 | doc = ["packaging", "sphinx-rtd-theme", "sphinx-autodoc-typehints (>=1.2.0)"] 15 | 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)"] 16 | trio = ["trio (>=0.16)"] 17 | 18 | [[package]] 19 | name = "atomicwrites" 20 | version = "1.4.1" 21 | description = "Atomic file writes." 22 | category = "dev" 23 | optional = false 24 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 25 | 26 | [[package]] 27 | name = "attrs" 28 | version = "22.1.0" 29 | description = "Classes Without Boilerplate" 30 | category = "dev" 31 | optional = false 32 | python-versions = ">=3.5" 33 | 34 | [package.extras] 35 | 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"] 36 | docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] 37 | tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "zope.interface", "cloudpickle"] 38 | tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "cloudpickle"] 39 | 40 | [[package]] 41 | name = "beanie" 42 | version = "1.11.9" 43 | description = "Asynchronous Python ODM for MongoDB" 44 | category = "main" 45 | optional = false 46 | python-versions = ">=3.7,<4.0" 47 | 48 | [package.dependencies] 49 | click = ">=7" 50 | motor = ">=2.5,<4.0" 51 | pydantic = ">=1.9.0" 52 | toml = "*" 53 | yarl = ">=1.6" 54 | 55 | [[package]] 56 | name = "cachecontrol" 57 | version = "0.12.11" 58 | description = "httplib2 caching for requests" 59 | category = "main" 60 | optional = false 61 | python-versions = ">=3.6" 62 | 63 | [package.dependencies] 64 | msgpack = ">=0.5.2" 65 | requests = "*" 66 | 67 | [package.extras] 68 | filecache = ["lockfile (>=0.9)"] 69 | redis = ["redis (>=2.10.5)"] 70 | 71 | [[package]] 72 | name = "cachetools" 73 | version = "5.2.0" 74 | description = "Extensible memoizing collections and decorators" 75 | category = "main" 76 | optional = false 77 | python-versions = "~=3.7" 78 | 79 | [[package]] 80 | name = "certifi" 81 | version = "2022.6.15" 82 | description = "Python package for providing Mozilla's CA Bundle." 83 | category = "main" 84 | optional = false 85 | python-versions = ">=3.6" 86 | 87 | [[package]] 88 | name = "charset-normalizer" 89 | version = "2.1.1" 90 | description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." 91 | category = "main" 92 | optional = false 93 | python-versions = ">=3.6.0" 94 | 95 | [package.extras] 96 | unicode_backport = ["unicodedata2"] 97 | 98 | [[package]] 99 | name = "click" 100 | version = "8.1.3" 101 | description = "Composable command line interface toolkit" 102 | category = "main" 103 | optional = false 104 | python-versions = ">=3.7" 105 | 106 | [package.dependencies] 107 | colorama = {version = "*", markers = "platform_system == \"Windows\""} 108 | 109 | [[package]] 110 | name = "colorama" 111 | version = "0.4.5" 112 | description = "Cross-platform colored terminal text." 113 | category = "main" 114 | optional = false 115 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 116 | 117 | [[package]] 118 | name = "dnspython" 119 | version = "2.2.1" 120 | description = "DNS toolkit" 121 | category = "main" 122 | optional = false 123 | python-versions = ">=3.6,<4.0" 124 | 125 | [package.extras] 126 | dnssec = ["cryptography (>=2.6,<37.0)"] 127 | curio = ["curio (>=1.2,<2.0)", "sniffio (>=1.1,<2.0)"] 128 | doh = ["h2 (>=4.1.0)", "httpx (>=0.21.1)", "requests (>=2.23.0,<3.0.0)", "requests-toolbelt (>=0.9.1,<0.10.0)"] 129 | idna = ["idna (>=2.1,<4.0)"] 130 | trio = ["trio (>=0.14,<0.20)"] 131 | wmi = ["wmi (>=1.5.1,<2.0.0)"] 132 | 133 | [[package]] 134 | name = "email-validator" 135 | version = "1.2.1" 136 | description = "A robust email syntax and deliverability validation library." 137 | category = "main" 138 | optional = false 139 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" 140 | 141 | [package.dependencies] 142 | dnspython = ">=1.15.0" 143 | idna = ">=2.0.0" 144 | 145 | [[package]] 146 | name = "fastapi" 147 | version = "0.81.0" 148 | description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" 149 | category = "main" 150 | optional = false 151 | python-versions = ">=3.6.1" 152 | 153 | [package.dependencies] 154 | 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" 155 | starlette = "0.19.1" 156 | 157 | [package.extras] 158 | 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)"] 159 | 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,<6.0.0)", "uvicorn[standard] (>=0.12.0,<0.18.0)", "pre-commit (>=2.17.0,<3.0.0)"] 160 | 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)"] 161 | test = ["pytest (>=6.2.4,<7.0.0)", "pytest-cov (>=2.12.0,<4.0.0)", "mypy (==0.910)", "flake8 (>=3.8.3,<6.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)"] 162 | 163 | [[package]] 164 | name = "firebase-admin" 165 | version = "5.3.0" 166 | description = "Firebase Admin Python SDK" 167 | category = "main" 168 | optional = false 169 | python-versions = ">=3.6" 170 | 171 | [package.dependencies] 172 | cachecontrol = ">=0.12.6" 173 | google-api-core = {version = ">=1.22.1,<3.0.0dev", extras = ["grpc"], markers = "platform_python_implementation != \"PyPy\""} 174 | google-api-python-client = ">=1.7.8" 175 | google-cloud-firestore = {version = ">=2.1.0", markers = "platform_python_implementation != \"PyPy\""} 176 | google-cloud-storage = ">=1.37.1" 177 | 178 | [[package]] 179 | name = "google-api-core" 180 | version = "2.8.2" 181 | description = "Google API client core library" 182 | category = "main" 183 | optional = false 184 | python-versions = ">=3.6" 185 | 186 | [package.dependencies] 187 | google-auth = ">=1.25.0,<3.0dev" 188 | googleapis-common-protos = ">=1.56.2,<2.0dev" 189 | grpcio = {version = ">=1.33.2,<2.0dev", optional = true, markers = "extra == \"grpc\""} 190 | grpcio-status = {version = ">=1.33.2,<2.0dev", optional = true, markers = "extra == \"grpc\""} 191 | protobuf = ">=3.15.0,<5.0.0dev" 192 | requests = ">=2.18.0,<3.0.0dev" 193 | 194 | [package.extras] 195 | grpc = ["grpcio (>=1.33.2,<2.0dev)", "grpcio-status (>=1.33.2,<2.0dev)"] 196 | 197 | [[package]] 198 | name = "google-api-python-client" 199 | version = "2.58.0" 200 | description = "Google API Client Library for Python" 201 | category = "main" 202 | optional = false 203 | python-versions = ">=3.7" 204 | 205 | [package.dependencies] 206 | google-api-core = ">=1.31.5,<2.0.0 || >2.3.0,<3.0.0dev" 207 | google-auth = ">=1.19.0,<3.0.0dev" 208 | google-auth-httplib2 = ">=0.1.0" 209 | httplib2 = ">=0.15.0,<1dev" 210 | uritemplate = ">=3.0.1,<5" 211 | 212 | [[package]] 213 | name = "google-auth" 214 | version = "2.11.0" 215 | description = "Google Authentication Library" 216 | category = "main" 217 | optional = false 218 | python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*" 219 | 220 | [package.dependencies] 221 | cachetools = ">=2.0.0,<6.0" 222 | pyasn1-modules = ">=0.2.1" 223 | rsa = {version = ">=3.1.4,<5", markers = "python_version >= \"3.6\""} 224 | six = ">=1.9.0" 225 | 226 | [package.extras] 227 | reauth = ["pyu2f (>=0.1.5)"] 228 | pyopenssl = ["pyopenssl (>=20.0.0)"] 229 | enterprise_cert = ["pyopenssl (==22.0.0)", "cryptography (==36.0.2)"] 230 | aiohttp = ["aiohttp (>=3.6.2,<4.0.0dev)", "requests (>=2.20.0,<3.0.0dev)"] 231 | 232 | [[package]] 233 | name = "google-auth-httplib2" 234 | version = "0.1.0" 235 | description = "Google Authentication Library: httplib2 transport" 236 | category = "main" 237 | optional = false 238 | python-versions = "*" 239 | 240 | [package.dependencies] 241 | google-auth = "*" 242 | httplib2 = ">=0.15.0" 243 | six = "*" 244 | 245 | [[package]] 246 | name = "google-cloud-core" 247 | version = "2.3.2" 248 | description = "Google Cloud API client core library" 249 | category = "main" 250 | optional = false 251 | python-versions = ">=3.7" 252 | 253 | [package.dependencies] 254 | google-api-core = ">=1.31.6,<2.0.0 || >2.3.0,<3.0.0dev" 255 | google-auth = ">=1.25.0,<3.0dev" 256 | 257 | [package.extras] 258 | grpc = ["grpcio (>=1.38.0,<2.0dev)"] 259 | 260 | [[package]] 261 | name = "google-cloud-firestore" 262 | version = "2.6.1" 263 | description = "Google Cloud Firestore API client library" 264 | category = "main" 265 | optional = false 266 | python-versions = ">=3.7" 267 | 268 | [package.dependencies] 269 | google-api-core = {version = ">=1.32.0,<2.0.0 || >=2.8.0,<3.0.0dev", extras = ["grpc"]} 270 | google-cloud-core = ">=1.4.1,<3.0.0dev" 271 | proto-plus = ">=1.22.0,<2.0.0dev" 272 | protobuf = ">=3.19.0,<5.0.0dev" 273 | 274 | [[package]] 275 | name = "google-cloud-storage" 276 | version = "2.5.0" 277 | description = "Google Cloud Storage API client library" 278 | category = "main" 279 | optional = false 280 | python-versions = ">=3.7" 281 | 282 | [package.dependencies] 283 | google-api-core = ">=1.31.5,<2.0.0 || >2.3.0,<3.0.0dev" 284 | google-auth = ">=1.25.0,<3.0dev" 285 | google-cloud-core = ">=2.3.0,<3.0dev" 286 | google-resumable-media = ">=2.3.2" 287 | requests = ">=2.18.0,<3.0.0dev" 288 | 289 | [package.extras] 290 | protobuf = ["protobuf (<5.0.0dev)"] 291 | 292 | [[package]] 293 | name = "google-crc32c" 294 | version = "1.3.0" 295 | description = "A python wrapper of the C library 'Google CRC32C'" 296 | category = "main" 297 | optional = false 298 | python-versions = ">=3.6" 299 | 300 | [package.extras] 301 | testing = ["pytest"] 302 | 303 | [[package]] 304 | name = "google-resumable-media" 305 | version = "2.3.3" 306 | description = "Utilities for Google Media Downloads and Resumable Uploads" 307 | category = "main" 308 | optional = false 309 | python-versions = ">= 3.6" 310 | 311 | [package.dependencies] 312 | google-crc32c = ">=1.0,<2.0dev" 313 | 314 | [package.extras] 315 | aiohttp = ["aiohttp (>=3.6.2,<4.0.0dev)"] 316 | requests = ["requests (>=2.18.0,<3.0.0dev)"] 317 | 318 | [[package]] 319 | name = "googleapis-common-protos" 320 | version = "1.56.4" 321 | description = "Common protobufs used in Google APIs" 322 | category = "main" 323 | optional = false 324 | python-versions = ">=3.7" 325 | 326 | [package.dependencies] 327 | protobuf = ">=3.15.0,<5.0.0dev" 328 | 329 | [package.extras] 330 | grpc = ["grpcio (>=1.0.0,<2.0.0dev)"] 331 | 332 | [[package]] 333 | name = "grpcio" 334 | version = "1.48.0" 335 | description = "HTTP/2-based RPC framework" 336 | category = "main" 337 | optional = false 338 | python-versions = ">=3.6" 339 | 340 | [package.dependencies] 341 | six = ">=1.5.2" 342 | 343 | [package.extras] 344 | protobuf = ["grpcio-tools (>=1.48.0)"] 345 | 346 | [[package]] 347 | name = "grpcio-status" 348 | version = "1.48.0" 349 | description = "Status proto mapping for gRPC" 350 | category = "main" 351 | optional = false 352 | python-versions = ">=3.6" 353 | 354 | [package.dependencies] 355 | googleapis-common-protos = ">=1.5.5" 356 | grpcio = ">=1.48.0" 357 | protobuf = ">=3.12.0" 358 | 359 | [[package]] 360 | name = "gunicorn" 361 | version = "20.1.0" 362 | description = "WSGI HTTP Server for UNIX" 363 | category = "main" 364 | optional = false 365 | python-versions = ">=3.5" 366 | 367 | [package.extras] 368 | eventlet = ["eventlet (>=0.24.1)"] 369 | gevent = ["gevent (>=1.4.0)"] 370 | setproctitle = ["setproctitle"] 371 | tornado = ["tornado (>=0.2)"] 372 | 373 | [[package]] 374 | name = "h11" 375 | version = "0.13.0" 376 | description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" 377 | category = "main" 378 | optional = false 379 | python-versions = ">=3.6" 380 | 381 | [[package]] 382 | name = "httplib2" 383 | version = "0.20.4" 384 | description = "A comprehensive HTTP client library." 385 | category = "main" 386 | optional = false 387 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 388 | 389 | [package.dependencies] 390 | pyparsing = {version = ">=2.4.2,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.0.2 || >3.0.2,<3.0.3 || >3.0.3,<4", markers = "python_version > \"3.0\""} 391 | 392 | [[package]] 393 | name = "httptools" 394 | version = "0.4.0" 395 | description = "A collection of framework independent HTTP protocol utils." 396 | category = "main" 397 | optional = false 398 | python-versions = ">=3.5.0" 399 | 400 | [package.extras] 401 | test = ["Cython (>=0.29.24,<0.30.0)"] 402 | 403 | [[package]] 404 | name = "idna" 405 | version = "3.3" 406 | description = "Internationalized Domain Names in Applications (IDNA)" 407 | category = "main" 408 | optional = false 409 | python-versions = ">=3.5" 410 | 411 | [[package]] 412 | name = "loguru" 413 | version = "0.6.0" 414 | description = "Python logging made (stupidly) simple" 415 | category = "main" 416 | optional = false 417 | python-versions = ">=3.5" 418 | 419 | [package.dependencies] 420 | colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""} 421 | win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""} 422 | 423 | [package.extras] 424 | dev = ["colorama (>=0.3.4)", "docutils (==0.16)", "flake8 (>=3.7.7)", "tox (>=3.9.0)", "pytest (>=4.6.2)", "pytest-cov (>=2.7.1)", "black (>=19.10b0)", "isort (>=5.1.1)", "Sphinx (>=4.1.1)", "sphinx-autobuild (>=0.7.1)", "sphinx-rtd-theme (>=0.4.3)"] 425 | 426 | [[package]] 427 | name = "more-itertools" 428 | version = "8.14.0" 429 | description = "More routines for operating on iterables, beyond itertools" 430 | category = "dev" 431 | optional = false 432 | python-versions = ">=3.5" 433 | 434 | [[package]] 435 | name = "motor" 436 | version = "3.0.0" 437 | description = "Non-blocking MongoDB driver for Tornado or asyncio" 438 | category = "main" 439 | optional = false 440 | python-versions = ">=3.7" 441 | 442 | [package.dependencies] 443 | pymongo = ">=4.1,<5" 444 | 445 | [package.extras] 446 | aws = ["pymongo[aws] (>=4.1,<5)"] 447 | encryption = ["pymongo[encryption] (>=4.1,<5)"] 448 | gssapi = ["pymongo[gssapi] (>=4.1,<5)"] 449 | ocsp = ["pymongo[ocsp] (>=4.1,<5)"] 450 | snappy = ["pymongo[snappy] (>=4.1,<5)"] 451 | srv = ["pymongo[srv] (>=4.1,<5)"] 452 | zstd = ["pymongo[zstd] (>=4.1,<5)"] 453 | 454 | [[package]] 455 | name = "msgpack" 456 | version = "1.0.4" 457 | description = "MessagePack serializer" 458 | category = "main" 459 | optional = false 460 | python-versions = "*" 461 | 462 | [[package]] 463 | name = "multidict" 464 | version = "6.0.2" 465 | description = "multidict implementation" 466 | category = "main" 467 | optional = false 468 | python-versions = ">=3.7" 469 | 470 | [[package]] 471 | name = "packaging" 472 | version = "21.3" 473 | description = "Core utilities for Python packages" 474 | category = "dev" 475 | optional = false 476 | python-versions = ">=3.6" 477 | 478 | [package.dependencies] 479 | pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" 480 | 481 | [[package]] 482 | name = "pluggy" 483 | version = "0.13.1" 484 | description = "plugin and hook calling mechanisms for python" 485 | category = "dev" 486 | optional = false 487 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 488 | 489 | [package.extras] 490 | dev = ["pre-commit", "tox"] 491 | 492 | [[package]] 493 | name = "proto-plus" 494 | version = "1.22.1" 495 | description = "Beautiful, Pythonic protocol buffers." 496 | category = "main" 497 | optional = false 498 | python-versions = ">=3.6" 499 | 500 | [package.dependencies] 501 | protobuf = ">=3.19.0,<5.0.0dev" 502 | 503 | [package.extras] 504 | testing = ["google-api-core[grpc] (>=1.31.5)"] 505 | 506 | [[package]] 507 | name = "protobuf" 508 | version = "4.21.5" 509 | description = "" 510 | category = "main" 511 | optional = false 512 | python-versions = ">=3.7" 513 | 514 | [[package]] 515 | name = "py" 516 | version = "1.11.0" 517 | description = "library with cross-python path, ini-parsing, io, code, log facilities" 518 | category = "dev" 519 | optional = false 520 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 521 | 522 | [[package]] 523 | name = "pyasn1" 524 | version = "0.4.8" 525 | description = "ASN.1 types and codecs" 526 | category = "main" 527 | optional = false 528 | python-versions = "*" 529 | 530 | [[package]] 531 | name = "pyasn1-modules" 532 | version = "0.2.8" 533 | description = "A collection of ASN.1-based protocols modules." 534 | category = "main" 535 | optional = false 536 | python-versions = "*" 537 | 538 | [package.dependencies] 539 | pyasn1 = ">=0.4.6,<0.5.0" 540 | 541 | [[package]] 542 | name = "pydantic" 543 | version = "1.10.1" 544 | description = "Data validation and settings management using python type hints" 545 | category = "main" 546 | optional = false 547 | python-versions = ">=3.7" 548 | 549 | [package.dependencies] 550 | typing-extensions = ">=4.1.0" 551 | 552 | [package.extras] 553 | dotenv = ["python-dotenv (>=0.10.4)"] 554 | email = ["email-validator (>=1.0.3)"] 555 | 556 | [[package]] 557 | name = "pymongo" 558 | version = "4.2.0" 559 | description = "Python driver for MongoDB " 560 | category = "main" 561 | optional = false 562 | python-versions = ">=3.7" 563 | 564 | [package.extras] 565 | aws = ["pymongo-auth-aws (<2.0.0)"] 566 | encryption = ["pymongocrypt (>=1.3.0,<2.0.0)"] 567 | gssapi = ["pykerberos"] 568 | ocsp = ["pyopenssl (>=17.2.0)", "requests (<3.0.0)", "service-identity (>=18.1.0)", "certifi"] 569 | snappy = ["python-snappy"] 570 | srv = ["dnspython (>=1.16.0,<3.0.0)"] 571 | zstd = ["zstandard"] 572 | 573 | [[package]] 574 | name = "pyparsing" 575 | version = "3.0.9" 576 | description = "pyparsing module - Classes and methods to define and execute parsing grammars" 577 | category = "main" 578 | optional = false 579 | python-versions = ">=3.6.8" 580 | 581 | [package.extras] 582 | diagrams = ["railroad-diagrams", "jinja2"] 583 | 584 | [[package]] 585 | name = "pytest" 586 | version = "5.4.3" 587 | description = "pytest: simple powerful testing with Python" 588 | category = "dev" 589 | optional = false 590 | python-versions = ">=3.5" 591 | 592 | [package.dependencies] 593 | atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} 594 | attrs = ">=17.4.0" 595 | colorama = {version = "*", markers = "sys_platform == \"win32\""} 596 | more-itertools = ">=4.0.0" 597 | packaging = "*" 598 | pluggy = ">=0.12,<1.0" 599 | py = ">=1.5.0" 600 | wcwidth = "*" 601 | 602 | [package.extras] 603 | checkqa-mypy = ["mypy (==v0.761)"] 604 | testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] 605 | 606 | [[package]] 607 | name = "python-dotenv" 608 | version = "0.20.0" 609 | description = "Read key-value pairs from a .env file and set them as environment variables" 610 | category = "main" 611 | optional = false 612 | python-versions = ">=3.5" 613 | 614 | [package.extras] 615 | cli = ["click (>=5.0)"] 616 | 617 | [[package]] 618 | name = "pyyaml" 619 | version = "6.0" 620 | description = "YAML parser and emitter for Python" 621 | category = "main" 622 | optional = false 623 | python-versions = ">=3.6" 624 | 625 | [[package]] 626 | name = "requests" 627 | version = "2.28.1" 628 | description = "Python HTTP for Humans." 629 | category = "main" 630 | optional = false 631 | python-versions = ">=3.7, <4" 632 | 633 | [package.dependencies] 634 | certifi = ">=2017.4.17" 635 | charset-normalizer = ">=2,<3" 636 | idna = ">=2.5,<4" 637 | urllib3 = ">=1.21.1,<1.27" 638 | 639 | [package.extras] 640 | socks = ["PySocks (>=1.5.6,!=1.5.7)"] 641 | use_chardet_on_py3 = ["chardet (>=3.0.2,<6)"] 642 | 643 | [[package]] 644 | name = "rsa" 645 | version = "4.9" 646 | description = "Pure-Python RSA implementation" 647 | category = "main" 648 | optional = false 649 | python-versions = ">=3.6,<4" 650 | 651 | [package.dependencies] 652 | pyasn1 = ">=0.1.3" 653 | 654 | [[package]] 655 | name = "six" 656 | version = "1.16.0" 657 | description = "Python 2 and 3 compatibility utilities" 658 | category = "main" 659 | optional = false 660 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" 661 | 662 | [[package]] 663 | name = "sniffio" 664 | version = "1.2.0" 665 | description = "Sniff out which async library your code is running under" 666 | category = "main" 667 | optional = false 668 | python-versions = ">=3.5" 669 | 670 | [[package]] 671 | name = "starlette" 672 | version = "0.19.1" 673 | description = "The little ASGI library that shines." 674 | category = "main" 675 | optional = false 676 | python-versions = ">=3.6" 677 | 678 | [package.dependencies] 679 | anyio = ">=3.4.0,<5" 680 | typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""} 681 | 682 | [package.extras] 683 | full = ["itsdangerous", "jinja2", "python-multipart", "pyyaml", "requests"] 684 | 685 | [[package]] 686 | name = "toml" 687 | version = "0.10.2" 688 | description = "Python Library for Tom's Obvious, Minimal Language" 689 | category = "main" 690 | optional = false 691 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" 692 | 693 | [[package]] 694 | name = "typing-extensions" 695 | version = "4.3.0" 696 | description = "Backported and Experimental Type Hints for Python 3.7+" 697 | category = "main" 698 | optional = false 699 | python-versions = ">=3.7" 700 | 701 | [[package]] 702 | name = "uritemplate" 703 | version = "4.1.1" 704 | description = "Implementation of RFC 6570 URI Templates" 705 | category = "main" 706 | optional = false 707 | python-versions = ">=3.6" 708 | 709 | [[package]] 710 | name = "urllib3" 711 | version = "1.26.12" 712 | description = "HTTP library with thread-safe connection pooling, file post, and more." 713 | category = "main" 714 | optional = false 715 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4" 716 | 717 | [package.extras] 718 | brotli = ["brotlicffi (>=0.8.0)", "brotli (>=1.0.9)", "brotlipy (>=0.6.0)"] 719 | secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "urllib3-secure-extra", "ipaddress"] 720 | socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] 721 | 722 | [[package]] 723 | name = "uvicorn" 724 | version = "0.18.3" 725 | description = "The lightning-fast ASGI server." 726 | category = "main" 727 | optional = false 728 | python-versions = ">=3.7" 729 | 730 | [package.dependencies] 731 | click = ">=7.0" 732 | colorama = {version = ">=0.4", optional = true, markers = "sys_platform == \"win32\" and extra == \"standard\""} 733 | h11 = ">=0.8" 734 | httptools = {version = ">=0.4.0", optional = true, markers = "extra == \"standard\""} 735 | python-dotenv = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} 736 | pyyaml = {version = ">=5.1", optional = true, markers = "extra == \"standard\""} 737 | uvloop = {version = ">=0.14.0,<0.15.0 || >0.15.0,<0.15.1 || >0.15.1", optional = true, markers = "sys_platform != \"win32\" and sys_platform != \"cygwin\" and platform_python_implementation != \"PyPy\" and extra == \"standard\""} 738 | watchfiles = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} 739 | websockets = {version = ">=10.0", optional = true, markers = "extra == \"standard\""} 740 | 741 | [package.extras] 742 | standard = ["colorama (>=0.4)", "httptools (>=0.4.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.0)"] 743 | 744 | [[package]] 745 | name = "uvloop" 746 | version = "0.16.0" 747 | description = "Fast implementation of asyncio event loop on top of libuv" 748 | category = "main" 749 | optional = false 750 | python-versions = ">=3.7" 751 | 752 | [package.extras] 753 | dev = ["Cython (>=0.29.24,<0.30.0)", "pytest (>=3.6.0)", "Sphinx (>=4.1.2,<4.2.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "aiohttp", "flake8 (>=3.9.2,<3.10.0)", "psutil", "pycodestyle (>=2.7.0,<2.8.0)", "pyOpenSSL (>=19.0.0,<19.1.0)", "mypy (>=0.800)"] 754 | docs = ["Sphinx (>=4.1.2,<4.2.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)"] 755 | test = ["aiohttp", "flake8 (>=3.9.2,<3.10.0)", "psutil", "pycodestyle (>=2.7.0,<2.8.0)", "pyOpenSSL (>=19.0.0,<19.1.0)", "mypy (>=0.800)"] 756 | 757 | [[package]] 758 | name = "watchfiles" 759 | version = "0.16.1" 760 | description = "Simple, modern and high performance file watching and code reload in python." 761 | category = "main" 762 | optional = false 763 | python-versions = ">=3.7" 764 | 765 | [package.dependencies] 766 | anyio = ">=3.0.0,<4" 767 | 768 | [[package]] 769 | name = "wcwidth" 770 | version = "0.2.5" 771 | description = "Measures the displayed width of unicode strings in a terminal" 772 | category = "dev" 773 | optional = false 774 | python-versions = "*" 775 | 776 | [[package]] 777 | name = "websockets" 778 | version = "10.3" 779 | description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" 780 | category = "main" 781 | optional = false 782 | python-versions = ">=3.7" 783 | 784 | [[package]] 785 | name = "win32-setctime" 786 | version = "1.1.0" 787 | description = "A small Python utility to set file creation time on Windows" 788 | category = "main" 789 | optional = false 790 | python-versions = ">=3.5" 791 | 792 | [package.extras] 793 | dev = ["pytest (>=4.6.2)", "black (>=19.3b0)"] 794 | 795 | [[package]] 796 | name = "yarl" 797 | version = "1.8.1" 798 | description = "Yet another URL library" 799 | category = "main" 800 | optional = false 801 | python-versions = ">=3.7" 802 | 803 | [package.dependencies] 804 | idna = ">=2.0" 805 | multidict = ">=4.0" 806 | 807 | [metadata] 808 | lock-version = "1.1" 809 | python-versions = "^3.8" 810 | content-hash = "80b6f6dbd6d9189421cc98f0de9c75631ec1104bfcf56f7f2eaaec68a9326681" 811 | 812 | [metadata.files] 813 | anyio = [ 814 | {file = "anyio-3.6.1-py3-none-any.whl", hash = "sha256:cb29b9c70620506a9a8f87a309591713446953302d7d995344d0d7c6c0c9a7be"}, 815 | {file = "anyio-3.6.1.tar.gz", hash = "sha256:413adf95f93886e442aea925f3ee43baa5a765a64a0f52c6081894f9992fdd0b"}, 816 | ] 817 | atomicwrites = [] 818 | attrs = [] 819 | beanie = [] 820 | cachecontrol = [ 821 | {file = "CacheControl-0.12.11-py2.py3-none-any.whl", hash = "sha256:2c75d6a8938cb1933c75c50184549ad42728a27e9f6b92fd677c3151aa72555b"}, 822 | {file = "CacheControl-0.12.11.tar.gz", hash = "sha256:a5b9fcc986b184db101aa280b42ecdcdfc524892596f606858e0b7a8b4d9e144"}, 823 | ] 824 | cachetools = [ 825 | {file = "cachetools-5.2.0-py3-none-any.whl", hash = "sha256:f9f17d2aec496a9aa6b76f53e3b614c965223c061982d434d160f930c698a9db"}, 826 | {file = "cachetools-5.2.0.tar.gz", hash = "sha256:6a94c6402995a99c3970cc7e4884bb60b4a8639938157eeed436098bf9831757"}, 827 | ] 828 | certifi = [] 829 | charset-normalizer = [] 830 | click = [ 831 | {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, 832 | {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, 833 | ] 834 | colorama = [] 835 | dnspython = [ 836 | {file = "dnspython-2.2.1-py3-none-any.whl", hash = "sha256:a851e51367fb93e9e1361732c1d60dab63eff98712e503ea7d92e6eccb109b4f"}, 837 | {file = "dnspython-2.2.1.tar.gz", hash = "sha256:0f7569a4a6ff151958b64304071d370daa3243d15941a7beedf0c9fe5105603e"}, 838 | ] 839 | email-validator = [ 840 | {file = "email_validator-1.2.1-py2.py3-none-any.whl", hash = "sha256:c8589e691cf73eb99eed8d10ce0e9cbb05a0886ba920c8bcb7c82873f4c5789c"}, 841 | {file = "email_validator-1.2.1.tar.gz", hash = "sha256:6757aea012d40516357c0ac2b1a4c31219ab2f899d26831334c5d069e8b6c3d8"}, 842 | ] 843 | fastapi = [] 844 | firebase-admin = [] 845 | google-api-core = [ 846 | {file = "google-api-core-2.8.2.tar.gz", hash = "sha256:06f7244c640322b508b125903bb5701bebabce8832f85aba9335ec00b3d02edc"}, 847 | {file = "google_api_core-2.8.2-py3-none-any.whl", hash = "sha256:93c6a91ccac79079ac6bbf8b74ee75db970cc899278b97d53bc012f35908cf50"}, 848 | ] 849 | google-api-python-client = [] 850 | google-auth = [] 851 | google-auth-httplib2 = [ 852 | {file = "google-auth-httplib2-0.1.0.tar.gz", hash = "sha256:a07c39fd632becacd3f07718dfd6021bf396978f03ad3ce4321d060015cc30ac"}, 853 | {file = "google_auth_httplib2-0.1.0-py2.py3-none-any.whl", hash = "sha256:31e49c36c6b5643b57e82617cb3e021e3e1d2df9da63af67252c02fa9c1f4a10"}, 854 | ] 855 | google-cloud-core = [] 856 | google-cloud-firestore = [] 857 | google-cloud-storage = [] 858 | google-crc32c = [ 859 | {file = "google-crc32c-1.3.0.tar.gz", hash = "sha256:276de6273eb074a35bc598f8efbc00c7869c5cf2e29c90748fccc8c898c244df"}, 860 | {file = "google_crc32c-1.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cb6994fff247987c66a8a4e550ef374671c2b82e3c0d2115e689d21e511a652d"}, 861 | {file = "google_crc32c-1.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c9da0a39b53d2fab3e5467329ed50e951eb91386e9d0d5b12daf593973c3b168"}, 862 | {file = "google_crc32c-1.3.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:eb0b14523758e37802f27b7f8cd973f5f3d33be7613952c0df904b68c4842f0e"}, 863 | {file = "google_crc32c-1.3.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:95c68a4b9b7828ba0428f8f7e3109c5d476ca44996ed9a5f8aac6269296e2d59"}, 864 | {file = "google_crc32c-1.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c3cf890c3c0ecfe1510a452a165431b5831e24160c5fcf2071f0f85ca5a47cd"}, 865 | {file = "google_crc32c-1.3.0-cp310-cp310-win32.whl", hash = "sha256:3bbce1be3687bbfebe29abdb7631b83e6b25da3f4e1856a1611eb21854b689ea"}, 866 | {file = "google_crc32c-1.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:c124b8c8779bf2d35d9b721e52d4adb41c9bfbde45e6a3f25f0820caa9aba73f"}, 867 | {file = "google_crc32c-1.3.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:42ae4781333e331a1743445931b08ebdad73e188fd554259e772556fc4937c48"}, 868 | {file = "google_crc32c-1.3.0-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ff71073ebf0e42258a42a0b34f2c09ec384977e7f6808999102eedd5b49920e3"}, 869 | {file = "google_crc32c-1.3.0-cp36-cp36m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fe31de3002e7b08eb20823b3735b97c86c5926dd0581c7710a680b418a8709d4"}, 870 | {file = "google_crc32c-1.3.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd7760a88a8d3d705ff562aa93f8445ead54f58fd482e4f9e2bafb7e177375d4"}, 871 | {file = "google_crc32c-1.3.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a0b9e622c3b2b8d0ce32f77eba617ab0d6768b82836391e4f8f9e2074582bf02"}, 872 | {file = "google_crc32c-1.3.0-cp36-cp36m-win32.whl", hash = "sha256:779cbf1ce375b96111db98fca913c1f5ec11b1d870e529b1dc7354b2681a8c3a"}, 873 | {file = "google_crc32c-1.3.0-cp36-cp36m-win_amd64.whl", hash = "sha256:04e7c220798a72fd0f08242bc8d7a05986b2a08a0573396187fd32c1dcdd58b3"}, 874 | {file = "google_crc32c-1.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e7a539b9be7b9c00f11ef16b55486141bc2cdb0c54762f84e3c6fc091917436d"}, 875 | {file = "google_crc32c-1.3.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ca60076c388728d3b6ac3846842474f4250c91efbfe5afa872d3ffd69dd4b318"}, 876 | {file = "google_crc32c-1.3.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:05340b60bf05b574159e9bd940152a47d38af3fb43803ffe71f11d704b7696a6"}, 877 | {file = "google_crc32c-1.3.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:318f73f5484b5671f0c7f5f63741ab020a599504ed81d209b5c7129ee4667407"}, 878 | {file = "google_crc32c-1.3.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:9f58099ad7affc0754ae42e6d87443299f15d739b0ce03c76f515153a5cda06c"}, 879 | {file = "google_crc32c-1.3.0-cp37-cp37m-win32.whl", hash = "sha256:f52a4ad2568314ee713715b1e2d79ab55fab11e8b304fd1462ff5cccf4264b3e"}, 880 | {file = "google_crc32c-1.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:bab4aebd525218bab4ee615786c4581952eadc16b1ff031813a2fd51f0cc7b08"}, 881 | {file = "google_crc32c-1.3.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:dda4d8a3bb0b50f540f6ff4b6033f3a74e8bf0bd5320b70fab2c03e512a62812"}, 882 | {file = "google_crc32c-1.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:fec221a051150eeddfdfcff162e6db92c65ecf46cb0f7bb1bf812a1520ec026b"}, 883 | {file = "google_crc32c-1.3.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:226f2f9b8e128a6ca6a9af9b9e8384f7b53a801907425c9a292553a3a7218ce0"}, 884 | {file = "google_crc32c-1.3.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a7f9cbea4245ee36190f85fe1814e2d7b1e5f2186381b082f5d59f99b7f11328"}, 885 | {file = "google_crc32c-1.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a4db36f9721fdf391646685ecffa404eb986cbe007a3289499020daf72e88a2"}, 886 | {file = "google_crc32c-1.3.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:12674a4c3b56b706153a358eaa1018c4137a5a04635b92b4652440d3d7386206"}, 887 | {file = "google_crc32c-1.3.0-cp38-cp38-win32.whl", hash = "sha256:650e2917660e696041ab3dcd7abac160b4121cd9a484c08406f24c5964099829"}, 888 | {file = "google_crc32c-1.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:58be56ae0529c664cc04a9c76e68bb92b091e0194d6e3c50bea7e0f266f73713"}, 889 | {file = "google_crc32c-1.3.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:96a8918a78d5d64e07c8ea4ed2bc44354e3f93f46a4866a40e8db934e4c0d74b"}, 890 | {file = "google_crc32c-1.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:13af315c3a0eec8bb8b8d80b8b128cb3fcd17d7e4edafc39647846345a3f003a"}, 891 | {file = "google_crc32c-1.3.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6311853aa2bba4064d0c28ca54e7b50c4d48e3de04f6770f6c60ebda1e975267"}, 892 | {file = "google_crc32c-1.3.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ed447680ff21c14aaceb6a9f99a5f639f583ccfe4ce1a5e1d48eb41c3d6b3217"}, 893 | {file = "google_crc32c-1.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1c1d6236feab51200272d79b3d3e0f12cf2cbb12b208c835b175a21efdb0a73"}, 894 | {file = "google_crc32c-1.3.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:e0f1ff55dde0ebcfbef027edc21f71c205845585fffe30d4ec4979416613e9b3"}, 895 | {file = "google_crc32c-1.3.0-cp39-cp39-win32.whl", hash = "sha256:fbd60c6aaa07c31d7754edbc2334aef50601b7f1ada67a96eb1eb57c7c72378f"}, 896 | {file = "google_crc32c-1.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:127f9cc3ac41b6a859bd9dc4321097b1a4f6aa7fdf71b4f9227b9e3ebffb4422"}, 897 | {file = "google_crc32c-1.3.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:fc28e0db232c62ca0c3600884933178f0825c99be4474cdd645e378a10588125"}, 898 | {file = "google_crc32c-1.3.0-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1926fd8de0acb9d15ee757175ce7242e235482a783cd4ec711cc999fc103c24e"}, 899 | {file = "google_crc32c-1.3.0-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5da2c81575cc3ccf05d9830f9e8d3c70954819ca9a63828210498c0774fda1a3"}, 900 | {file = "google_crc32c-1.3.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:891f712ce54e0d631370e1f4997b3f182f3368179198efc30d477c75d1f44942"}, 901 | {file = "google_crc32c-1.3.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:7f6fe42536d9dcd3e2ffb9d3053f5d05221ae3bbcefbe472bdf2c71c793e3183"}, 902 | ] 903 | google-resumable-media = [ 904 | {file = "google-resumable-media-2.3.3.tar.gz", hash = "sha256:27c52620bd364d1c8116eaac4ea2afcbfb81ae9139fb3199652fcac1724bfb6c"}, 905 | {file = "google_resumable_media-2.3.3-py2.py3-none-any.whl", hash = "sha256:5b52774ea7a829a8cdaa8bd2d4c3d4bc660c91b30857ab2668d0eb830f4ea8c5"}, 906 | ] 907 | googleapis-common-protos = [] 908 | grpcio = [] 909 | grpcio-status = [] 910 | gunicorn = [] 911 | h11 = [ 912 | {file = "h11-0.13.0-py3-none-any.whl", hash = "sha256:8ddd78563b633ca55346c8cd41ec0af27d3c79931828beffb46ce70a379e7442"}, 913 | {file = "h11-0.13.0.tar.gz", hash = "sha256:70813c1135087a248a4d38cc0e1a0181ffab2188141a93eaf567940c3957ff06"}, 914 | ] 915 | httplib2 = [ 916 | {file = "httplib2-0.20.4-py3-none-any.whl", hash = "sha256:8b6a905cb1c79eefd03f8669fd993c36dc341f7c558f056cb5a33b5c2f458543"}, 917 | {file = "httplib2-0.20.4.tar.gz", hash = "sha256:58a98e45b4b1a48273073f905d2961666ecf0fbac4250ea5b47aef259eb5c585"}, 918 | ] 919 | httptools = [ 920 | {file = "httptools-0.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fcddfe70553be717d9745990dfdb194e22ee0f60eb8f48c0794e7bfeda30d2d5"}, 921 | {file = "httptools-0.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1ee0b459257e222b878a6c09ccf233957d3a4dcb883b0847640af98d2d9aac23"}, 922 | {file = "httptools-0.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ceafd5e960b39c7e0d160a1936b68eb87c5e79b3979d66e774f0c77d4d8faaed"}, 923 | {file = "httptools-0.4.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fdb9f9ed79bc6f46b021b3319184699ba1a22410a82204e6e89c774530069683"}, 924 | {file = "httptools-0.4.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:abe829275cdd4174b4c4e65ad718715d449e308d59793bf3a931ee1bf7e7b86c"}, 925 | {file = "httptools-0.4.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7af6bdbd21a2a25d6784f6d67f44f5df33ef39b6159543b9f9064d365c01f919"}, 926 | {file = "httptools-0.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:5d1fe6b6661022fd6cac541f54a4237496b246e6f1c0a6b41998ee08a1135afe"}, 927 | {file = "httptools-0.4.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:48e48530d9b995a84d1d89ae6b3ec4e59ea7d494b150ac3bbc5e2ac4acce92cd"}, 928 | {file = "httptools-0.4.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a113789e53ac1fa26edf99856a61e4c493868e125ae0dd6354cf518948fbbd5c"}, 929 | {file = "httptools-0.4.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8e2eb957787cbb614a0f006bfc5798ff1d90ac7c4dd24854c84edbdc8c02369e"}, 930 | {file = "httptools-0.4.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:7ee9f226acab9085037582c059d66769862706e8e8cd2340470ceb8b3850873d"}, 931 | {file = "httptools-0.4.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:701e66b59dd21a32a274771238025d58db7e2b6ecebbab64ceff51b8e31527ae"}, 932 | {file = "httptools-0.4.0-cp36-cp36m-win_amd64.whl", hash = "sha256:6a1a7dfc1f9c78a833e2c4904757a0f47ce25d08634dd2a52af394eefe5f9777"}, 933 | {file = "httptools-0.4.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:903f739c9fb78dab8970b0f3ea51f21955b24b45afa77b22ff0e172fc11ef111"}, 934 | {file = "httptools-0.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54bbd295f031b866b9799dd39cb45deee81aca036c9bff9f58ca06726f6494f1"}, 935 | {file = "httptools-0.4.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3194f6d6443befa8d4db16c1946b2fc428a3ceb8ab32eb6f09a59f86104dc1a0"}, 936 | {file = "httptools-0.4.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:cd1295f52971097f757edfbfce827b6dbbfb0f7a74901ee7d4933dff5ad4c9af"}, 937 | {file = "httptools-0.4.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:20a45bcf22452a10fa8d58b7dbdb474381f6946bf5b8933e3662d572bc61bae4"}, 938 | {file = "httptools-0.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d1f27bb0f75bef722d6e22dc609612bfa2f994541621cd2163f8c943b6463dfe"}, 939 | {file = "httptools-0.4.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:7f7bfb74718f52d5ed47d608d507bf66d3bc01d4a8b3e6dd7134daaae129357b"}, 940 | {file = "httptools-0.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a522d12e2ddbc2e91842ffb454a1aeb0d47607972c7d8fc88bd0838d97fb8a2a"}, 941 | {file = "httptools-0.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2db44a0b294d317199e9f80123e72c6b005c55b625b57fae36de68670090fa48"}, 942 | {file = "httptools-0.4.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c286985b5e194ca0ebb2908d71464b9be8f17cc66d6d3e330e8d5407248f56ad"}, 943 | {file = "httptools-0.4.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:d3a4e165ca6204f34856b765d515d558dc84f1352033b8721e8d06c3e44930c3"}, 944 | {file = "httptools-0.4.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:72aa3fbe636b16d22e04b5a9d24711b043495e0ecfe58080addf23a1a37f3409"}, 945 | {file = "httptools-0.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:9967d9758df505975913304c434cb9ab21e2c609ad859eb921f2f615a038c8de"}, 946 | {file = "httptools-0.4.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f72b5d24d6730035128b238decdc4c0f2104b7056a7ca55cf047c106842ec890"}, 947 | {file = "httptools-0.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:29bf97a5c532da9c7a04de2c7a9c31d1d54f3abd65a464119b680206bbbb1055"}, 948 | {file = "httptools-0.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98993805f1e3cdb53de4eed02b55dcc953cdf017ba7bbb2fd89226c086a6d855"}, 949 | {file = "httptools-0.4.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d9b90bf58f3ba04e60321a23a8723a1ff2a9377502535e70495e5ada8e6e6722"}, 950 | {file = "httptools-0.4.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:1a99346ebcb801b213c591540837340bdf6fd060a8687518d01c607d338b7424"}, 951 | {file = "httptools-0.4.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:645373c070080e632480a3d251d892cb795be3d3a15f86975d0f1aca56fd230d"}, 952 | {file = "httptools-0.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:34d2903dd2a3dd85d33705b6fde40bf91fc44411661283763fd0746723963c83"}, 953 | {file = "httptools-0.4.0.tar.gz", hash = "sha256:2c9a930c378b3d15d6b695fb95ebcff81a7395b4f9775c4f10a076beb0b2c1ff"}, 954 | ] 955 | idna = [ 956 | {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, 957 | {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, 958 | ] 959 | loguru = [ 960 | {file = "loguru-0.6.0-py3-none-any.whl", hash = "sha256:4e2414d534a2ab57573365b3e6d0234dfb1d84b68b7f3b948e6fb743860a77c3"}, 961 | {file = "loguru-0.6.0.tar.gz", hash = "sha256:066bd06758d0a513e9836fd9c6b5a75bfb3fd36841f4b996bc60b547a309d41c"}, 962 | ] 963 | more-itertools = [] 964 | motor = [ 965 | {file = "motor-3.0.0-py3-none-any.whl", hash = "sha256:b076de44970f518177f0eeeda8b183f52eafa557775bfe3294e93bda18867a71"}, 966 | {file = "motor-3.0.0.tar.gz", hash = "sha256:3e36d29406c151b61342e6a8fa5e90c00c4723b76e30f11276a4373ea2064b7d"}, 967 | ] 968 | msgpack = [ 969 | {file = "msgpack-1.0.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4ab251d229d10498e9a2f3b1e68ef64cb393394ec477e3370c457f9430ce9250"}, 970 | {file = "msgpack-1.0.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:112b0f93202d7c0fef0b7810d465fde23c746a2d482e1e2de2aafd2ce1492c88"}, 971 | {file = "msgpack-1.0.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:002b5c72b6cd9b4bafd790f364b8480e859b4712e91f43014fe01e4f957b8467"}, 972 | {file = "msgpack-1.0.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35bc0faa494b0f1d851fd29129b2575b2e26d41d177caacd4206d81502d4c6a6"}, 973 | {file = "msgpack-1.0.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4733359808c56d5d7756628736061c432ded018e7a1dff2d35a02439043321aa"}, 974 | {file = "msgpack-1.0.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb514ad14edf07a1dbe63761fd30f89ae79b42625731e1ccf5e1f1092950eaa6"}, 975 | {file = "msgpack-1.0.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c23080fdeec4716aede32b4e0ef7e213c7b1093eede9ee010949f2a418ced6ba"}, 976 | {file = "msgpack-1.0.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:49565b0e3d7896d9ea71d9095df15b7f75a035c49be733051c34762ca95bbf7e"}, 977 | {file = "msgpack-1.0.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:aca0f1644d6b5a73eb3e74d4d64d5d8c6c3d577e753a04c9e9c87d07692c58db"}, 978 | {file = "msgpack-1.0.4-cp310-cp310-win32.whl", hash = "sha256:0dfe3947db5fb9ce52aaea6ca28112a170db9eae75adf9339a1aec434dc954ef"}, 979 | {file = "msgpack-1.0.4-cp310-cp310-win_amd64.whl", hash = "sha256:4dea20515f660aa6b7e964433b1808d098dcfcabbebeaaad240d11f909298075"}, 980 | {file = "msgpack-1.0.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e83f80a7fec1a62cf4e6c9a660e39c7f878f603737a0cdac8c13131d11d97f52"}, 981 | {file = "msgpack-1.0.4-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c11a48cf5e59026ad7cb0dc29e29a01b5a66a3e333dc11c04f7e991fc5510a9"}, 982 | {file = "msgpack-1.0.4-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1276e8f34e139aeff1c77a3cefb295598b504ac5314d32c8c3d54d24fadb94c9"}, 983 | {file = "msgpack-1.0.4-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c9566f2c39ccced0a38d37c26cc3570983b97833c365a6044edef3574a00c08"}, 984 | {file = "msgpack-1.0.4-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:fcb8a47f43acc113e24e910399376f7277cf8508b27e5b88499f053de6b115a8"}, 985 | {file = "msgpack-1.0.4-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:76ee788122de3a68a02ed6f3a16bbcd97bc7c2e39bd4d94be2f1821e7c4a64e6"}, 986 | {file = "msgpack-1.0.4-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:0a68d3ac0104e2d3510de90a1091720157c319ceeb90d74f7b5295a6bee51bae"}, 987 | {file = "msgpack-1.0.4-cp36-cp36m-win32.whl", hash = "sha256:85f279d88d8e833ec015650fd15ae5eddce0791e1e8a59165318f371158efec6"}, 988 | {file = "msgpack-1.0.4-cp36-cp36m-win_amd64.whl", hash = "sha256:c1683841cd4fa45ac427c18854c3ec3cd9b681694caf5bff04edb9387602d661"}, 989 | {file = "msgpack-1.0.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a75dfb03f8b06f4ab093dafe3ddcc2d633259e6c3f74bb1b01996f5d8aa5868c"}, 990 | {file = "msgpack-1.0.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9667bdfdf523c40d2511f0e98a6c9d3603be6b371ae9a238b7ef2dc4e7a427b0"}, 991 | {file = "msgpack-1.0.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11184bc7e56fd74c00ead4f9cc9a3091d62ecb96e97653add7a879a14b003227"}, 992 | {file = "msgpack-1.0.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ac5bd7901487c4a1dd51a8c58f2632b15d838d07ceedaa5e4c080f7190925bff"}, 993 | {file = "msgpack-1.0.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1e91d641d2bfe91ba4c52039adc5bccf27c335356055825c7f88742c8bb900dd"}, 994 | {file = "msgpack-1.0.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2a2df1b55a78eb5f5b7d2a4bb221cd8363913830145fad05374a80bf0877cb1e"}, 995 | {file = "msgpack-1.0.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:545e3cf0cf74f3e48b470f68ed19551ae6f9722814ea969305794645da091236"}, 996 | {file = "msgpack-1.0.4-cp37-cp37m-win32.whl", hash = "sha256:2cc5ca2712ac0003bcb625c96368fd08a0f86bbc1a5578802512d87bc592fe44"}, 997 | {file = "msgpack-1.0.4-cp37-cp37m-win_amd64.whl", hash = "sha256:eba96145051ccec0ec86611fe9cf693ce55f2a3ce89c06ed307de0e085730ec1"}, 998 | {file = "msgpack-1.0.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:7760f85956c415578c17edb39eed99f9181a48375b0d4a94076d84148cf67b2d"}, 999 | {file = "msgpack-1.0.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:449e57cc1ff18d3b444eb554e44613cffcccb32805d16726a5494038c3b93dab"}, 1000 | {file = "msgpack-1.0.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d603de2b8d2ea3f3bcb2efe286849aa7a81531abc52d8454da12f46235092bcb"}, 1001 | {file = "msgpack-1.0.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48f5d88c99f64c456413d74a975bd605a9b0526293218a3b77220a2c15458ba9"}, 1002 | {file = "msgpack-1.0.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6916c78f33602ecf0509cc40379271ba0f9ab572b066bd4bdafd7434dee4bc6e"}, 1003 | {file = "msgpack-1.0.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:81fc7ba725464651190b196f3cd848e8553d4d510114a954681fd0b9c479d7e1"}, 1004 | {file = "msgpack-1.0.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:d5b5b962221fa2c5d3a7f8133f9abffc114fe218eb4365e40f17732ade576c8e"}, 1005 | {file = "msgpack-1.0.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:77ccd2af37f3db0ea59fb280fa2165bf1b096510ba9fe0cc2bf8fa92a22fdb43"}, 1006 | {file = "msgpack-1.0.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b17be2478b622939e39b816e0aa8242611cc8d3583d1cd8ec31b249f04623243"}, 1007 | {file = "msgpack-1.0.4-cp38-cp38-win32.whl", hash = "sha256:2bb8cdf50dd623392fa75525cce44a65a12a00c98e1e37bf0fb08ddce2ff60d2"}, 1008 | {file = "msgpack-1.0.4-cp38-cp38-win_amd64.whl", hash = "sha256:26b8feaca40a90cbe031b03d82b2898bf560027160d3eae1423f4a67654ec5d6"}, 1009 | {file = "msgpack-1.0.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:462497af5fd4e0edbb1559c352ad84f6c577ffbbb708566a0abaaa84acd9f3ae"}, 1010 | {file = "msgpack-1.0.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2999623886c5c02deefe156e8f869c3b0aaeba14bfc50aa2486a0415178fce55"}, 1011 | {file = "msgpack-1.0.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f0029245c51fd9473dc1aede1160b0a29f4a912e6b1dd353fa6d317085b219da"}, 1012 | {file = "msgpack-1.0.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed6f7b854a823ea44cf94919ba3f727e230da29feb4a99711433f25800cf747f"}, 1013 | {file = "msgpack-1.0.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0df96d6eaf45ceca04b3f3b4b111b86b33785683d682c655063ef8057d61fd92"}, 1014 | {file = "msgpack-1.0.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6a4192b1ab40f8dca3f2877b70e63799d95c62c068c84dc028b40a6cb03ccd0f"}, 1015 | {file = "msgpack-1.0.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0e3590f9fb9f7fbc36df366267870e77269c03172d086fa76bb4eba8b2b46624"}, 1016 | {file = "msgpack-1.0.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:1576bd97527a93c44fa856770197dec00d223b0b9f36ef03f65bac60197cedf8"}, 1017 | {file = "msgpack-1.0.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:63e29d6e8c9ca22b21846234913c3466b7e4ee6e422f205a2988083de3b08cae"}, 1018 | {file = "msgpack-1.0.4-cp39-cp39-win32.whl", hash = "sha256:fb62ea4b62bfcb0b380d5680f9a4b3f9a2d166d9394e9bbd9666c0ee09a3645c"}, 1019 | {file = "msgpack-1.0.4-cp39-cp39-win_amd64.whl", hash = "sha256:4d5834a2a48965a349da1c5a79760d94a1a0172fbb5ab6b5b33cbf8447e109ce"}, 1020 | {file = "msgpack-1.0.4.tar.gz", hash = "sha256:f5d869c18f030202eb412f08b28d2afeea553d6613aee89e200d7aca7ef01f5f"}, 1021 | ] 1022 | multidict = [ 1023 | {file = "multidict-6.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b9e95a740109c6047602f4db4da9949e6c5945cefbad34a1299775ddc9a62e2"}, 1024 | {file = "multidict-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac0e27844758d7177989ce406acc6a83c16ed4524ebc363c1f748cba184d89d3"}, 1025 | {file = "multidict-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:041b81a5f6b38244b34dc18c7b6aba91f9cdaf854d9a39e5ff0b58e2b5773b9c"}, 1026 | {file = "multidict-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5fdda29a3c7e76a064f2477c9aab1ba96fd94e02e386f1e665bca1807fc5386f"}, 1027 | {file = "multidict-6.0.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3368bf2398b0e0fcbf46d85795adc4c259299fec50c1416d0f77c0a843a3eed9"}, 1028 | {file = "multidict-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4f052ee022928d34fe1f4d2bc743f32609fb79ed9c49a1710a5ad6b2198db20"}, 1029 | {file = "multidict-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:225383a6603c086e6cef0f2f05564acb4f4d5f019a4e3e983f572b8530f70c88"}, 1030 | {file = "multidict-6.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50bd442726e288e884f7be9071016c15a8742eb689a593a0cac49ea093eef0a7"}, 1031 | {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:47e6a7e923e9cada7c139531feac59448f1f47727a79076c0b1ee80274cd8eee"}, 1032 | {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0556a1d4ea2d949efe5fd76a09b4a82e3a4a30700553a6725535098d8d9fb672"}, 1033 | {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:626fe10ac87851f4cffecee161fc6f8f9853f0f6f1035b59337a51d29ff3b4f9"}, 1034 | {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:8064b7c6f0af936a741ea1efd18690bacfbae4078c0c385d7c3f611d11f0cf87"}, 1035 | {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2d36e929d7f6a16d4eb11b250719c39560dd70545356365b494249e2186bc389"}, 1036 | {file = "multidict-6.0.2-cp310-cp310-win32.whl", hash = "sha256:fcb91630817aa8b9bc4a74023e4198480587269c272c58b3279875ed7235c293"}, 1037 | {file = "multidict-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:8cbf0132f3de7cc6c6ce00147cc78e6439ea736cee6bca4f068bcf892b0fd658"}, 1038 | {file = "multidict-6.0.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:05f6949d6169878a03e607a21e3b862eaf8e356590e8bdae4227eedadacf6e51"}, 1039 | {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2c2e459f7050aeb7c1b1276763364884595d47000c1cddb51764c0d8976e608"}, 1040 | {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d0509e469d48940147e1235d994cd849a8f8195e0bca65f8f5439c56e17872a3"}, 1041 | {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:514fe2b8d750d6cdb4712346a2c5084a80220821a3e91f3f71eec11cf8d28fd4"}, 1042 | {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19adcfc2a7197cdc3987044e3f415168fc5dc1f720c932eb1ef4f71a2067e08b"}, 1043 | {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b9d153e7f1f9ba0b23ad1568b3b9e17301e23b042c23870f9ee0522dc5cc79e8"}, 1044 | {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:aef9cc3d9c7d63d924adac329c33835e0243b5052a6dfcbf7732a921c6e918ba"}, 1045 | {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:4571f1beddff25f3e925eea34268422622963cd8dc395bb8778eb28418248e43"}, 1046 | {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:d48b8ee1d4068561ce8033d2c344cf5232cb29ee1a0206a7b828c79cbc5982b8"}, 1047 | {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:45183c96ddf61bf96d2684d9fbaf6f3564d86b34cb125761f9a0ef9e36c1d55b"}, 1048 | {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:75bdf08716edde767b09e76829db8c1e5ca9d8bb0a8d4bd94ae1eafe3dac5e15"}, 1049 | {file = "multidict-6.0.2-cp37-cp37m-win32.whl", hash = "sha256:a45e1135cb07086833ce969555df39149680e5471c04dfd6a915abd2fc3f6dbc"}, 1050 | {file = "multidict-6.0.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6f3cdef8a247d1eafa649085812f8a310e728bdf3900ff6c434eafb2d443b23a"}, 1051 | {file = "multidict-6.0.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0327292e745a880459ef71be14e709aaea2f783f3537588fb4ed09b6c01bca60"}, 1052 | {file = "multidict-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e875b6086e325bab7e680e4316d667fc0e5e174bb5611eb16b3ea121c8951b86"}, 1053 | {file = "multidict-6.0.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:feea820722e69451743a3d56ad74948b68bf456984d63c1a92e8347b7b88452d"}, 1054 | {file = "multidict-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cc57c68cb9139c7cd6fc39f211b02198e69fb90ce4bc4a094cf5fe0d20fd8b0"}, 1055 | {file = "multidict-6.0.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:497988d6b6ec6ed6f87030ec03280b696ca47dbf0648045e4e1d28b80346560d"}, 1056 | {file = "multidict-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:89171b2c769e03a953d5969b2f272efa931426355b6c0cb508022976a17fd376"}, 1057 | {file = "multidict-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:684133b1e1fe91eda8fa7447f137c9490a064c6b7f392aa857bba83a28cfb693"}, 1058 | {file = "multidict-6.0.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd9fc9c4849a07f3635ccffa895d57abce554b467d611a5009ba4f39b78a8849"}, 1059 | {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e07c8e79d6e6fd37b42f3250dba122053fddb319e84b55dd3a8d6446e1a7ee49"}, 1060 | {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4070613ea2227da2bfb2c35a6041e4371b0af6b0be57f424fe2318b42a748516"}, 1061 | {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:47fbeedbf94bed6547d3aa632075d804867a352d86688c04e606971595460227"}, 1062 | {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:5774d9218d77befa7b70d836004a768fb9aa4fdb53c97498f4d8d3f67bb9cfa9"}, 1063 | {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2957489cba47c2539a8eb7ab32ff49101439ccf78eab724c828c1a54ff3ff98d"}, 1064 | {file = "multidict-6.0.2-cp38-cp38-win32.whl", hash = "sha256:e5b20e9599ba74391ca0cfbd7b328fcc20976823ba19bc573983a25b32e92b57"}, 1065 | {file = "multidict-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:8004dca28e15b86d1b1372515f32eb6f814bdf6f00952699bdeb541691091f96"}, 1066 | {file = "multidict-6.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2e4a0785b84fb59e43c18a015ffc575ba93f7d1dbd272b4cdad9f5134b8a006c"}, 1067 | {file = "multidict-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6701bf8a5d03a43375909ac91b6980aea74b0f5402fbe9428fc3f6edf5d9677e"}, 1068 | {file = "multidict-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a007b1638e148c3cfb6bf0bdc4f82776cef0ac487191d093cdc316905e504071"}, 1069 | {file = "multidict-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07a017cfa00c9890011628eab2503bee5872f27144936a52eaab449be5eaf032"}, 1070 | {file = "multidict-6.0.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c207fff63adcdf5a485969131dc70e4b194327666b7e8a87a97fbc4fd80a53b2"}, 1071 | {file = "multidict-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:373ba9d1d061c76462d74e7de1c0c8e267e9791ee8cfefcf6b0b2495762c370c"}, 1072 | {file = "multidict-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfba7c6d5d7c9099ba21f84662b037a0ffd4a5e6b26ac07d19e423e6fdf965a9"}, 1073 | {file = "multidict-6.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:19d9bad105dfb34eb539c97b132057a4e709919ec4dd883ece5838bcbf262b80"}, 1074 | {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:de989b195c3d636ba000ee4281cd03bb1234635b124bf4cd89eeee9ca8fcb09d"}, 1075 | {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7c40b7bbece294ae3a87c1bc2abff0ff9beef41d14188cda94ada7bcea99b0fb"}, 1076 | {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:d16cce709ebfadc91278a1c005e3c17dd5f71f5098bfae1035149785ea6e9c68"}, 1077 | {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:a2c34a93e1d2aa35fbf1485e5010337c72c6791407d03aa5f4eed920343dd360"}, 1078 | {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:feba80698173761cddd814fa22e88b0661e98cb810f9f986c54aa34d281e4937"}, 1079 | {file = "multidict-6.0.2-cp39-cp39-win32.whl", hash = "sha256:23b616fdc3c74c9fe01d76ce0d1ce872d2d396d8fa8e4899398ad64fb5aa214a"}, 1080 | {file = "multidict-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:4bae31803d708f6f15fd98be6a6ac0b6958fcf68fda3c77a048a4f9073704aae"}, 1081 | {file = "multidict-6.0.2.tar.gz", hash = "sha256:5ff3bd75f38e4c43f1f470f2df7a4d430b821c4ce22be384e1459cb57d6bb013"}, 1082 | ] 1083 | packaging = [ 1084 | {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, 1085 | {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, 1086 | ] 1087 | pluggy = [ 1088 | {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"}, 1089 | {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, 1090 | ] 1091 | proto-plus = [] 1092 | protobuf = [] 1093 | py = [ 1094 | {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, 1095 | {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, 1096 | ] 1097 | pyasn1 = [ 1098 | {file = "pyasn1-0.4.8-py2.4.egg", hash = "sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3"}, 1099 | {file = "pyasn1-0.4.8-py2.5.egg", hash = "sha256:0458773cfe65b153891ac249bcf1b5f8f320b7c2ce462151f8fa74de8934becf"}, 1100 | {file = "pyasn1-0.4.8-py2.6.egg", hash = "sha256:5c9414dcfede6e441f7e8f81b43b34e834731003427e5b09e4e00e3172a10f00"}, 1101 | {file = "pyasn1-0.4.8-py2.7.egg", hash = "sha256:6e7545f1a61025a4e58bb336952c5061697da694db1cae97b116e9c46abcf7c8"}, 1102 | {file = "pyasn1-0.4.8-py2.py3-none-any.whl", hash = "sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d"}, 1103 | {file = "pyasn1-0.4.8-py3.1.egg", hash = "sha256:78fa6da68ed2727915c4767bb386ab32cdba863caa7dbe473eaae45f9959da86"}, 1104 | {file = "pyasn1-0.4.8-py3.2.egg", hash = "sha256:08c3c53b75eaa48d71cf8c710312316392ed40899cb34710d092e96745a358b7"}, 1105 | {file = "pyasn1-0.4.8-py3.3.egg", hash = "sha256:03840c999ba71680a131cfaee6fab142e1ed9bbd9c693e285cc6aca0d555e576"}, 1106 | {file = "pyasn1-0.4.8-py3.4.egg", hash = "sha256:7ab8a544af125fb704feadb008c99a88805126fb525280b2270bb25cc1d78a12"}, 1107 | {file = "pyasn1-0.4.8-py3.5.egg", hash = "sha256:e89bf84b5437b532b0803ba5c9a5e054d21fec423a89952a74f87fa2c9b7bce2"}, 1108 | {file = "pyasn1-0.4.8-py3.6.egg", hash = "sha256:014c0e9976956a08139dc0712ae195324a75e142284d5f87f1a87ee1b068a359"}, 1109 | {file = "pyasn1-0.4.8-py3.7.egg", hash = "sha256:99fcc3c8d804d1bc6d9a099921e39d827026409a58f2a720dcdb89374ea0c776"}, 1110 | {file = "pyasn1-0.4.8.tar.gz", hash = "sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba"}, 1111 | ] 1112 | pyasn1-modules = [ 1113 | {file = "pyasn1-modules-0.2.8.tar.gz", hash = "sha256:905f84c712230b2c592c19470d3ca8d552de726050d1d1716282a1f6146be65e"}, 1114 | {file = "pyasn1_modules-0.2.8-py2.4.egg", hash = "sha256:0fe1b68d1e486a1ed5473f1302bd991c1611d319bba158e98b106ff86e1d7199"}, 1115 | {file = "pyasn1_modules-0.2.8-py2.5.egg", hash = "sha256:fe0644d9ab041506b62782e92b06b8c68cca799e1a9636ec398675459e031405"}, 1116 | {file = "pyasn1_modules-0.2.8-py2.6.egg", hash = "sha256:a99324196732f53093a84c4369c996713eb8c89d360a496b599fb1a9c47fc3eb"}, 1117 | {file = "pyasn1_modules-0.2.8-py2.7.egg", hash = "sha256:0845a5582f6a02bb3e1bde9ecfc4bfcae6ec3210dd270522fee602365430c3f8"}, 1118 | {file = "pyasn1_modules-0.2.8-py2.py3-none-any.whl", hash = "sha256:a50b808ffeb97cb3601dd25981f6b016cbb3d31fbf57a8b8a87428e6158d0c74"}, 1119 | {file = "pyasn1_modules-0.2.8-py3.1.egg", hash = "sha256:f39edd8c4ecaa4556e989147ebf219227e2cd2e8a43c7e7fcb1f1c18c5fd6a3d"}, 1120 | {file = "pyasn1_modules-0.2.8-py3.2.egg", hash = "sha256:b80486a6c77252ea3a3e9b1e360bc9cf28eaac41263d173c032581ad2f20fe45"}, 1121 | {file = "pyasn1_modules-0.2.8-py3.3.egg", hash = "sha256:65cebbaffc913f4fe9e4808735c95ea22d7a7775646ab690518c056784bc21b4"}, 1122 | {file = "pyasn1_modules-0.2.8-py3.4.egg", hash = "sha256:15b7c67fabc7fc240d87fb9aabf999cf82311a6d6fb2c70d00d3d0604878c811"}, 1123 | {file = "pyasn1_modules-0.2.8-py3.5.egg", hash = "sha256:426edb7a5e8879f1ec54a1864f16b882c2837bfd06eee62f2c982315ee2473ed"}, 1124 | {file = "pyasn1_modules-0.2.8-py3.6.egg", hash = "sha256:cbac4bc38d117f2a49aeedec4407d23e8866ea4ac27ff2cf7fb3e5b570df19e0"}, 1125 | {file = "pyasn1_modules-0.2.8-py3.7.egg", hash = "sha256:c29a5e5cc7a3f05926aff34e097e84f8589cd790ce0ed41b67aed6857b26aafd"}, 1126 | ] 1127 | pydantic = [] 1128 | pymongo = [] 1129 | pyparsing = [ 1130 | {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, 1131 | {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, 1132 | ] 1133 | pytest = [ 1134 | {file = "pytest-5.4.3-py3-none-any.whl", hash = "sha256:5c0db86b698e8f170ba4582a492248919255fcd4c79b1ee64ace34301fb589a1"}, 1135 | {file = "pytest-5.4.3.tar.gz", hash = "sha256:7979331bfcba207414f5e1263b5a0f8f521d0f457318836a7355531ed1a4c7d8"}, 1136 | ] 1137 | python-dotenv = [ 1138 | {file = "python-dotenv-0.20.0.tar.gz", hash = "sha256:b7e3b04a59693c42c36f9ab1cc2acc46fa5df8c78e178fc33a8d4cd05c8d498f"}, 1139 | {file = "python_dotenv-0.20.0-py3-none-any.whl", hash = "sha256:d92a187be61fe482e4fd675b6d52200e7be63a12b724abbf931a40ce4fa92938"}, 1140 | ] 1141 | pyyaml = [ 1142 | {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, 1143 | {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, 1144 | {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, 1145 | {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, 1146 | {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"}, 1147 | {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, 1148 | {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, 1149 | {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, 1150 | {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, 1151 | {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, 1152 | {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"}, 1153 | {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"}, 1154 | {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"}, 1155 | {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"}, 1156 | {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"}, 1157 | {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"}, 1158 | {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"}, 1159 | {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"}, 1160 | {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"}, 1161 | {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, 1162 | {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, 1163 | {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, 1164 | {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"}, 1165 | {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, 1166 | {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, 1167 | {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, 1168 | {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, 1169 | {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, 1170 | {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, 1171 | {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"}, 1172 | {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, 1173 | {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, 1174 | {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, 1175 | ] 1176 | requests = [] 1177 | rsa = [] 1178 | six = [ 1179 | {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, 1180 | {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, 1181 | ] 1182 | sniffio = [ 1183 | {file = "sniffio-1.2.0-py3-none-any.whl", hash = "sha256:471b71698eac1c2112a40ce2752bb2f4a4814c22a54a3eed3676bc0f5ca9f663"}, 1184 | {file = "sniffio-1.2.0.tar.gz", hash = "sha256:c4666eecec1d3f50960c6bdf61ab7bc350648da6c126e3cf6898d8cd4ddcd3de"}, 1185 | ] 1186 | starlette = [ 1187 | {file = "starlette-0.19.1-py3-none-any.whl", hash = "sha256:5a60c5c2d051f3a8eb546136aa0c9399773a689595e099e0877704d5888279bf"}, 1188 | {file = "starlette-0.19.1.tar.gz", hash = "sha256:c6d21096774ecb9639acad41b86b7706e52ba3bf1dc13ea4ed9ad593d47e24c7"}, 1189 | ] 1190 | toml = [ 1191 | {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, 1192 | {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, 1193 | ] 1194 | typing-extensions = [] 1195 | uritemplate = [ 1196 | {file = "uritemplate-4.1.1-py2.py3-none-any.whl", hash = "sha256:830c08b8d99bdd312ea4ead05994a38e8936266f84b9a7878232db50b044e02e"}, 1197 | {file = "uritemplate-4.1.1.tar.gz", hash = "sha256:4346edfc5c3b79f694bccd6d6099a322bbeb628dbf2cd86eea55a456ce5124f0"}, 1198 | ] 1199 | urllib3 = [] 1200 | uvicorn = [] 1201 | uvloop = [ 1202 | {file = "uvloop-0.16.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6224f1401025b748ffecb7a6e2652b17768f30b1a6a3f7b44660e5b5b690b12d"}, 1203 | {file = "uvloop-0.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:30ba9dcbd0965f5c812b7c2112a1ddf60cf904c1c160f398e7eed3a6b82dcd9c"}, 1204 | {file = "uvloop-0.16.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:bd53f7f5db562f37cd64a3af5012df8cac2c464c97e732ed556800129505bd64"}, 1205 | {file = "uvloop-0.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:772206116b9b57cd625c8a88f2413df2fcfd0b496eb188b82a43bed7af2c2ec9"}, 1206 | {file = "uvloop-0.16.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b572256409f194521a9895aef274cea88731d14732343da3ecdb175228881638"}, 1207 | {file = "uvloop-0.16.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:04ff57aa137230d8cc968f03481176041ae789308b4d5079118331ab01112450"}, 1208 | {file = "uvloop-0.16.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a19828c4f15687675ea912cc28bbcb48e9bb907c801873bd1519b96b04fb805"}, 1209 | {file = "uvloop-0.16.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e814ac2c6f9daf4c36eb8e85266859f42174a4ff0d71b99405ed559257750382"}, 1210 | {file = "uvloop-0.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bd8f42ea1ea8f4e84d265769089964ddda95eb2bb38b5cbe26712b0616c3edee"}, 1211 | {file = "uvloop-0.16.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:647e481940379eebd314c00440314c81ea547aa636056f554d491e40503c8464"}, 1212 | {file = "uvloop-0.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e0d26fa5875d43ddbb0d9d79a447d2ace4180d9e3239788208527c4784f7cab"}, 1213 | {file = "uvloop-0.16.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:6ccd57ae8db17d677e9e06192e9c9ec4bd2066b77790f9aa7dede2cc4008ee8f"}, 1214 | {file = "uvloop-0.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:089b4834fd299d82d83a25e3335372f12117a7d38525217c2258e9b9f4578897"}, 1215 | {file = "uvloop-0.16.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98d117332cc9e5ea8dfdc2b28b0a23f60370d02e1395f88f40d1effd2cb86c4f"}, 1216 | {file = "uvloop-0.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e5f2e2ff51aefe6c19ee98af12b4ae61f5be456cd24396953244a30880ad861"}, 1217 | {file = "uvloop-0.16.0.tar.gz", hash = "sha256:f74bc20c7b67d1c27c72601c78cf95be99d5c2cdd4514502b4f3eb0933ff1228"}, 1218 | ] 1219 | watchfiles = [] 1220 | wcwidth = [ 1221 | {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, 1222 | {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"}, 1223 | ] 1224 | websockets = [ 1225 | {file = "websockets-10.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:661f641b44ed315556a2fa630239adfd77bd1b11cb0b9d96ed8ad90b0b1e4978"}, 1226 | {file = "websockets-10.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b529fdfa881b69fe563dbd98acce84f3e5a67df13de415e143ef053ff006d500"}, 1227 | {file = "websockets-10.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f351c7d7d92f67c0609329ab2735eee0426a03022771b00102816a72715bb00b"}, 1228 | {file = "websockets-10.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:379e03422178436af4f3abe0aa8f401aa77ae2487843738542a75faf44a31f0c"}, 1229 | {file = "websockets-10.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e904c0381c014b914136c492c8fa711ca4cced4e9b3d110e5e7d436d0fc289e8"}, 1230 | {file = "websockets-10.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e7e6f2d6fd48422071cc8a6f8542016f350b79cc782752de531577d35e9bd677"}, 1231 | {file = "websockets-10.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b9c77f0d1436ea4b4dc089ed8335fa141e6a251a92f75f675056dac4ab47a71e"}, 1232 | {file = "websockets-10.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e6fa05a680e35d0fcc1470cb070b10e6fe247af54768f488ed93542e71339d6f"}, 1233 | {file = "websockets-10.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2f94fa3ae454a63ea3a19f73b95deeebc9f02ba2d5617ca16f0bbdae375cda47"}, 1234 | {file = "websockets-10.3-cp310-cp310-win32.whl", hash = "sha256:6ed1d6f791eabfd9808afea1e068f5e59418e55721db8b7f3bfc39dc831c42ae"}, 1235 | {file = "websockets-10.3-cp310-cp310-win_amd64.whl", hash = "sha256:347974105bbd4ea068106ec65e8e8ebd86f28c19e529d115d89bd8cc5cda3079"}, 1236 | {file = "websockets-10.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fab7c640815812ed5f10fbee7abbf58788d602046b7bb3af9b1ac753a6d5e916"}, 1237 | {file = "websockets-10.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:994cdb1942a7a4c2e10098d9162948c9e7b235df755de91ca33f6e0481366fdb"}, 1238 | {file = "websockets-10.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:aad5e300ab32036eb3fdc350ad30877210e2f51bceaca83fb7fef4d2b6c72b79"}, 1239 | {file = "websockets-10.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e49ea4c1a9543d2bd8a747ff24411509c29e4bdcde05b5b0895e2120cb1a761d"}, 1240 | {file = "websockets-10.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6ea6b300a6bdd782e49922d690e11c3669828fe36fc2471408c58b93b5535a98"}, 1241 | {file = "websockets-10.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:ef5ce841e102278c1c2e98f043db99d6755b1c58bde475516aef3a008ed7f28e"}, 1242 | {file = "websockets-10.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d1655a6fc7aecd333b079d00fb3c8132d18988e47f19740c69303bf02e9883c6"}, 1243 | {file = "websockets-10.3-cp37-cp37m-win32.whl", hash = "sha256:83e5ca0d5b743cde3d29fda74ccab37bdd0911f25bd4cdf09ff8b51b7b4f2fa1"}, 1244 | {file = "websockets-10.3-cp37-cp37m-win_amd64.whl", hash = "sha256:da4377904a3379f0c1b75a965fff23b28315bcd516d27f99a803720dfebd94d4"}, 1245 | {file = "websockets-10.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a1e15b230c3613e8ea82c9fc6941b2093e8eb939dd794c02754d33980ba81e36"}, 1246 | {file = "websockets-10.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:31564a67c3e4005f27815634343df688b25705cccb22bc1db621c781ddc64c69"}, 1247 | {file = "websockets-10.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c8d1d14aa0f600b5be363077b621b1b4d1eb3fbf90af83f9281cda668e6ff7fd"}, 1248 | {file = "websockets-10.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8fbd7d77f8aba46d43245e86dd91a8970eac4fb74c473f8e30e9c07581f852b2"}, 1249 | {file = "websockets-10.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:210aad7fdd381c52e58777560860c7e6110b6174488ef1d4b681c08b68bf7f8c"}, 1250 | {file = "websockets-10.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6075fd24df23133c1b078e08a9b04a3bc40b31a8def4ee0b9f2c8865acce913e"}, 1251 | {file = "websockets-10.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:7f6d96fdb0975044fdd7953b35d003b03f9e2bcf85f2d2cf86285ece53e9f991"}, 1252 | {file = "websockets-10.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:c7250848ce69559756ad0086a37b82c986cd33c2d344ab87fea596c5ac6d9442"}, 1253 | {file = "websockets-10.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:28dd20b938a57c3124028680dc1600c197294da5db4292c76a0b48efb3ed7f76"}, 1254 | {file = "websockets-10.3-cp38-cp38-win32.whl", hash = "sha256:54c000abeaff6d8771a4e2cef40900919908ea7b6b6a30eae72752607c6db559"}, 1255 | {file = "websockets-10.3-cp38-cp38-win_amd64.whl", hash = "sha256:7ab36e17af592eec5747c68ef2722a74c1a4a70f3772bc661079baf4ae30e40d"}, 1256 | {file = "websockets-10.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a141de3d5a92188234afa61653ed0bbd2dde46ad47b15c3042ffb89548e77094"}, 1257 | {file = "websockets-10.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:97bc9d41e69a7521a358f9b8e44871f6cdeb42af31815c17aed36372d4eec667"}, 1258 | {file = "websockets-10.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d6353ba89cfc657a3f5beabb3b69be226adbb5c6c7a66398e17809b0ce3c4731"}, 1259 | {file = "websockets-10.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec2b0ab7edc8cd4b0eb428b38ed89079bdc20c6bdb5f889d353011038caac2f9"}, 1260 | {file = "websockets-10.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:85506b3328a9e083cc0a0fb3ba27e33c8db78341b3eb12eb72e8afd166c36680"}, 1261 | {file = "websockets-10.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8af75085b4bc0b5c40c4a3c0e113fa95e84c60f4ed6786cbb675aeb1ee128247"}, 1262 | {file = "websockets-10.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:07cdc0a5b2549bcfbadb585ad8471ebdc7bdf91e32e34ae3889001c1c106a6af"}, 1263 | {file = "websockets-10.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:5b936bf552e4f6357f5727579072ff1e1324717902127ffe60c92d29b67b7be3"}, 1264 | {file = "websockets-10.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e4e08305bfd76ba8edab08dcc6496f40674f44eb9d5e23153efa0a35750337e8"}, 1265 | {file = "websockets-10.3-cp39-cp39-win32.whl", hash = "sha256:bb621ec2dbbbe8df78a27dbd9dd7919f9b7d32a73fafcb4d9252fc4637343582"}, 1266 | {file = "websockets-10.3-cp39-cp39-win_amd64.whl", hash = "sha256:51695d3b199cd03098ae5b42833006a0f43dc5418d3102972addc593a783bc02"}, 1267 | {file = "websockets-10.3-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:907e8247480f287aa9bbc9391bd6de23c906d48af54c8c421df84655eef66af7"}, 1268 | {file = "websockets-10.3-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b1359aba0ff810d5830d5ab8e2c4a02bebf98a60aa0124fb29aa78cfdb8031f"}, 1269 | {file = "websockets-10.3-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:93d5ea0b5da8d66d868b32c614d2b52d14304444e39e13a59566d4acb8d6e2e4"}, 1270 | {file = "websockets-10.3-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7934e055fd5cd9dee60f11d16c8d79c4567315824bacb1246d0208a47eca9755"}, 1271 | {file = "websockets-10.3-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:3eda1cb7e9da1b22588cefff09f0951771d6ee9fa8dbe66f5ae04cc5f26b2b55"}, 1272 | {file = "websockets-10.3.tar.gz", hash = "sha256:fc06cc8073c8e87072138ba1e431300e2d408f054b27047d047b549455066ff4"}, 1273 | ] 1274 | win32-setctime = [ 1275 | {file = "win32_setctime-1.1.0-py3-none-any.whl", hash = "sha256:231db239e959c2fe7eb1d7dc129f11172354f98361c4fa2d6d2d7e278baa8aad"}, 1276 | {file = "win32_setctime-1.1.0.tar.gz", hash = "sha256:15cf5750465118d6929ae4de4eb46e8edae9a5634350c01ba582df868e932cb2"}, 1277 | ] 1278 | yarl = [] 1279 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "python_fastapi_firebase_authentication" 3 | version = "0.1.0" 4 | description = "" 5 | authors = ["Rahul"] 6 | 7 | [tool.poetry.scripts] 8 | cli_script = "packagename.cli:main" 9 | dev = "python_fastapi_firebase_authentication.core.configEnv:dev" 10 | prod = "python_fastapi_firebase_authentication.core.configEnv:prod" 11 | test = "python_fastapi_firebase_authentication.core.configEnv:test" 12 | server = "serve_uvicorn:uvi_server" 13 | 14 | 15 | [tool.poetry.dependencies] 16 | python = "^3.8" 17 | fastapi = "^0.81.0" 18 | uvicorn = {extras = ["standard"], version = "^0.18.3"} 19 | loguru = "^0.6.0" 20 | firebase-admin = "^5.3.0" 21 | email-validator = "^1.2.1" 22 | beanie = "^1.11.9" 23 | gunicorn = "^20.1.0" 24 | 25 | [tool.poetry.dev-dependencies] 26 | pytest = "^5.2" 27 | 28 | [build-system] 29 | requires = ["poetry-core>=1.0.0"] 30 | build-backend = "poetry.core.masonry.api" 31 | -------------------------------------------------------------------------------- /python_fastapi_firebase_authentication/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = '0.1.0' 2 | -------------------------------------------------------------------------------- /python_fastapi_firebase_authentication/core/configEnv.py: -------------------------------------------------------------------------------- 1 | 2 | from loguru import logger 3 | from pathlib import Path 4 | 5 | 6 | def write_dotenv(env:str): 7 | logger.debug(f"------Setting up environment!----") 8 | logger.debug(f"------copying .env.{env} to .env file----") 9 | 10 | root = Path(__file__).parent.parent 11 | 12 | copyFrom = root.with_name(f".env.{env}") 13 | copyTo = root.with_name(".env") 14 | 15 | with open(copyFrom, "r", encoding = 'utf-8') as input: 16 | with open(copyTo, 'w', encoding = 'utf-8') as output: 17 | for line in input: 18 | output.write(line) 19 | 20 | 21 | def dev(): 22 | write_dotenv(env="dev") 23 | 24 | def prod(): 25 | write_dotenv(env="prod") 26 | 27 | def test(): 28 | write_dotenv(env="test") 29 | 30 | -------------------------------------------------------------------------------- /python_fastapi_firebase_authentication/core/custom_logging.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import sys 3 | from pathlib import Path 4 | from loguru import logger 5 | import json 6 | 7 | 8 | class InterceptHandler(logging.Handler): 9 | loglevel_mapping = { 10 | 50: 'CRITICAL', 11 | 40: 'ERROR', 12 | 30: 'WARNING', 13 | 20: 'INFO', 14 | 10: 'DEBUG', 15 | 0: 'NOTSET', 16 | } 17 | 18 | def emit(self, record): 19 | try: 20 | level = logger.level(record.levelname).name 21 | except AttributeError: 22 | level = self.loglevel_mapping[record.levelno] 23 | 24 | frame, depth = logging.currentframe(), 2 25 | while frame.f_code.co_filename == logging.__file__: 26 | frame = frame.f_back 27 | depth += 1 28 | 29 | 30 | logger.opt( 31 | depth=depth, 32 | exception=record.exc_info 33 | ).log(level,record.getMessage()) 34 | 35 | 36 | class CustomizeLogger: 37 | 38 | @classmethod 39 | def make_logger(cls,config_path: Path): 40 | 41 | config = cls.load_logging_config(config_path) 42 | logging_config = config.get('logger') 43 | 44 | logger = cls.customize_logging( 45 | level=logging_config.get('level'), 46 | retention=logging_config.get('retention'), 47 | rotation=logging_config.get('rotation'), 48 | format=logging_config.get('format') 49 | ) 50 | return logger 51 | 52 | @classmethod 53 | def customize_logging(cls, 54 | level: str, 55 | rotation: str, 56 | retention: str, 57 | format: str 58 | ): 59 | 60 | logger.remove(0) 61 | logger.add( 62 | # sys.stdout, 63 | sys.stderr, 64 | enqueue=True, 65 | backtrace=True, 66 | level=level.upper(), 67 | format=format 68 | ) 69 | logging.basicConfig(handlers=[InterceptHandler()], level=0) 70 | logging.getLogger("uvicorn.access").handlers = [InterceptHandler()] 71 | for _log in ['uvicorn', 72 | 'uvicorn.error', 73 | 'fastapi' 74 | ]: 75 | _logger = logging.getLogger(_log) 76 | _logger.handlers = [InterceptHandler()] 77 | 78 | # return logger.bind(request_id=None, method=None) 79 | return logger 80 | 81 | 82 | @classmethod 83 | def load_logging_config(cls, config_path): 84 | config = None 85 | with open(config_path) as config_file: 86 | config = json.load(config_file) 87 | return config 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /python_fastapi_firebase_authentication/core/data/mongodb/db.py: -------------------------------------------------------------------------------- 1 | 2 | from motor.motor_asyncio import AsyncIOMotorClient 3 | from beanie import init_beanie 4 | 5 | from ...env import DB_NAME, MONGODB_URL 6 | 7 | from ....modules.user.data.mongoDb.models.authProvider import AuthProvider 8 | from ....modules.user.data.mongoDb.models.player import Player 9 | from ....modules.user.data.mongoDb.models.user import User 10 | 11 | 12 | __models = [ 13 | AuthProvider, 14 | User, 15 | Player 16 | ] 17 | 18 | mongodb_client:AsyncIOMotorClient = AsyncIOMotorClient(MONGODB_URL) 19 | 20 | # logger.info(client.db) 21 | 22 | 23 | async def init_db(client:AsyncIOMotorClient): 24 | # client = AsyncIOMotorClient(MONGODB_URL) 25 | await init_beanie(client[DB_NAME], document_models=__models) 26 | -------------------------------------------------------------------------------- /python_fastapi_firebase_authentication/core/env.py: -------------------------------------------------------------------------------- 1 | 2 | from loguru import logger 3 | 4 | from .settings import settings, BASE_DIR 5 | 6 | 7 | TITLE = settings.TITLE 8 | SECRET_KEY = settings.SECRET_KEY 9 | WEB_API_KEY = settings.WEB_API_KEY 10 | ENV = settings.ENV 11 | MONGODB_URL = settings.MONGODB_URL 12 | DB_NAME = settings.DB_NAME 13 | DEBUG = settings.FASTAPI_DEBUG 14 | ALLOWED_ORIGINS = settings.CORS_ALLOWED_ORIGINS.split(",") 15 | LOGGING_CONFIG_PATH = BASE_DIR.with_name("logging_config.json") 16 | 17 | UVI_PORT = settings.UVI_PORT 18 | UVI_SERVER_HOST = settings.UVI_SERVER_HOST 19 | UVI_RELOAD = settings.UVI_RELOAD 20 | UVI_LOG_LEVEL = settings.UVI_LOG_LEVEL 21 | UVI_ACCESS_LOG = settings.UVI_ACCESS_LOG 22 | 23 | 24 | logger.info(f"------Working inside {settings.ENV} environment!----") 25 | -------------------------------------------------------------------------------- /python_fastapi_firebase_authentication/core/settings.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | from dotenv import load_dotenv 3 | from functools import lru_cache 4 | from typing import Optional 5 | from pydantic import BaseSettings 6 | 7 | 8 | BASE_DIR: Path = Path(__file__).resolve().parent.parent 9 | 10 | load_dotenv(BASE_DIR.with_name(".env")) 11 | 12 | 13 | class GlobalConfig(BaseSettings): 14 | """Global configurations.""" 15 | TITLE : str 16 | SECRET_KEY : Optional[str] 17 | WEB_API_KEY : Optional[str] 18 | MONGODB_URL : Optional[str] 19 | DB_NAME : Optional[str] 20 | FASTAPI_DEBUG : bool = False 21 | CORS_ALLOWED_ORIGINS : str 22 | 23 | UVI_PORT : Optional[int] = None 24 | UVI_SERVER_HOST : Optional[str] = None 25 | UVI_RELOAD : Optional[bool] = False 26 | UVI_LOG_LEVEL : Optional[str] = "info" 27 | UVI_ACCESS_LOG : Optional[bool] = False 28 | ENV : str 29 | 30 | 31 | @lru_cache() 32 | def get_settings(): 33 | return GlobalConfig() 34 | 35 | 36 | settings = get_settings() -------------------------------------------------------------------------------- /python_fastapi_firebase_authentication/modules/dummy/endpoints.py: -------------------------------------------------------------------------------- 1 | 2 | import json 3 | import requests 4 | 5 | from fastapi import APIRouter, status, HTTPException 6 | from loguru import logger 7 | from firebase_admin import auth 8 | 9 | from ...modules.dummy.model import Login, Register 10 | from ...core.env import WEB_API_KEY 11 | # requests 12 | 13 | dummyRouter = APIRouter(prefix="/dummy", tags=["dummy"]) 14 | 15 | 16 | @dummyRouter.post("/register", status_code=status.HTTP_200_OK, response_model_exclude_none=True) # , responses=, response_model=PlayerUpdateOut 17 | async def create_user(pModel:Register): 18 | try: 19 | logger.debug(pModel) 20 | email = pModel.email 21 | password = pModel.password 22 | 23 | res = auth.create_user(email=email, email_verified=False, password=password) 24 | logger.info(res) 25 | resOut = {"msg" : "Logged in successfully. LOGIN WITH THE REGISTERED CREDETIALS TO GET ACCESS TOKEN TO USE API."} 26 | return resOut 27 | except Exception as err: 28 | logger.info(err.args[0]) 29 | logger.info(type(err.args[0])) 30 | if "EMAIL_EXISTS" in err.args[0]: 31 | logger.debug("CAUGHT") 32 | raise HTTPException(status_code=status.HTTP_302_FOUND, detail="Email already registered!!! Please login.") 33 | 34 | logger.error(err) 35 | raise err 36 | 37 | @dummyRouter.post("/login", status_code=status.HTTP_200_OK, response_model_exclude_none=True) # , responses= 38 | async def login_user(pModel:Login): 39 | try: 40 | logger.debug(pModel) 41 | email = pModel.email 42 | password = pModel.password 43 | 44 | rest_api_url = f"https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword" 45 | 46 | payload = json.dumps({ 47 | "email": email, 48 | "password": password, 49 | "returnSecureToken": True 50 | }) 51 | 52 | r = requests.post(rest_api_url, params={"key": WEB_API_KEY}, data=payload) 53 | #check for errors in result 54 | # logger.info(r.json()) 55 | if 'error' in r.json().keys(): 56 | raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail={'status':'error','message':r.json()['error']['message']}) 57 | # return {'status':'error','message':r.json()['error']['message']} 58 | #if the registration succeeded 59 | if 'idToken' in r.json().keys() : 60 | return {'status':'success','idToken':r.json()['idToken']} 61 | 62 | except Exception as err: 63 | logger.error(err) 64 | raise err 65 | -------------------------------------------------------------------------------- /python_fastapi_firebase_authentication/modules/dummy/model.py: -------------------------------------------------------------------------------- 1 | 2 | from typing import Optional 3 | from pydantic import BaseModel, EmailStr 4 | 5 | class Login(BaseModel): 6 | email : Optional[EmailStr] = "dummy@email.com" 7 | password : Optional[str] = "password" 8 | 9 | class Register(BaseModel): 10 | email : Optional[EmailStr] = "dummy@email.com" 11 | password : Optional[str] = "password" -------------------------------------------------------------------------------- /python_fastapi_firebase_authentication/modules/user/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rahulprakash11/Python-Firebase-Authentication-FastAPI/7341e6a223eca6388bb22047567777e52b088d9e/python_fastapi_firebase_authentication/modules/user/__init__.py -------------------------------------------------------------------------------- /python_fastapi_firebase_authentication/modules/user/constants.py: -------------------------------------------------------------------------------- 1 | class UserConstant: 2 | 3 | class Auth: 4 | 5 | class AuthType: 6 | firebase = "firebase" 7 | # custom = "custom" 8 | 9 | class VerificationType: 10 | player = "player" 11 | admin = "admin" 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /python_fastapi_firebase_authentication/modules/user/controller/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rahulprakash11/Python-Firebase-Authentication-FastAPI/7341e6a223eca6388bb22047567777e52b088d9e/python_fastapi_firebase_authentication/modules/user/controller/__init__.py -------------------------------------------------------------------------------- /python_fastapi_firebase_authentication/modules/user/controller/authVerify.py: -------------------------------------------------------------------------------- 1 | 2 | from typing import Optional 3 | from fastapi import APIRouter, status, HTTPException 4 | from loguru import logger 5 | from firebase_admin import auth 6 | 7 | from ..data.mongoDb.interface.authProvider import MongoDbAuthProvider 8 | from ..data.mongoDb.interface.player import MongoDbPlayer 9 | from ..data.mongoDb.interface.user import MongoDbUser 10 | from ..data.mongoDb.models.authProvider import AuthProvider 11 | from ..data.mongoDb.models.player import Player, Contact 12 | from ..data.mongoDb.models.user import User 13 | from ..constants import UserConstant 14 | from ..data.mongoDb.dbConstants import MongoDbConstant 15 | from ..model.model import AuthorisedUserOut, UserAuthRequest 16 | 17 | from ....utils.bitwise import Bitwise 18 | 19 | 20 | # logger = logging.getLogger(__name__) 21 | 22 | 23 | router = APIRouter() 24 | 25 | mongoDbAuthProvider = MongoDbAuthProvider() 26 | mongoDbPlayer = MongoDbPlayer() 27 | mongoDbUser = MongoDbUser() 28 | 29 | 30 | @router.post("/verify", status_code=status.HTTP_200_OK, response_model=AuthorisedUserOut , response_model_exclude_none=True) 31 | async def verify(pModel : UserAuthRequest): 32 | 33 | try: 34 | verificationType = pModel.verificationType 35 | token = pModel.token 36 | 37 | if ( 38 | (verificationType != UserConstant.Auth.VerificationType.player) & 39 | (verificationType != UserConstant.Auth.VerificationType.admin) 40 | ): 41 | 42 | logger.debug(f"--wrong verificationType {verificationType}---") 43 | raise HTTPException(status_code=status.HTTP_404_NOT_FOUND , detail="Invalid verification Type") 44 | 45 | 46 | deToken : dict = auth.verify_id_token(id_token=token) 47 | logger.info(deToken) 48 | 49 | pUserId = deToken["user_id"] 50 | provider :str = deToken["firebase"]["sign_in_provider"] # google.com 51 | 52 | 53 | if pModel.name: 54 | name = pModel.name 55 | else: 56 | name = deToken.get("name","user") 57 | 58 | # email : str | None 59 | identifier : Optional[str] 60 | contact = Contact() 61 | 62 | if provider == MongoDbConstant.AuthProvider.Providers.google: # google.com 63 | # email = deToken.get("email") 64 | identifier = deToken.get("email") 65 | contact.email = identifier 66 | elif provider == MongoDbConstant.AuthProvider.Providers.phone: # phone 67 | # phone = deToken.get("phone") 68 | identifier = deToken.get("phone") 69 | contact.phone = identifier 70 | 71 | elif provider == MongoDbConstant.AuthProvider.Providers.password: # phone 72 | # phone = deToken.get("phone") 73 | identifier = deToken.get("email") 74 | contact.email = identifier 75 | else: 76 | logger.exception(f"---Invalid provider : {provider}---") 77 | raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Invalid provider") 78 | 79 | dbAuthProvider = await mongoDbAuthProvider.getByIdentifier(identifier=identifier, provider=provider) 80 | 81 | if dbAuthProvider is None: 82 | 83 | modelUser = User( 84 | name=name, 85 | role=MongoDbConstant.User.Role.player, 86 | status=MongoDbConstant.User.Status.verified 87 | ) 88 | dbUser = await mongoDbUser.add(user=modelUser) 89 | logger.info(f"---user id {dbUser.id}") 90 | 91 | modelAuthProvider = AuthProvider( 92 | userId=dbUser.id, 93 | pUid=pUserId, 94 | credentials=identifier, 95 | provider=provider, 96 | authType=MongoDbConstant.AuthProvider.AuthType.firebase 97 | ) 98 | dbAuthProvider = await mongoDbAuthProvider.add(modelAuth=modelAuthProvider) 99 | logger.info(f"---created authprovider : {dbAuthProvider}---") 100 | 101 | else: 102 | logger.debug("---Found dbAuthProvider---") 103 | userId = dbAuthProvider.userId 104 | 105 | dbUser = await mongoDbUser.getById(id=userId) 106 | 107 | # if status == MongoDbUserConstant.Status.unverified: first get value through masking then check 108 | if ( 109 | Bitwise().containsValue( 110 | originalValue=dbUser.status, 111 | maskValue=MongoDbConstant.User.Status.statusMask, 112 | checkFor=MongoDbConstant.User.Status.unverified 113 | ) 114 | ): 115 | 116 | userStatus = Bitwise().attachValue( 117 | originalValue=dbUser.status, 118 | maskValue=MongoDbConstant.User.Status.unverifiedMask, 119 | attachValue=MongoDbConstant.User.Status.verified 120 | ) 121 | dbUser = await dbUser.set({User.status : userStatus}) 122 | 123 | if dbAuthProvider.pUid is None: 124 | dbAuthProvider = await dbAuthProvider.set({AuthProvider.pUid : pUserId}) 125 | 126 | # get player by uID 127 | logger.info(f"---user id : {dbAuthProvider.userId}, {dbUser.id}") 128 | 129 | dbPlayer = await mongoDbPlayer.getByUserId(userId=dbUser.id) 130 | 131 | if dbPlayer is None: 132 | if provider == MongoDbConstant.AuthProvider.Providers.google: # google.com 133 | modelPlayer = Player(userId=dbUser.id, contact=contact, name=name, verifiedEmail=identifier, imageUrl=deToken["picture"]) 134 | elif provider == MongoDbConstant.AuthProvider.Providers.phone: # phone 135 | modelPlayer = Player(userId=dbUser.id, contact=contact, name=name, verifiedPhone=identifier) 136 | elif provider == MongoDbConstant.AuthProvider.Providers.password: # phone 137 | modelPlayer = Player(userId=dbUser.id, contact=contact, name=name, verifiedEmail=identifier) 138 | else: 139 | raise HTTPException(status_code=status.HTTP_405_METHOD_NOT_ALLOWED , detail="login method is not set") 140 | dbPlayer = await mongoDbPlayer.add(player=modelPlayer) 141 | else: 142 | if provider == MongoDbConstant.AuthProvider.Providers.google: # google.com 143 | await dbPlayer.set({Player.verifiedEmail : identifier}) 144 | elif provider == MongoDbConstant.AuthProvider.Providers.phone: # phone 145 | await dbPlayer.set({Player.verifiedPhone : identifier}) 146 | elif provider == MongoDbConstant.AuthProvider.Providers.password: # phone 147 | await dbPlayer.set({Player.verifiedEmail : identifier}) 148 | else: 149 | raise HTTPException(status_code=status.HTTP_405_METHOD_NOT_ALLOWED , detail="login method is not set") 150 | 151 | logger.info(f"---player id : {dbPlayer.id}") 152 | 153 | response = AuthorisedUserOut(user=dbUser, player=dbPlayer) 154 | return response 155 | except Exception as err: 156 | logger.error(err) 157 | raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=err.args) 158 | 159 | -------------------------------------------------------------------------------- /python_fastapi_firebase_authentication/modules/user/controller/player.py: -------------------------------------------------------------------------------- 1 | from beanie import PydanticObjectId 2 | from fastapi import Depends, APIRouter, status, HTTPException 3 | from loguru import logger 4 | 5 | from ...user.data.mongoDb.interface.player import MongoDbPlayer 6 | from ...user.data.mongoDb.models.player import Player 7 | from ...user.model.model import PlayerUpdate, PlayerUpdateOut 8 | from ....utils.dependencies import currentPlayer 9 | 10 | 11 | 12 | router = APIRouter() 13 | 14 | mongoDbPlayer = MongoDbPlayer() 15 | 16 | 17 | @router.get("/{id}", status_code=status.HTTP_200_OK, response_model=Player , response_model_exclude_none=True) # , responses= 18 | async def getPlayer(id :PydanticObjectId, currentPlayer : Player = Depends(currentPlayer)): 19 | try: 20 | objectId = id 21 | if objectId == currentPlayer.id: 22 | response = currentPlayer 23 | return response 24 | else: 25 | raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail={"msg":"Referenced Player not found", "id":objectId}) 26 | except Exception as err: 27 | logger.error(err) 28 | raise err 29 | 30 | 31 | 32 | @router.put("/{id}", status_code=status.HTTP_200_OK, response_model=PlayerUpdateOut , response_model_exclude_none=True) # , responses= 33 | async def updatePlayer(id :PydanticObjectId, pModel:PlayerUpdate, currentPlayer : Player = Depends(currentPlayer)): 34 | try: 35 | logger.debug(id) 36 | logger.debug(currentPlayer.id) 37 | objectId = id 38 | if objectId != currentPlayer.id: 39 | raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail={"msg" : "Not Authorised. Can update only self player info."}) 40 | 41 | dbPlayer = await mongoDbPlayer.update(dbPlayer=currentPlayer, pModel=pModel) 42 | response = PlayerUpdateOut(isUpdated=True, item=dbPlayer) 43 | 44 | logger.debug(response) 45 | return response 46 | except HTTPException as err: 47 | logger.error(err.detail) 48 | raise err 49 | except Exception as err: 50 | logger.error(err) 51 | raise err 52 | -------------------------------------------------------------------------------- /python_fastapi_firebase_authentication/modules/user/data/mongoDb/dbConstants.py: -------------------------------------------------------------------------------- 1 | class MongoDbConstant: 2 | 3 | class AuthProvider: 4 | 5 | class AuthType: 6 | firebase = 0x1 7 | 8 | class Providers: 9 | google = "google.com" 10 | phone = "phone" 11 | password = "password" 12 | 13 | class User: 14 | class Status: 15 | unverified = 0x1 16 | unverifiedMask = 0x1 17 | verified = 0x2 18 | verifiedMask = 0x2 19 | statusMask = 0xf 20 | 21 | class Role: 22 | player = 0x1 23 | playerMask = 0x1 24 | admin = 0x2 25 | adminMask = 0x2 26 | roleMask = 0xf 27 | 28 | 29 | -------------------------------------------------------------------------------- /python_fastapi_firebase_authentication/modules/user/data/mongoDb/interface/authProvider.py: -------------------------------------------------------------------------------- 1 | 2 | from typing import Optional 3 | from beanie import PydanticObjectId 4 | # from loguru import logger 5 | 6 | from ..models.authProvider import AuthProvider 7 | from ....model.model import UpdateAuthProvider 8 | 9 | 10 | class MongoDbAuthProvider: 11 | 12 | async def add(self, modelAuth : AuthProvider) -> AuthProvider: 13 | authUser = await modelAuth.create() 14 | return authUser 15 | 16 | async def getByProviderUserId(self, pUserId : PydanticObjectId) -> AuthProvider: 17 | authUser = await AuthProvider.find(AuthProvider.pUid == pUserId).first_or_none() 18 | return authUser 19 | 20 | async def getByIdentifier(self, identifier : str, provider : Optional[str]) -> AuthProvider: 21 | authUser = await AuthProvider.find(AuthProvider.credentials == identifier).find(AuthProvider.provider == provider).first_or_none() 22 | return authUser 23 | 24 | async def updateSelf(self, updateModel:UpdateAuthProvider) -> AuthProvider: 25 | authUser = await AuthProvider.get(document_id=updateModel.id) 26 | authUser = authUser.copy(update=updateModel.dict(exclude_unset=True)) 27 | await authUser.save() 28 | return authUser 29 | 30 | 31 | -------------------------------------------------------------------------------- /python_fastapi_firebase_authentication/modules/user/data/mongoDb/interface/player.py: -------------------------------------------------------------------------------- 1 | 2 | from beanie import PydanticObjectId 3 | from loguru import logger 4 | 5 | from ..models.player import Player 6 | from ....model.model import PlayerUpdate 7 | 8 | 9 | class MongoDbPlayer: 10 | 11 | async def add(self, player : Player) -> Player: 12 | player = await player.create() 13 | return player 14 | 15 | async def update(self, dbPlayer:Player, pModel:PlayerUpdate) -> Player: 16 | try: 17 | logger.debug(dbPlayer) 18 | await dbPlayer.set(expression=pModel.dict(exclude_unset=True)) 19 | logger.debug(dbPlayer) 20 | return dbPlayer 21 | except Exception as err: 22 | logger.error('error in', err) 23 | raise err 24 | 25 | 26 | async def getById(self, id : PydanticObjectId) -> Player: 27 | try: 28 | player = await Player.get(document_id=id) 29 | return player 30 | except Exception as err: 31 | raise err 32 | 33 | async def getByUserId(self, userId : PydanticObjectId) -> Player: 34 | logger.info(userId) 35 | player = await Player.find(Player.userId == userId).first_or_none() 36 | # logger.info(player.id) 37 | return player 38 | 39 | -------------------------------------------------------------------------------- /python_fastapi_firebase_authentication/modules/user/data/mongoDb/interface/user.py: -------------------------------------------------------------------------------- 1 | 2 | from beanie import PydanticObjectId 3 | 4 | from ..models.user import User 5 | 6 | 7 | class MongoDbUser: 8 | def __init__(self) -> None: 9 | self.dbInterface = User 10 | 11 | async def add(self, user : User) -> User: 12 | dbUser = await user.create() 13 | return dbUser 14 | 15 | async def getById(self, id : PydanticObjectId) -> User: 16 | dbUser = await User.get(document_id=id) 17 | return dbUser 18 | 19 | -------------------------------------------------------------------------------- /python_fastapi_firebase_authentication/modules/user/data/mongoDb/models/authProvider.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | from beanie import Document, PydanticObjectId 3 | from pydantic import BaseModel 4 | import pymongo 5 | 6 | 7 | 8 | class AuthProvider(Document, BaseModel): 9 | 10 | userId : PydanticObjectId 11 | pUid : Optional[str] 12 | credentials : str 13 | provider : str 14 | authType : int 15 | status : Optional[int] 16 | 17 | class Settings: 18 | name = "authProviders" # mongodb collection name 19 | indexes = [ 20 | [ 21 | ("credentials", pymongo.ASCENDING), 22 | ("authType", pymongo.ASCENDING) 23 | ] 24 | ] 25 | 26 | class Config: 27 | schema_extra = { 28 | "example": { 29 | "userId": "_id", 30 | "pUid": "asdjkjfdsfdjj", 31 | "credentials": "email@email.com", 32 | "provider": "firebase", 33 | "authType": 0x0, 34 | "status" : None 35 | } 36 | } 37 | 38 | 39 | -------------------------------------------------------------------------------- /python_fastapi_firebase_authentication/modules/user/data/mongoDb/models/player.py: -------------------------------------------------------------------------------- 1 | from typing import List, Optional 2 | from beanie import Document, PydanticObjectId 3 | from pydantic import BaseModel, EmailStr 4 | 5 | 6 | class Address(BaseModel): 7 | name: Optional[str] 8 | line1: Optional[str] 9 | line2: Optional[str] 10 | city: str 11 | state: str 12 | pin: str 13 | 14 | class Contact(BaseModel): 15 | phone: Optional[str] 16 | email: Optional[str] 17 | 18 | 19 | class Player(Document): 20 | 21 | # ref user table 22 | userId : PydanticObjectId 23 | name : str 24 | address : Optional[Address] 25 | contact : Contact 26 | communes : Optional[List[PydanticObjectId]] 27 | verifiedPhone : Optional[str] 28 | verifiedEmail : Optional[EmailStr] 29 | imageUrl : Optional[str] 30 | status : Optional[int] = None 31 | 32 | class Settings: 33 | name = "players" # mongodb collection name 34 | 35 | 36 | class Config: 37 | schema_extra = { 38 | "example": { 39 | "userId": "_id", 40 | "name": "rahul", 41 | "address": '{"city" : "adj", "state" : "adasd", "pin" : "111111"}', 42 | "contact": "[phone : 12334454, email : email@email.com]", 43 | "communes": "[asdfweesd, eawasdffe]", 44 | "status" : "s" 45 | } 46 | } 47 | 48 | 49 | -------------------------------------------------------------------------------- /python_fastapi_firebase_authentication/modules/user/data/mongoDb/models/user.py: -------------------------------------------------------------------------------- 1 | 2 | from beanie import Document 3 | 4 | 5 | # role -> 0 : admin, 1 : player 6 | # status -> 0 : inactive, 1 : active 7 | 8 | 9 | class User(Document): 10 | 11 | name : str 12 | role : int 13 | status : int 14 | 15 | class Settings: 16 | name = "users" # mongodb collection name 17 | 18 | class Config: 19 | schema_extra = { 20 | "example": { 21 | "Id": "_id", 22 | "name":"rahul", 23 | "role": 1, 24 | "status" : 1 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /python_fastapi_firebase_authentication/modules/user/model/model.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | from pydantic import BaseModel 3 | from beanie import PydanticObjectId 4 | 5 | from ..data.mongoDb.models.user import User 6 | from ..data.mongoDb.models.player import Player 7 | from ..data.mongoDb.models.player import Address, Player, Contact 8 | 9 | 10 | 11 | class UserAuthRequest(BaseModel): 12 | token : str 13 | verificationType : str 14 | name : Optional[str] = None 15 | 16 | class Config: 17 | schema_extra = { 18 | "example": { 19 | "token" : "asdfdsiuffdsdfnlasfd6asf5f16dsf4645sdfsdfdsfdsj", 20 | "verificationType": "player", 21 | "name" : "rahul" 22 | } 23 | } 24 | 25 | 26 | class AuthorisedUserOut(BaseModel): 27 | user : User 28 | player : Player 29 | 30 | class UpdateAuthProvider(BaseModel): 31 | id : PydanticObjectId 32 | pUid : Optional[str] 33 | status : Optional[str] 34 | 35 | 36 | 37 | 38 | class PlayerUpdate(BaseModel): 39 | name : Optional[str] 40 | address : Optional[Address] 41 | contact : Optional[Contact] 42 | imageUrl : Optional[str] 43 | 44 | class PlayerUpdateOut(BaseModel): 45 | isUpdated : bool 46 | item : Player 47 | 48 | class UpdateUser(BaseModel): 49 | id : PydanticObjectId 50 | name : Optional[str] 51 | role : Optional[int] 52 | status : Optional[int] 53 | -------------------------------------------------------------------------------- /python_fastapi_firebase_authentication/modules/user/route/endpoints.py: -------------------------------------------------------------------------------- 1 | from fastapi import APIRouter 2 | 3 | from ....modules.user.controller import authVerify, player 4 | 5 | 6 | 7 | 8 | userRouter = APIRouter() 9 | 10 | userRouter.include_router(authVerify.router, prefix="/auth", tags=["auth"]) 11 | userRouter.include_router(player.router, prefix="/player", tags=["player"]) 12 | 13 | -------------------------------------------------------------------------------- /python_fastapi_firebase_authentication/utils/bitwise.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | class Bitwise: 4 | mask = [0xf, 0xf0, 0xf00, 0xf000] 5 | 6 | def containsValue(self, originalValue:int, maskValue:int, checkFor:int) -> bool: 7 | n = self.mask.index(maskValue) 8 | return ((originalValue & maskValue) == (checkFor << 4*n)) 9 | 10 | 11 | def clearValue(self, value:int, maskValue:int) -> int: 12 | return ((value & (~maskValue)) & maskValue) 13 | 14 | 15 | def attachValue(self, originalValue:int, maskValue:int, attachValue:int) -> int: 16 | n = self.mask.index(maskValue) 17 | return ((originalValue & (~maskValue)) | (attachValue << 4*n)) 18 | 19 | def getValue(self, originalVal:int, maskVal:int) -> int: 20 | n = self.mask.index(maskVal) 21 | 22 | return ((originalVal & maskVal) >> 4*n) 23 | -------------------------------------------------------------------------------- /python_fastapi_firebase_authentication/utils/dependencies.py: -------------------------------------------------------------------------------- 1 | from beanie import PydanticObjectId 2 | from fastapi import Depends, HTTPException, Header, status 3 | from firebase_admin import auth 4 | from loguru import logger 5 | 6 | from ..modules.user.data.mongoDb.interface.authProvider import MongoDbAuthProvider 7 | from ..modules.user.data.mongoDb.interface.player import MongoDbPlayer 8 | 9 | mongoDbAuthProvider = MongoDbAuthProvider() 10 | mongoDbPlayer = MongoDbPlayer() 11 | 12 | async def decodeToken(token : str = Header()): 13 | try: 14 | deToken : dict = auth.verify_id_token(id_token=token) 15 | pUserId:str = deToken.get("user_id") 16 | dbAuthProvider = await mongoDbAuthProvider.getByProviderUserId(pUserId=pUserId) 17 | userId = dbAuthProvider.userId 18 | return userId 19 | except Exception as e: 20 | logger.error(e) 21 | raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="user not authorised") 22 | 23 | 24 | async def currentPlayer(userId : PydanticObjectId = Depends(decodeToken)): 25 | player = await mongoDbPlayer.getByUserId(userId=userId) 26 | return player 27 | -------------------------------------------------------------------------------- /serve_uvicorn.py: -------------------------------------------------------------------------------- 1 | 2 | import uvicorn 3 | # import gunicorn 4 | # import uvicorn.workers.UvicornWorker 5 | 6 | from python_fastapi_firebase_authentication.core.env import UVI_PORT, UVI_SERVER_HOST, UVI_LOG_LEVEL, UVI_ACCESS_LOG, UVI_RELOAD 7 | 8 | 9 | def uvi_server(): 10 | 11 | uvicorn.run( 12 | "main:app", 13 | host=UVI_SERVER_HOST, 14 | port=UVI_PORT, 15 | reload=UVI_RELOAD, 16 | use_colors=True, 17 | access_log=UVI_ACCESS_LOG, 18 | log_level=UVI_LOG_LEVEL 19 | ) 20 | 21 | # gunicorn main:app --workers 4 --worker-class uvicorn.workers.UvicornWorker --bind 0.0.0.0:80 22 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rahulprakash11/Python-Firebase-Authentication-FastAPI/7341e6a223eca6388bb22047567777e52b088d9e/tests/__init__.py -------------------------------------------------------------------------------- /tests/test_python_fastapi_firebase_authentication.py: -------------------------------------------------------------------------------- 1 | from python_fastapi_firebase_authentication import __version__ 2 | 3 | 4 | def test_version(): 5 | assert __version__ == '0.1.0' 6 | --------------------------------------------------------------------------------