├── .build └── Dockerfile ├── .github ├── dependabot.yml └── workflows │ ├── codeql-analysis.yml │ ├── docker-image.yml │ ├── lint.yml │ └── update_dep.yml ├── .gitignore ├── .pdm-python ├── README.md ├── accounts ├── __init__.py ├── endpoints.py ├── piccolo_app.py ├── piccolo_migrations │ └── __init__.py └── tables.py ├── app.py ├── conftest.py ├── docker-compose.yml ├── home ├── __init__.py ├── endpoints.py ├── piccolo_app.py ├── piccolo_migrations │ └── README.md ├── tables.py └── templates │ ├── base.html.jinja │ └── home.html.jinja ├── main.py ├── pdm.lock ├── piccolo_conf.py ├── piccolo_conf_test.py ├── pyproject.toml ├── sample.env ├── settings.py └── static ├── favicon.ico └── main.css /.build/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.12.2-slim-bullseye 2 | WORKDIR /opt/app 3 | ENV PYTHONDONTWRITEBYTECODE 1 4 | ENV PYTHONUNBUFFERED 1 5 | 6 | # Install required packages 7 | RUN apt-get update --allow-releaseinfo-change && \ 8 | apt-get update && \ 9 | apt-get upgrade -y 10 | RUN apt-get install -y git 11 | RUN apt-get install -y vim 12 | 13 | # Install project requirements 14 | COPY ["pyproject.toml", "pdm.lock", "/opt/app/"] 15 | RUN pip install --upgrade pip 16 | RUN pip install pdm 17 | RUN pdm venv create -f -n env 18 | RUN pdm sync --dev 19 | COPY . /opt/app/ 20 | EXPOSE 8000 21 | 22 | CMD exec /bin/sh -c "trap : TERM INT; (while true; do sleep 1000; done) & wait" 23 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "pip" 9 | directory: "/" 10 | schedule: 11 | interval: "daily" 12 | 13 | 14 | - package-ecosystem: 'github-actions' 15 | directory: '/' 16 | schedule: 17 | interval: 'daily' 18 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ master ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ master ] 20 | schedule: 21 | - cron: '26 2 * * 6' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'python' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 37 | # Learn more about CodeQL language support at https://git.io/codeql-language-support 38 | 39 | steps: 40 | - name: Checkout repository 41 | uses: actions/checkout@v4 42 | 43 | # Initializes the CodeQL tools for scanning. 44 | - name: Initialize CodeQL 45 | uses: github/codeql-action/init@v3 46 | with: 47 | languages: ${{ matrix.language }} 48 | # If you wish to specify custom queries, you can do so here or in a config file. 49 | # By default, queries listed here will override any specified in a config file. 50 | # Prefix the list here with "+" to use these queries and those in the config file. 51 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 52 | 53 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 54 | # If this step fails, then you should remove it and run the build manually (see below) 55 | - name: Autobuild 56 | uses: github/codeql-action/autobuild@v3 57 | 58 | # ℹ️ Command-line programs to run using the OS shell. 59 | # 📚 https://git.io/JvXDl 60 | 61 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 62 | # and modify them (or add more) to build your code if your project 63 | # uses a compiled language 64 | 65 | #- run: | 66 | # make bootstrap 67 | # make release 68 | 69 | - name: Perform CodeQL Analysis 70 | uses: github/codeql-action/analyze@v3 71 | -------------------------------------------------------------------------------- /.github/workflows/docker-image.yml: -------------------------------------------------------------------------------- 1 | name: Docker Image CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | 11 | build: 12 | 13 | runs-on: ubuntu-latest 14 | services: 15 | postgres: 16 | image: postgres:14 17 | env: 18 | POSTGRES_PASSWORD: postgres 19 | options: >- 20 | --health-cmd pg_isready 21 | --health-interval 10s 22 | --health-timeout 5s 23 | --health-retries 5 24 | ports: 25 | - 5432:5432 26 | 27 | steps: 28 | - uses: actions/checkout@v4 29 | - name: Setup env 30 | run: | 31 | rm sample.env 32 | touch sample.env 33 | echo DB_HOST=postgres >> sample.env 34 | echo DB_TEST_NAME=test >> sample.env 35 | echo DB_TEST_USER=postgres >> sample.env 36 | echo DB_TEST_PASSWORD=postgres >> sample.env 37 | echo DB_TEST_HOST=postgres >> sample.env 38 | echo DB_TEST_PORT=5432 >> sample.env 39 | 40 | - name: Build the Docker image 41 | run: docker build . --file .build/Dockerfile --tag fastapi-piccolo:dev 42 | # - name: Setup postgres 43 | # run: | 44 | # export PGPASSWORD=postgres 45 | # psql -h localhost -c 'CREATE DATABASE test;' -U postgres 46 | 47 | # - name: Run tests 48 | # run: docker run fastapi-piccolo:dev piccolo tester run 49 | 50 | # - name: Upload coverage 51 | # uses: codecov/codecov-action@v1 52 | 53 | 54 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | 3 | on: 4 | # Trigger the workflow on push or pull request, 5 | # but only for the main branch 6 | push: 7 | branches: 8 | - master 9 | pull_request: 10 | branches: 11 | - master 12 | 13 | jobs: 14 | run-linters: 15 | name: Run linters 16 | runs-on: ubuntu-latest 17 | 18 | steps: 19 | - name: Check out Git repository 20 | uses: actions/checkout@v4 21 | 22 | - name: Set up Python 23 | uses: actions/setup-python@v5 24 | with: 25 | python-version: 3.10.5 26 | 27 | - name: Install Python dependencies 28 | run: pip install black 29 | 30 | - name: Run linters 31 | uses: wearerequired/lint-action@v2 32 | with: 33 | black: true 34 | auto_fix: true 35 | -------------------------------------------------------------------------------- /.github/workflows/update_dep.yml: -------------------------------------------------------------------------------- 1 | name: Update dependencies 2 | 3 | on: 4 | schedule: 5 | - cron: "0 0 * * *" 6 | workflow_dispatch: 7 | 8 | jobs: 9 | update-dependencies: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v4 13 | 14 | - name: Update dependencies 15 | uses: pdm-project/update-deps-action@main 16 | with: 17 | # The personal access token, default: ${{ github.token }} 18 | token: ${{ secrets.GITHUB_TOKEN }} 19 | # The commit message" 20 | commit-message: "chore: Update pdm.lock" 21 | # The PR title 22 | pr-title: "Update dependencies" 23 | # The update strategy, can be 'reuse', 'eager' or 'all' 24 | update-strategy: eager 25 | # Whether to install PDM plugins before update 26 | install-plugins: "false" 27 | # Whether commit message contains signed-off-by 28 | sign-off-commit: "false" 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | .idea 3 | .vscode 4 | .DS_Store 5 | .env.example 6 | /__pypackages__/ -------------------------------------------------------------------------------- /.pdm-python: -------------------------------------------------------------------------------- 1 | /home/runner/work/FastAPI-Piccolo-Template/FastAPI-Piccolo-Template/.venv/bin/python -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FastAPI-Piccolo project template 2 | - Amazing [FastAPI](https://github.com/tiangolo/fastapi) framework. 3 | - Full Docker integration 4 | - Docker Compose integration and optimization for local development. 5 | - JWT token authentication. 6 | - Secure password hashing by default. 7 | - [Piccolo Admin](https://github.com/piccolo-orm/piccolo_admin) frontend 8 | - [Piccolo async ORM](https://github.com/piccolo-orm/piccolo) 9 | - simple CI/CD 10 | 11 | 12 | See each project's documentation for more information. 13 | 14 | 15 | ## Setup 16 | 17 | ### build the containers 18 | 19 | ```bash 20 | docker-compose build 21 | ``` 22 | ### run the server for local development 23 | 24 | ```bash 25 | docker-compose up 26 | ``` 27 | 28 | 29 | ### Running tests 30 | 31 | ```bash 32 | docker-compose run web sh -c "pdm run test" 33 | ``` 34 | 35 | 36 | ## todo: 37 | - [ ] add load balancer (Traefik) 38 | - [ ] add caching mechanism 39 | - [ ] add celery and flower for monitoring jobs 40 | - [X] add pgadmin 41 | - [ ] full CI/CD actions 42 | - [ ] Gunicorn integration for single server mode 43 | - [ ] Docker swarm integration + kubernetes for cluster mode 44 | - [ ] CookieCutter 45 | -------------------------------------------------------------------------------- /accounts/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AliSayyah/FastAPI-Piccolo-Template/426399074fe3db98237fc30b18586fb677a1ab41/accounts/__init__.py -------------------------------------------------------------------------------- /accounts/endpoints.py: -------------------------------------------------------------------------------- 1 | import typing as t 2 | from datetime import timedelta, datetime 3 | 4 | from fastapi import Depends, HTTPException, status, APIRouter 5 | from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm 6 | from jose import JWTError, jwt 7 | from piccolo.apps.user.tables import BaseUser 8 | 9 | from accounts.tables import TokenData, Token, UserModelOut, UserModelIn 10 | from settings import SECRET_KEY, ALGORITHM, ACCESS_TOKEN_EXPIRE_MINUTES 11 | 12 | oauth2_scheme = OAuth2PasswordBearer(tokenUrl="accounts/login") 13 | accounts_router = APIRouter(prefix="/accounts") 14 | 15 | 16 | def create_access_token(data: dict, expires_delta: t.Optional[timedelta] = None): 17 | to_encode = data.copy() 18 | if expires_delta: 19 | expire = datetime.utcnow() + expires_delta 20 | else: 21 | expire = datetime.utcnow() + timedelta(minutes=15) 22 | to_encode.update({"exp": expire}) 23 | encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM) 24 | return encoded_jwt 25 | 26 | 27 | async def get_current_user(token: str = Depends(oauth2_scheme)): 28 | credentials_exception = HTTPException( 29 | status_code=status.HTTP_401_UNAUTHORIZED, 30 | detail="Could not validate credentials", 31 | headers={"WWW-Authenticate": "Bearer"}, 32 | ) 33 | try: 34 | payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) 35 | username: str = payload.get("sub") 36 | if username is None: 37 | raise credentials_exception 38 | token_data = TokenData(username=username) 39 | except JWTError: 40 | raise credentials_exception 41 | user = ( 42 | await BaseUser.select() 43 | .where(BaseUser.username == token_data.username) 44 | .first() 45 | .run() 46 | ) 47 | if user is None: 48 | raise credentials_exception 49 | return user 50 | 51 | 52 | @accounts_router.post("/login/", response_model=Token, tags=["Auth"]) 53 | async def login_user( 54 | form_data: OAuth2PasswordRequestForm = Depends(), 55 | ): 56 | user = await BaseUser.login( 57 | username=form_data.username, password=form_data.password 58 | ) 59 | result = await BaseUser.select().where(BaseUser.id == user).first().run() 60 | if not user: 61 | raise HTTPException( 62 | status_code=status.HTTP_401_UNAUTHORIZED, 63 | detail="Incorrect username or password", 64 | headers={"WWW-Authenticate": "Bearer"}, 65 | ) 66 | access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES) 67 | access_token = create_access_token( 68 | data={"sub": result["username"]}, expires_delta=access_token_expires 69 | ) 70 | return { 71 | "access_token": access_token, 72 | "token_type": "bearer", 73 | } 74 | 75 | 76 | @accounts_router.post("/register/", response_model=UserModelOut, tags=["Auth"]) 77 | async def register_user(user: UserModelIn): 78 | user = BaseUser(**user.__dict__) 79 | if ( 80 | await BaseUser.exists().where(BaseUser.email == str(user.email)).run() 81 | or await BaseUser.exists().where(BaseUser.username == str(user.username)).run() 82 | ): 83 | raise HTTPException( 84 | status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, 85 | detail="User with that email or username already exists.", 86 | ) 87 | await user.save().run() 88 | return UserModelOut(**user.__dict__) 89 | -------------------------------------------------------------------------------- /accounts/piccolo_app.py: -------------------------------------------------------------------------------- 1 | """ 2 | Import all of the Tables subclasses in your app here, and register them with 3 | the APP_CONFIG. 4 | """ 5 | 6 | import os 7 | 8 | from piccolo.conf.apps import AppConfig 9 | 10 | 11 | CURRENT_DIRECTORY = os.path.dirname(os.path.abspath(__file__)) 12 | 13 | 14 | APP_CONFIG = AppConfig( 15 | app_name="accounts", 16 | migrations_folder_path=os.path.join(CURRENT_DIRECTORY, "piccolo_migrations"), 17 | table_classes=[], 18 | migration_dependencies=[], 19 | commands=[], 20 | ) 21 | -------------------------------------------------------------------------------- /accounts/piccolo_migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AliSayyah/FastAPI-Piccolo-Template/426399074fe3db98237fc30b18586fb677a1ab41/accounts/piccolo_migrations/__init__.py -------------------------------------------------------------------------------- /accounts/tables.py: -------------------------------------------------------------------------------- 1 | import typing as t 2 | 3 | from piccolo.apps.user.tables import BaseUser 4 | from piccolo.utils.pydantic import create_pydantic_model 5 | from pydantic import BaseModel 6 | 7 | 8 | # token schema 9 | class Token(BaseModel): 10 | access_token: str 11 | token_type: str 12 | 13 | 14 | class TokenData(BaseModel): 15 | username: t.Optional[str] = None 16 | 17 | 18 | UserModelIn = create_pydantic_model( 19 | table=BaseUser, 20 | model_name="UserModelIn", 21 | ) 22 | UserModelOut = create_pydantic_model( 23 | table=BaseUser, 24 | include_default_columns=True, 25 | model_name="UserModelOut", 26 | ) 27 | -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | from fastapi import FastAPI 2 | from fastapi.middleware.cors import CORSMiddleware 3 | from piccolo.engine import engine_finder 4 | from piccolo_admin.endpoints import create_admin 5 | from starlette.routing import Route, Mount 6 | from starlette.staticfiles import StaticFiles 7 | 8 | from accounts.endpoints import accounts_router 9 | from home.endpoints import HomeEndpoint 10 | from home.piccolo_app import APP_CONFIG 11 | from settings import ORIGINS 12 | 13 | app = FastAPI( 14 | routes=[ 15 | Route("/", HomeEndpoint), 16 | Mount( 17 | "/admin/", 18 | create_admin( 19 | tables=APP_CONFIG.table_classes, 20 | # Required when running under HTTPS: 21 | # allowed_hosts=['my_site.com'] 22 | ), 23 | ), 24 | Mount("/static/", StaticFiles(directory="static")), 25 | ], 26 | ) 27 | 28 | app.add_middleware( 29 | CORSMiddleware, 30 | allow_origins=ORIGINS, 31 | allow_credentials=True, 32 | allow_methods=["*"], 33 | allow_headers=["*"], 34 | ) 35 | 36 | app.include_router(accounts_router) 37 | 38 | 39 | @app.on_event("startup") 40 | async def open_database_connection_pool(): 41 | try: 42 | engine = engine_finder() 43 | await engine.start_connection_pool() 44 | except Exception as e: 45 | print(f"Unable to connect to the database: {e}") 46 | 47 | 48 | @app.on_event("shutdown") 49 | async def close_database_connection_pool(): 50 | try: 51 | engine = engine_finder() 52 | await engine.close_connection_pool() 53 | except Exception as e: 54 | print(f"Unable to connect to the database: {e}") 55 | -------------------------------------------------------------------------------- /conftest.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | from piccolo.utils.warnings import colored_warning 5 | 6 | 7 | def pytest_configure(*args): 8 | if os.environ.get("PICCOLO_TEST_RUNNER") != "True": 9 | colored_warning( 10 | "\n\n" 11 | "We recommend running Piccolo tests using the " 12 | "`piccolo tester run` command, which wraps Pytest, and makes " 13 | "sure the test database is being used. " 14 | "To stop this warning, modify conftest.py." 15 | "\n\n" 16 | ) 17 | sys.exit(1) 18 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.8" 2 | 3 | volumes: 4 | psql_data: 5 | pgadmin_data: 6 | pdm_data: 7 | 8 | services: 9 | web: 10 | build: 11 | dockerfile: .build/Dockerfile 12 | context: . 13 | restart: unless-stopped 14 | image: fastapi-piccolo:dev 15 | volumes: 16 | - ./:/opt/app 17 | - pdm_data:/root/.local/share/pdm/venvs/app-jNE7dXAC-env:z 18 | ports: 19 | - "8000:8000" 20 | depends_on: 21 | - db 22 | command: bash -c 'while ! 2 | 3 | 4 | 5 | 6 | ASGI 7 | 8 | 9 | 10 | 11 | 12 | 13 | {% block content %}{% endblock %} 14 | 15 | 16 | -------------------------------------------------------------------------------- /home/templates/home.html.jinja: -------------------------------------------------------------------------------- 1 | {% extends "base.html.jinja" %} 2 | 3 | {% block content %} 4 |
5 |

{{ title }}

6 |
7 |
8 |
9 |

Postgres

10 |

Make sure you create the database. See the docs for guidance.

11 |

See piccolo_conf.py for the database settings.

12 |
13 | 14 |
15 |

Migrations

16 |

To use the admin, first run the migrations. This will create the user and session tables in the database:

17 |

18 | piccolo migrations forwards session_auth 19 | piccolo migrations forwards user 20 |

21 |

Then create a new user, making sure they're an admin.

22 |

23 | piccolo user create 24 |

25 |
26 | 27 |
28 |

Custom Tables

29 |

An example table called Task exists in tables.py.

30 |

When you're ready, create a migration, and run it to add the table to the database:

31 |

32 | piccolo migrations new home --auto 33 | piccolo migrations forwards home 34 |

35 |
36 | 37 |
38 |

Try it out

39 |

FastAPI

40 | 44 |

Starlette

45 | 49 |

BlackSheep

50 | 54 |
55 |
56 | {% endblock content %} 57 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | if __name__ == "__main__": 2 | import uvicorn 3 | from settings import RELOAD 4 | 5 | kwargs = {"reload": RELOAD, "host": "0.0.0.0"} 6 | 7 | uvicorn.run("app:app", **kwargs) 8 | -------------------------------------------------------------------------------- /pdm.lock: -------------------------------------------------------------------------------- 1 | # This file is @generated by PDM. 2 | # It is not intended for manual editing. 3 | 4 | [metadata] 5 | groups = ["default"] 6 | strategy = ["cross_platform"] 7 | lock_version = "4.5.0" 8 | content_hash = "sha256:08cd8d0b1905394bbeca1e1888b8f03c807bc5222ee059ca822fc769df55de20" 9 | 10 | [[metadata.targets]] 11 | requires_python = ">=3.10" 12 | 13 | [[package]] 14 | name = "aiofiles" 15 | version = "24.1.0" 16 | requires_python = ">=3.8" 17 | summary = "File support for asyncio." 18 | files = [ 19 | {file = "aiofiles-24.1.0-py3-none-any.whl", hash = "sha256:b4ec55f4195e3eb5d7abd1bf7e061763e864dd4954231fb8539a0ef8bb8260e5"}, 20 | {file = "aiofiles-24.1.0.tar.gz", hash = "sha256:22a075c9e5a3810f0c2e48f3008c94d68c65d763b9b03857924c99e57355166c"}, 21 | ] 22 | 23 | [[package]] 24 | name = "annotated-types" 25 | version = "0.7.0" 26 | requires_python = ">=3.8" 27 | summary = "Reusable constraint types to use with typing.Annotated" 28 | dependencies = [ 29 | "typing-extensions>=4.0.0; python_version < \"3.9\"", 30 | ] 31 | files = [ 32 | {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, 33 | {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, 34 | ] 35 | 36 | [[package]] 37 | name = "anyio" 38 | version = "4.9.0" 39 | requires_python = ">=3.9" 40 | summary = "High level compatibility layer for multiple asynchronous event loop implementations" 41 | dependencies = [ 42 | "exceptiongroup>=1.0.2; python_version < \"3.11\"", 43 | "idna>=2.8", 44 | "sniffio>=1.1", 45 | "typing-extensions>=4.5; python_version < \"3.13\"", 46 | ] 47 | files = [ 48 | {file = "anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c"}, 49 | {file = "anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028"}, 50 | ] 51 | 52 | [[package]] 53 | name = "async-timeout" 54 | version = "5.0.1" 55 | requires_python = ">=3.8" 56 | summary = "Timeout context manager for asyncio programs" 57 | files = [ 58 | {file = "async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c"}, 59 | {file = "async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3"}, 60 | ] 61 | 62 | [[package]] 63 | name = "asyncpg" 64 | version = "0.30.0" 65 | requires_python = ">=3.8.0" 66 | summary = "An asyncio PostgreSQL driver" 67 | dependencies = [ 68 | "async-timeout>=4.0.3; python_version < \"3.11.0\"", 69 | ] 70 | files = [ 71 | {file = "asyncpg-0.30.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bfb4dd5ae0699bad2b233672c8fc5ccbd9ad24b89afded02341786887e37927e"}, 72 | {file = "asyncpg-0.30.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dc1f62c792752a49f88b7e6f774c26077091b44caceb1983509edc18a2222ec0"}, 73 | {file = "asyncpg-0.30.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3152fef2e265c9c24eec4ee3d22b4f4d2703d30614b0b6753e9ed4115c8a146f"}, 74 | {file = "asyncpg-0.30.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c7255812ac85099a0e1ffb81b10dc477b9973345793776b128a23e60148dd1af"}, 75 | {file = "asyncpg-0.30.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:578445f09f45d1ad7abddbff2a3c7f7c291738fdae0abffbeb737d3fc3ab8b75"}, 76 | {file = "asyncpg-0.30.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c42f6bb65a277ce4d93f3fba46b91a265631c8df7250592dd4f11f8b0152150f"}, 77 | {file = "asyncpg-0.30.0-cp310-cp310-win32.whl", hash = "sha256:aa403147d3e07a267ada2ae34dfc9324e67ccc4cdca35261c8c22792ba2b10cf"}, 78 | {file = "asyncpg-0.30.0-cp310-cp310-win_amd64.whl", hash = "sha256:fb622c94db4e13137c4c7f98834185049cc50ee01d8f657ef898b6407c7b9c50"}, 79 | {file = "asyncpg-0.30.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5e0511ad3dec5f6b4f7a9e063591d407eee66b88c14e2ea636f187da1dcfff6a"}, 80 | {file = "asyncpg-0.30.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:915aeb9f79316b43c3207363af12d0e6fd10776641a7de8a01212afd95bdf0ed"}, 81 | {file = "asyncpg-0.30.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c198a00cce9506fcd0bf219a799f38ac7a237745e1d27f0e1f66d3707c84a5a"}, 82 | {file = "asyncpg-0.30.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3326e6d7381799e9735ca2ec9fd7be4d5fef5dcbc3cb555d8a463d8460607956"}, 83 | {file = "asyncpg-0.30.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:51da377487e249e35bd0859661f6ee2b81db11ad1f4fc036194bc9cb2ead5056"}, 84 | {file = "asyncpg-0.30.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bc6d84136f9c4d24d358f3b02be4b6ba358abd09f80737d1ac7c444f36108454"}, 85 | {file = "asyncpg-0.30.0-cp311-cp311-win32.whl", hash = "sha256:574156480df14f64c2d76450a3f3aaaf26105869cad3865041156b38459e935d"}, 86 | {file = "asyncpg-0.30.0-cp311-cp311-win_amd64.whl", hash = "sha256:3356637f0bd830407b5597317b3cb3571387ae52ddc3bca6233682be88bbbc1f"}, 87 | {file = "asyncpg-0.30.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c902a60b52e506d38d7e80e0dd5399f657220f24635fee368117b8b5fce1142e"}, 88 | {file = "asyncpg-0.30.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:aca1548e43bbb9f0f627a04666fedaca23db0a31a84136ad1f868cb15deb6e3a"}, 89 | {file = "asyncpg-0.30.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c2a2ef565400234a633da0eafdce27e843836256d40705d83ab7ec42074efb3"}, 90 | {file = "asyncpg-0.30.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1292b84ee06ac8a2ad8e51c7475aa309245874b61333d97411aab835c4a2f737"}, 91 | {file = "asyncpg-0.30.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0f5712350388d0cd0615caec629ad53c81e506b1abaaf8d14c93f54b35e3595a"}, 92 | {file = "asyncpg-0.30.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:db9891e2d76e6f425746c5d2da01921e9a16b5a71a1c905b13f30e12a257c4af"}, 93 | {file = "asyncpg-0.30.0-cp312-cp312-win32.whl", hash = "sha256:68d71a1be3d83d0570049cd1654a9bdfe506e794ecc98ad0873304a9f35e411e"}, 94 | {file = "asyncpg-0.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:9a0292c6af5c500523949155ec17b7fe01a00ace33b68a476d6b5059f9630305"}, 95 | {file = "asyncpg-0.30.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:05b185ebb8083c8568ea8a40e896d5f7af4b8554b64d7719c0eaa1eb5a5c3a70"}, 96 | {file = "asyncpg-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c47806b1a8cbb0a0db896f4cd34d89942effe353a5035c62734ab13b9f938da3"}, 97 | {file = "asyncpg-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b6fde867a74e8c76c71e2f64f80c64c0f3163e687f1763cfaf21633ec24ec33"}, 98 | {file = "asyncpg-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46973045b567972128a27d40001124fbc821c87a6cade040cfcd4fa8a30bcdc4"}, 99 | {file = "asyncpg-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9110df111cabc2ed81aad2f35394a00cadf4f2e0635603db6ebbd0fc896f46a4"}, 100 | {file = "asyncpg-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:04ff0785ae7eed6cc138e73fc67b8e51d54ee7a3ce9b63666ce55a0bf095f7ba"}, 101 | {file = "asyncpg-0.30.0-cp313-cp313-win32.whl", hash = "sha256:ae374585f51c2b444510cdf3595b97ece4f233fde739aa14b50e0d64e8a7a590"}, 102 | {file = "asyncpg-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:f59b430b8e27557c3fb9869222559f7417ced18688375825f8f12302c34e915e"}, 103 | {file = "asyncpg-0.30.0.tar.gz", hash = "sha256:c551e9928ab6707602f44811817f82ba3c446e018bfe1d3abecc8ba5f3eac851"}, 104 | ] 105 | 106 | [[package]] 107 | name = "black" 108 | version = "25.1.0" 109 | requires_python = ">=3.9" 110 | summary = "The uncompromising code formatter." 111 | dependencies = [ 112 | "click>=8.0.0", 113 | "mypy-extensions>=0.4.3", 114 | "packaging>=22.0", 115 | "pathspec>=0.9.0", 116 | "platformdirs>=2", 117 | "tomli>=1.1.0; python_version < \"3.11\"", 118 | "typing-extensions>=4.0.1; python_version < \"3.11\"", 119 | ] 120 | files = [ 121 | {file = "black-25.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:759e7ec1e050a15f89b770cefbf91ebee8917aac5c20483bc2d80a6c3a04df32"}, 122 | {file = "black-25.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e519ecf93120f34243e6b0054db49c00a35f84f195d5bce7e9f5cfc578fc2da"}, 123 | {file = "black-25.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:055e59b198df7ac0b7efca5ad7ff2516bca343276c466be72eb04a3bcc1f82d7"}, 124 | {file = "black-25.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:db8ea9917d6f8fc62abd90d944920d95e73c83a5ee3383493e35d271aca872e9"}, 125 | {file = "black-25.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a39337598244de4bae26475f77dda852ea00a93bd4c728e09eacd827ec929df0"}, 126 | {file = "black-25.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:96c1c7cd856bba8e20094e36e0f948718dc688dba4a9d78c3adde52b9e6c2299"}, 127 | {file = "black-25.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bce2e264d59c91e52d8000d507eb20a9aca4a778731a08cfff7e5ac4a4bb7096"}, 128 | {file = "black-25.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:172b1dbff09f86ce6f4eb8edf9dede08b1fce58ba194c87d7a4f1a5aa2f5b3c2"}, 129 | {file = "black-25.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4b60580e829091e6f9238c848ea6750efed72140b91b048770b64e74fe04908b"}, 130 | {file = "black-25.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e2978f6df243b155ef5fa7e558a43037c3079093ed5d10fd84c43900f2d8ecc"}, 131 | {file = "black-25.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b48735872ec535027d979e8dcb20bf4f70b5ac75a8ea99f127c106a7d7aba9f"}, 132 | {file = "black-25.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:ea0213189960bda9cf99be5b8c8ce66bb054af5e9e861249cd23471bd7b0b3ba"}, 133 | {file = "black-25.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8f0b18a02996a836cc9c9c78e5babec10930862827b1b724ddfe98ccf2f2fe4f"}, 134 | {file = "black-25.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:afebb7098bfbc70037a053b91ae8437c3857482d3a690fefc03e9ff7aa9a5fd3"}, 135 | {file = "black-25.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:030b9759066a4ee5e5aca28c3c77f9c64789cdd4de8ac1df642c40b708be6171"}, 136 | {file = "black-25.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:a22f402b410566e2d1c950708c77ebf5ebd5d0d88a6a2e87c86d9fb48afa0d18"}, 137 | {file = "black-25.1.0-py3-none-any.whl", hash = "sha256:95e8176dae143ba9097f351d174fdaf0ccd29efb414b362ae3fd72bf0f710717"}, 138 | {file = "black-25.1.0.tar.gz", hash = "sha256:33496d5cd1222ad73391352b4ae8da15253c5de89b93a80b3e2c8d9a19ec2666"}, 139 | ] 140 | 141 | [[package]] 142 | name = "certifi" 143 | version = "2025.1.31" 144 | requires_python = ">=3.6" 145 | summary = "Python package for providing Mozilla's CA Bundle." 146 | files = [ 147 | {file = "certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe"}, 148 | {file = "certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651"}, 149 | ] 150 | 151 | [[package]] 152 | name = "click" 153 | version = "8.1.8" 154 | requires_python = ">=3.7" 155 | summary = "Composable command line interface toolkit" 156 | dependencies = [ 157 | "colorama; platform_system == \"Windows\"", 158 | "importlib-metadata; python_version < \"3.8\"", 159 | ] 160 | files = [ 161 | {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, 162 | {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, 163 | ] 164 | 165 | [[package]] 166 | name = "colorama" 167 | version = "0.4.6" 168 | requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" 169 | summary = "Cross-platform colored terminal text." 170 | files = [ 171 | {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, 172 | {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, 173 | ] 174 | 175 | [[package]] 176 | name = "dnspython" 177 | version = "2.6.1" 178 | requires_python = ">=3.8" 179 | summary = "DNS toolkit" 180 | files = [ 181 | {file = "dnspython-2.6.1-py3-none-any.whl", hash = "sha256:5ef3b9680161f6fa89daf8ad451b5f1a33b18ae8a1c6778cdf4b43f08c0a6e50"}, 182 | {file = "dnspython-2.6.1.tar.gz", hash = "sha256:e8f0f9c23a7b7cb99ded64e6c3a6f3e701d78f50c55e002b839dea7225cff7cc"}, 183 | ] 184 | 185 | [[package]] 186 | name = "docstring-parser" 187 | version = "0.16" 188 | requires_python = ">=3.6,<4.0" 189 | summary = "Parse Python docstrings in reST, Google and Numpydoc format" 190 | files = [ 191 | {file = "docstring_parser-0.16-py3-none-any.whl", hash = "sha256:bf0a1387354d3691d102edef7ec124f219ef639982d096e26e3b60aeffa90637"}, 192 | {file = "docstring_parser-0.16.tar.gz", hash = "sha256:538beabd0af1e2db0146b6bd3caa526c35a34d61af9fd2887f3a8a27a739aa6e"}, 193 | ] 194 | 195 | [[package]] 196 | name = "email-validator" 197 | version = "2.1.1" 198 | requires_python = ">=3.8" 199 | summary = "A robust email address syntax and deliverability validation library." 200 | dependencies = [ 201 | "dnspython>=2.0.0", 202 | "idna>=2.0.0", 203 | ] 204 | files = [ 205 | {file = "email_validator-2.1.1-py3-none-any.whl", hash = "sha256:97d882d174e2a65732fb43bfce81a3a834cbc1bde8bf419e30ef5ea976370a05"}, 206 | {file = "email_validator-2.1.1.tar.gz", hash = "sha256:200a70680ba08904be6d1eef729205cc0d687634399a5924d842533efb824b84"}, 207 | ] 208 | 209 | [[package]] 210 | name = "exceptiongroup" 211 | version = "1.2.2" 212 | requires_python = ">=3.7" 213 | summary = "Backport of PEP 654 (exception groups)" 214 | files = [ 215 | {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, 216 | {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, 217 | ] 218 | 219 | [[package]] 220 | name = "fastapi" 221 | version = "0.115.12" 222 | requires_python = ">=3.8" 223 | summary = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" 224 | dependencies = [ 225 | "pydantic!=1.8,!=1.8.1,!=2.0.0,!=2.0.1,!=2.1.0,<3.0.0,>=1.7.4", 226 | "starlette<0.47.0,>=0.40.0", 227 | "typing-extensions>=4.8.0", 228 | ] 229 | files = [ 230 | {file = "fastapi-0.115.12-py3-none-any.whl", hash = "sha256:e94613d6c05e27be7ffebdd6ea5f388112e5e430c8f7d6494a9d1d88d43e814d"}, 231 | {file = "fastapi-0.115.12.tar.gz", hash = "sha256:1e2c2a2646905f9e83d32f04a3f86aff4a286669c6c950ca95b5fd68c2602681"}, 232 | ] 233 | 234 | [[package]] 235 | name = "h11" 236 | version = "0.14.0" 237 | requires_python = ">=3.7" 238 | summary = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" 239 | dependencies = [ 240 | "typing-extensions; python_version < \"3.8\"", 241 | ] 242 | files = [ 243 | {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, 244 | {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, 245 | ] 246 | 247 | [[package]] 248 | name = "h2" 249 | version = "4.2.0" 250 | requires_python = ">=3.9" 251 | summary = "Pure-Python HTTP/2 protocol implementation" 252 | dependencies = [ 253 | "hpack<5,>=4.1", 254 | "hyperframe<7,>=6.1", 255 | ] 256 | files = [ 257 | {file = "h2-4.2.0-py3-none-any.whl", hash = "sha256:479a53ad425bb29af087f3458a61d30780bc818e4ebcf01f0b536ba916462ed0"}, 258 | {file = "h2-4.2.0.tar.gz", hash = "sha256:c8a52129695e88b1a0578d8d2cc6842bbd79128ac685463b887ee278126ad01f"}, 259 | ] 260 | 261 | [[package]] 262 | name = "hpack" 263 | version = "4.1.0" 264 | requires_python = ">=3.9" 265 | summary = "Pure-Python HPACK header encoding" 266 | files = [ 267 | {file = "hpack-4.1.0-py3-none-any.whl", hash = "sha256:157ac792668d995c657d93111f46b4535ed114f0c9c8d672271bbec7eae1b496"}, 268 | {file = "hpack-4.1.0.tar.gz", hash = "sha256:ec5eca154f7056aa06f196a557655c5b009b382873ac8d1e66e79e87535f1dca"}, 269 | ] 270 | 271 | [[package]] 272 | name = "httpcore" 273 | version = "1.0.8" 274 | requires_python = ">=3.8" 275 | summary = "A minimal low-level HTTP client." 276 | dependencies = [ 277 | "certifi", 278 | "h11<0.15,>=0.13", 279 | ] 280 | files = [ 281 | {file = "httpcore-1.0.8-py3-none-any.whl", hash = "sha256:5254cf149bcb5f75e9d1b2b9f729ea4a4b883d1ad7379fc632b727cec23674be"}, 282 | {file = "httpcore-1.0.8.tar.gz", hash = "sha256:86e94505ed24ea06514883fd44d2bc02d90e77e7979c8eb71b90f41d364a1bad"}, 283 | ] 284 | 285 | [[package]] 286 | name = "httpx" 287 | version = "0.28.1" 288 | requires_python = ">=3.8" 289 | summary = "The next generation HTTP client." 290 | dependencies = [ 291 | "anyio", 292 | "certifi", 293 | "httpcore==1.*", 294 | "idna", 295 | ] 296 | files = [ 297 | {file = "httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad"}, 298 | {file = "httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc"}, 299 | ] 300 | 301 | [[package]] 302 | name = "hypercorn" 303 | version = "0.17.3" 304 | requires_python = ">=3.8" 305 | summary = "A ASGI Server based on Hyper libraries and inspired by Gunicorn" 306 | dependencies = [ 307 | "exceptiongroup>=1.1.0; python_version < \"3.11\"", 308 | "h11", 309 | "h2>=3.1.0", 310 | "priority", 311 | "taskgroup; python_version < \"3.11\"", 312 | "tomli; python_version < \"3.11\"", 313 | "typing-extensions; python_version < \"3.11\"", 314 | "wsproto>=0.14.0", 315 | ] 316 | files = [ 317 | {file = "hypercorn-0.17.3-py3-none-any.whl", hash = "sha256:059215dec34537f9d40a69258d323f56344805efb462959e727152b0aa504547"}, 318 | {file = "hypercorn-0.17.3.tar.gz", hash = "sha256:1b37802ee3ac52d2d85270700d565787ab16cf19e1462ccfa9f089ca17574165"}, 319 | ] 320 | 321 | [[package]] 322 | name = "hyperframe" 323 | version = "6.1.0" 324 | requires_python = ">=3.9" 325 | summary = "Pure-Python HTTP/2 framing" 326 | files = [ 327 | {file = "hyperframe-6.1.0-py3-none-any.whl", hash = "sha256:b03380493a519fce58ea5af42e4a42317bf9bd425596f7a0835ffce80f1a42e5"}, 328 | {file = "hyperframe-6.1.0.tar.gz", hash = "sha256:f630908a00854a7adeabd6382b43923a4c4cd4b821fcb527e6ab9e15382a3b08"}, 329 | ] 330 | 331 | [[package]] 332 | name = "idna" 333 | version = "3.10" 334 | requires_python = ">=3.6" 335 | summary = "Internationalized Domain Names in Applications (IDNA)" 336 | files = [ 337 | {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, 338 | {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, 339 | ] 340 | 341 | [[package]] 342 | name = "inflection" 343 | version = "0.5.1" 344 | requires_python = ">=3.5" 345 | summary = "A port of Ruby on Rails inflector to Python" 346 | files = [ 347 | {file = "inflection-0.5.1-py2.py3-none-any.whl", hash = "sha256:f38b2b640938a4f35ade69ac3d053042959b62a0f1076a5bbaa1b9526605a8a2"}, 348 | {file = "inflection-0.5.1.tar.gz", hash = "sha256:1a29730d366e996aaacffb2f1f1cb9593dc38e2ddd30c91250c6dde09ea9b417"}, 349 | ] 350 | 351 | [[package]] 352 | name = "jinja2" 353 | version = "3.1.6" 354 | requires_python = ">=3.7" 355 | summary = "A very fast and expressive template engine." 356 | dependencies = [ 357 | "MarkupSafe>=2.0", 358 | ] 359 | files = [ 360 | {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"}, 361 | {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}, 362 | ] 363 | 364 | [[package]] 365 | name = "markupsafe" 366 | version = "3.0.2" 367 | requires_python = ">=3.9" 368 | summary = "Safely add untrusted strings to HTML/XML markup." 369 | files = [ 370 | {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"}, 371 | {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"}, 372 | {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579"}, 373 | {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d"}, 374 | {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb"}, 375 | {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b"}, 376 | {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c"}, 377 | {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171"}, 378 | {file = "MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50"}, 379 | {file = "MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a"}, 380 | {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d"}, 381 | {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93"}, 382 | {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832"}, 383 | {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84"}, 384 | {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca"}, 385 | {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798"}, 386 | {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e"}, 387 | {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4"}, 388 | {file = "MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d"}, 389 | {file = "MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b"}, 390 | {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf"}, 391 | {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225"}, 392 | {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028"}, 393 | {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8"}, 394 | {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c"}, 395 | {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557"}, 396 | {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22"}, 397 | {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48"}, 398 | {file = "MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30"}, 399 | {file = "MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87"}, 400 | {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd"}, 401 | {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430"}, 402 | {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094"}, 403 | {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396"}, 404 | {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79"}, 405 | {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a"}, 406 | {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca"}, 407 | {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c"}, 408 | {file = "MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1"}, 409 | {file = "MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f"}, 410 | {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c"}, 411 | {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb"}, 412 | {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c"}, 413 | {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d"}, 414 | {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe"}, 415 | {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5"}, 416 | {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a"}, 417 | {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9"}, 418 | {file = "MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6"}, 419 | {file = "MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f"}, 420 | {file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"}, 421 | ] 422 | 423 | [[package]] 424 | name = "mypy-extensions" 425 | version = "1.1.0" 426 | requires_python = ">=3.8" 427 | summary = "Type system extensions for programs checked with the mypy type checker." 428 | files = [ 429 | {file = "mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505"}, 430 | {file = "mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558"}, 431 | ] 432 | 433 | [[package]] 434 | name = "packaging" 435 | version = "25.0" 436 | requires_python = ">=3.8" 437 | summary = "Core utilities for Python packages" 438 | files = [ 439 | {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, 440 | {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, 441 | ] 442 | 443 | [[package]] 444 | name = "pathspec" 445 | version = "0.12.1" 446 | requires_python = ">=3.8" 447 | summary = "Utility library for gitignore style pattern matching of file paths." 448 | files = [ 449 | {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, 450 | {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, 451 | ] 452 | 453 | [[package]] 454 | name = "piccolo" 455 | version = "1.24.2" 456 | requires_python = ">=3.9.0" 457 | summary = "A fast, user friendly ORM and query builder which supports asyncio." 458 | dependencies = [ 459 | "Jinja2>=2.11.0", 460 | "black", 461 | "colorama>=0.4.0", 462 | "inflection>=0.5.1", 463 | "pydantic[email]==2.*", 464 | "targ>=0.3.7", 465 | "typing-extensions>=4.3.0", 466 | ] 467 | files = [ 468 | {file = "piccolo-1.24.2-py3-none-any.whl", hash = "sha256:50118f760196bbff20fcc67d657a8adb99b6a929fa2a4928e46119d12945b63b"}, 469 | {file = "piccolo-1.24.2.tar.gz", hash = "sha256:764ec1ad37f419aecd9df10cd12c0b5d59a37c52dcf9c99fc72682d41f9ba1b8"}, 470 | ] 471 | 472 | [[package]] 473 | name = "piccolo-admin" 474 | version = "1.9.1" 475 | requires_python = ">=3.8.0" 476 | summary = "A powerful and modern admin interface / CMS, powered by Piccolo and ASGI." 477 | dependencies = [ 478 | "Hypercorn", 479 | "aiofiles>=0.5.0", 480 | "fastapi>=0.100.0", 481 | "piccolo-api>=1.5.2", 482 | "piccolo>=1.7.0", 483 | "targ>=0.1.9", 484 | "uvicorn", 485 | ] 486 | files = [ 487 | {file = "piccolo_admin-1.9.1-py3-none-any.whl", hash = "sha256:a657d4879b76a78be23854c3a018be080f26f6619221b9ba2022332b1737fcdf"}, 488 | {file = "piccolo_admin-1.9.1.tar.gz", hash = "sha256:7d313ab1d229bfddbf051701039542777e94e9eb680e31e1546feca49f8e8fc3"}, 489 | ] 490 | 491 | [[package]] 492 | name = "piccolo-api" 493 | version = "1.5.2" 494 | requires_python = ">=3.8.0" 495 | summary = "Utilities for using the Piccolo ORM in ASGI apps, plus essential ASGI middleware such as authentication and rate limiting." 496 | dependencies = [ 497 | "Jinja2>=2.11.0", 498 | "PyJWT>=2.0.0", 499 | "fastapi>=0.100.0", 500 | "httpx>=0.20.0", 501 | "piccolo[postgres]>=1.16.0", 502 | "pydantic[email]>=2.0", 503 | "python-multipart>=0.0.5", 504 | ] 505 | files = [ 506 | {file = "piccolo_api-1.5.2-py3-none-any.whl", hash = "sha256:5a45c504239ae65f62987b545ea54712382ea9b676599f6e2042a5d9a896b778"}, 507 | {file = "piccolo_api-1.5.2.tar.gz", hash = "sha256:aac13df3c084e901d016c0bd0c9f6bbf00fa6bdf17f6b12b9f84a24ee3bd45fa"}, 508 | ] 509 | 510 | [[package]] 511 | name = "piccolo" 512 | version = "1.24.2" 513 | extras = ["postgres"] 514 | requires_python = ">=3.9.0" 515 | summary = "A fast, user friendly ORM and query builder which supports asyncio." 516 | dependencies = [ 517 | "asyncpg>=0.30.0", 518 | "piccolo==1.24.2", 519 | ] 520 | files = [ 521 | {file = "piccolo-1.24.2-py3-none-any.whl", hash = "sha256:50118f760196bbff20fcc67d657a8adb99b6a929fa2a4928e46119d12945b63b"}, 522 | {file = "piccolo-1.24.2.tar.gz", hash = "sha256:764ec1ad37f419aecd9df10cd12c0b5d59a37c52dcf9c99fc72682d41f9ba1b8"}, 523 | ] 524 | 525 | [[package]] 526 | name = "platformdirs" 527 | version = "4.3.7" 528 | requires_python = ">=3.9" 529 | summary = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." 530 | files = [ 531 | {file = "platformdirs-4.3.7-py3-none-any.whl", hash = "sha256:a03875334331946f13c549dbd8f4bac7a13a50a895a0eb1e8c6a8ace80d40a94"}, 532 | {file = "platformdirs-4.3.7.tar.gz", hash = "sha256:eb437d586b6a0986388f0d6f74aa0cde27b48d0e3d66843640bfb6bdcdb6e351"}, 533 | ] 534 | 535 | [[package]] 536 | name = "priority" 537 | version = "2.0.0" 538 | requires_python = ">=3.6.1" 539 | summary = "A pure-Python implementation of the HTTP/2 priority tree" 540 | files = [ 541 | {file = "priority-2.0.0-py3-none-any.whl", hash = "sha256:6f8eefce5f3ad59baf2c080a664037bb4725cd0a790d53d59ab4059288faf6aa"}, 542 | {file = "priority-2.0.0.tar.gz", hash = "sha256:c965d54f1b8d0d0b19479db3924c7c36cf672dbf2aec92d43fbdaf4492ba18c0"}, 543 | ] 544 | 545 | [[package]] 546 | name = "pydantic" 547 | version = "2.11.3" 548 | requires_python = ">=3.9" 549 | summary = "Data validation using Python type hints" 550 | dependencies = [ 551 | "annotated-types>=0.6.0", 552 | "pydantic-core==2.33.1", 553 | "typing-extensions>=4.12.2", 554 | "typing-inspection>=0.4.0", 555 | ] 556 | files = [ 557 | {file = "pydantic-2.11.3-py3-none-any.whl", hash = "sha256:a082753436a07f9ba1289c6ffa01cd93db3548776088aa917cc43b63f68fa60f"}, 558 | {file = "pydantic-2.11.3.tar.gz", hash = "sha256:7471657138c16adad9322fe3070c0116dd6c3ad8d649300e3cbdfe91f4db4ec3"}, 559 | ] 560 | 561 | [[package]] 562 | name = "pydantic-core" 563 | version = "2.33.1" 564 | requires_python = ">=3.9" 565 | summary = "Core functionality for Pydantic validation and serialization" 566 | dependencies = [ 567 | "typing-extensions!=4.7.0,>=4.6.0", 568 | ] 569 | files = [ 570 | {file = "pydantic_core-2.33.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3077cfdb6125cc8dab61b155fdd714663e401f0e6883f9632118ec12cf42df26"}, 571 | {file = "pydantic_core-2.33.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8ffab8b2908d152e74862d276cf5017c81a2f3719f14e8e3e8d6b83fda863927"}, 572 | {file = "pydantic_core-2.33.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5183e4f6a2d468787243ebcd70cf4098c247e60d73fb7d68d5bc1e1beaa0c4db"}, 573 | {file = "pydantic_core-2.33.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:398a38d323f37714023be1e0285765f0a27243a8b1506b7b7de87b647b517e48"}, 574 | {file = "pydantic_core-2.33.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:87d3776f0001b43acebfa86f8c64019c043b55cc5a6a2e313d728b5c95b46969"}, 575 | {file = "pydantic_core-2.33.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c566dd9c5f63d22226409553531f89de0cac55397f2ab8d97d6f06cfce6d947e"}, 576 | {file = "pydantic_core-2.33.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0d5f3acc81452c56895e90643a625302bd6be351e7010664151cc55b7b97f89"}, 577 | {file = "pydantic_core-2.33.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d3a07fadec2a13274a8d861d3d37c61e97a816beae717efccaa4b36dfcaadcde"}, 578 | {file = "pydantic_core-2.33.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f99aeda58dce827f76963ee87a0ebe75e648c72ff9ba1174a253f6744f518f65"}, 579 | {file = "pydantic_core-2.33.1-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:902dbc832141aa0ec374f4310f1e4e7febeebc3256f00dc359a9ac3f264a45dc"}, 580 | {file = "pydantic_core-2.33.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fe44d56aa0b00d66640aa84a3cbe80b7a3ccdc6f0b1ca71090696a6d4777c091"}, 581 | {file = "pydantic_core-2.33.1-cp310-cp310-win32.whl", hash = "sha256:ed3eb16d51257c763539bde21e011092f127a2202692afaeaccb50db55a31383"}, 582 | {file = "pydantic_core-2.33.1-cp310-cp310-win_amd64.whl", hash = "sha256:694ad99a7f6718c1a498dc170ca430687a39894a60327f548e02a9c7ee4b6504"}, 583 | {file = "pydantic_core-2.33.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:6e966fc3caaf9f1d96b349b0341c70c8d6573bf1bac7261f7b0ba88f96c56c24"}, 584 | {file = "pydantic_core-2.33.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bfd0adeee563d59c598ceabddf2c92eec77abcb3f4a391b19aa7366170bd9e30"}, 585 | {file = "pydantic_core-2.33.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91815221101ad3c6b507804178a7bb5cb7b2ead9ecd600041669c8d805ebd595"}, 586 | {file = "pydantic_core-2.33.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9fea9c1869bb4742d174a57b4700c6dadea951df8b06de40c2fedb4f02931c2e"}, 587 | {file = "pydantic_core-2.33.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d20eb4861329bb2484c021b9d9a977566ab16d84000a57e28061151c62b349a"}, 588 | {file = "pydantic_core-2.33.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb935c5591573ae3201640579f30128ccc10739b45663f93c06796854405505"}, 589 | {file = "pydantic_core-2.33.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c964fd24e6166420d18fb53996d8c9fd6eac9bf5ae3ec3d03015be4414ce497f"}, 590 | {file = "pydantic_core-2.33.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:681d65e9011f7392db5aa002b7423cc442d6a673c635668c227c6c8d0e5a4f77"}, 591 | {file = "pydantic_core-2.33.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e100c52f7355a48413e2999bfb4e139d2977a904495441b374f3d4fb4a170961"}, 592 | {file = "pydantic_core-2.33.1-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:048831bd363490be79acdd3232f74a0e9951b11b2b4cc058aeb72b22fdc3abe1"}, 593 | {file = "pydantic_core-2.33.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:bdc84017d28459c00db6f918a7272a5190bec3090058334e43a76afb279eac7c"}, 594 | {file = "pydantic_core-2.33.1-cp311-cp311-win32.whl", hash = "sha256:32cd11c5914d1179df70406427097c7dcde19fddf1418c787540f4b730289896"}, 595 | {file = "pydantic_core-2.33.1-cp311-cp311-win_amd64.whl", hash = "sha256:2ea62419ba8c397e7da28a9170a16219d310d2cf4970dbc65c32faf20d828c83"}, 596 | {file = "pydantic_core-2.33.1-cp311-cp311-win_arm64.whl", hash = "sha256:fc903512177361e868bc1f5b80ac8c8a6e05fcdd574a5fb5ffeac5a9982b9e89"}, 597 | {file = "pydantic_core-2.33.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:1293d7febb995e9d3ec3ea09caf1a26214eec45b0f29f6074abb004723fc1de8"}, 598 | {file = "pydantic_core-2.33.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:99b56acd433386c8f20be5c4000786d1e7ca0523c8eefc995d14d79c7a081498"}, 599 | {file = "pydantic_core-2.33.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35a5ec3fa8c2fe6c53e1b2ccc2454398f95d5393ab398478f53e1afbbeb4d939"}, 600 | {file = "pydantic_core-2.33.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b172f7b9d2f3abc0efd12e3386f7e48b576ef309544ac3a63e5e9cdd2e24585d"}, 601 | {file = "pydantic_core-2.33.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9097b9f17f91eea659b9ec58148c0747ec354a42f7389b9d50701610d86f812e"}, 602 | {file = "pydantic_core-2.33.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cc77ec5b7e2118b152b0d886c7514a4653bcb58c6b1d760134a9fab915f777b3"}, 603 | {file = "pydantic_core-2.33.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5e3d15245b08fa4a84cefc6c9222e6f37c98111c8679fbd94aa145f9a0ae23d"}, 604 | {file = "pydantic_core-2.33.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ef99779001d7ac2e2461d8ab55d3373fe7315caefdbecd8ced75304ae5a6fc6b"}, 605 | {file = "pydantic_core-2.33.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:fc6bf8869e193855e8d91d91f6bf59699a5cdfaa47a404e278e776dd7f168b39"}, 606 | {file = "pydantic_core-2.33.1-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:b1caa0bc2741b043db7823843e1bde8aaa58a55a58fda06083b0569f8b45693a"}, 607 | {file = "pydantic_core-2.33.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ec259f62538e8bf364903a7d0d0239447059f9434b284f5536e8402b7dd198db"}, 608 | {file = "pydantic_core-2.33.1-cp312-cp312-win32.whl", hash = "sha256:e14f369c98a7c15772b9da98987f58e2b509a93235582838bd0d1d8c08b68fda"}, 609 | {file = "pydantic_core-2.33.1-cp312-cp312-win_amd64.whl", hash = "sha256:1c607801d85e2e123357b3893f82c97a42856192997b95b4d8325deb1cd0c5f4"}, 610 | {file = "pydantic_core-2.33.1-cp312-cp312-win_arm64.whl", hash = "sha256:8d13f0276806ee722e70a1c93da19748594f19ac4299c7e41237fc791d1861ea"}, 611 | {file = "pydantic_core-2.33.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:70af6a21237b53d1fe7b9325b20e65cbf2f0a848cf77bed492b029139701e66a"}, 612 | {file = "pydantic_core-2.33.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:282b3fe1bbbe5ae35224a0dbd05aed9ccabccd241e8e6b60370484234b456266"}, 613 | {file = "pydantic_core-2.33.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b315e596282bbb5822d0c7ee9d255595bd7506d1cb20c2911a4da0b970187d3"}, 614 | {file = "pydantic_core-2.33.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1dfae24cf9921875ca0ca6a8ecb4bb2f13c855794ed0d468d6abbec6e6dcd44a"}, 615 | {file = "pydantic_core-2.33.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6dd8ecfde08d8bfadaea669e83c63939af76f4cf5538a72597016edfa3fad516"}, 616 | {file = "pydantic_core-2.33.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2f593494876eae852dc98c43c6f260f45abdbfeec9e4324e31a481d948214764"}, 617 | {file = "pydantic_core-2.33.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:948b73114f47fd7016088e5186d13faf5e1b2fe83f5e320e371f035557fd264d"}, 618 | {file = "pydantic_core-2.33.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e11f3864eb516af21b01e25fac915a82e9ddad3bb0fb9e95a246067398b435a4"}, 619 | {file = "pydantic_core-2.33.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:549150be302428b56fdad0c23c2741dcdb5572413776826c965619a25d9c6bde"}, 620 | {file = "pydantic_core-2.33.1-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:495bc156026efafd9ef2d82372bd38afce78ddd82bf28ef5276c469e57c0c83e"}, 621 | {file = "pydantic_core-2.33.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ec79de2a8680b1a67a07490bddf9636d5c2fab609ba8c57597e855fa5fa4dacd"}, 622 | {file = "pydantic_core-2.33.1-cp313-cp313-win32.whl", hash = "sha256:ee12a7be1742f81b8a65b36c6921022301d466b82d80315d215c4c691724986f"}, 623 | {file = "pydantic_core-2.33.1-cp313-cp313-win_amd64.whl", hash = "sha256:ede9b407e39949d2afc46385ce6bd6e11588660c26f80576c11c958e6647bc40"}, 624 | {file = "pydantic_core-2.33.1-cp313-cp313-win_arm64.whl", hash = "sha256:aa687a23d4b7871a00e03ca96a09cad0f28f443690d300500603bd0adba4b523"}, 625 | {file = "pydantic_core-2.33.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:401d7b76e1000d0dd5538e6381d28febdcacb097c8d340dde7d7fc6e13e9f95d"}, 626 | {file = "pydantic_core-2.33.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7aeb055a42d734c0255c9e489ac67e75397d59c6fbe60d155851e9782f276a9c"}, 627 | {file = "pydantic_core-2.33.1-cp313-cp313t-win_amd64.whl", hash = "sha256:338ea9b73e6e109f15ab439e62cb3b78aa752c7fd9536794112e14bee02c8d18"}, 628 | {file = "pydantic_core-2.33.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5c834f54f8f4640fd7e4b193f80eb25a0602bba9e19b3cd2fc7ffe8199f5ae02"}, 629 | {file = "pydantic_core-2.33.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:049e0de24cf23766f12cc5cc71d8abc07d4a9deb9061b334b62093dedc7cb068"}, 630 | {file = "pydantic_core-2.33.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a28239037b3d6f16916a4c831a5a0eadf856bdd6d2e92c10a0da3a59eadcf3e"}, 631 | {file = "pydantic_core-2.33.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d3da303ab5f378a268fa7d45f37d7d85c3ec19769f28d2cc0c61826a8de21fe"}, 632 | {file = "pydantic_core-2.33.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:25626fb37b3c543818c14821afe0fd3830bc327a43953bc88db924b68c5723f1"}, 633 | {file = "pydantic_core-2.33.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:3ab2d36e20fbfcce8f02d73c33a8a7362980cff717926bbae030b93ae46b56c7"}, 634 | {file = "pydantic_core-2.33.1-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:2f9284e11c751b003fd4215ad92d325d92c9cb19ee6729ebd87e3250072cdcde"}, 635 | {file = "pydantic_core-2.33.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:048c01eee07d37cbd066fc512b9d8b5ea88ceeb4e629ab94b3e56965ad655add"}, 636 | {file = "pydantic_core-2.33.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5ccd429694cf26af7997595d627dd2637e7932214486f55b8a357edaac9dae8c"}, 637 | {file = "pydantic_core-2.33.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3a371dc00282c4b84246509a5ddc808e61b9864aa1eae9ecc92bb1268b82db4a"}, 638 | {file = "pydantic_core-2.33.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:f59295ecc75a1788af8ba92f2e8c6eeaa5a94c22fc4d151e8d9638814f85c8fc"}, 639 | {file = "pydantic_core-2.33.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08530b8ac922003033f399128505f513e30ca770527cc8bbacf75a84fcc2c74b"}, 640 | {file = "pydantic_core-2.33.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bae370459da6a5466978c0eacf90690cb57ec9d533f8e63e564ef3822bfa04fe"}, 641 | {file = "pydantic_core-2.33.1-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e3de2777e3b9f4d603112f78006f4ae0acb936e95f06da6cb1a45fbad6bdb4b5"}, 642 | {file = "pydantic_core-2.33.1-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:3a64e81e8cba118e108d7126362ea30e021291b7805d47e4896e52c791be2761"}, 643 | {file = "pydantic_core-2.33.1-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:52928d8c1b6bda03cc6d811e8923dffc87a2d3c8b3bfd2ce16471c7147a24850"}, 644 | {file = "pydantic_core-2.33.1-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:1b30d92c9412beb5ac6b10a3eb7ef92ccb14e3f2a8d7732e2d739f58b3aa7544"}, 645 | {file = "pydantic_core-2.33.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:f995719707e0e29f0f41a8aa3bcea6e761a36c9136104d3189eafb83f5cec5e5"}, 646 | {file = "pydantic_core-2.33.1.tar.gz", hash = "sha256:bcc9c6fdb0ced789245b02b7d6603e17d1563064ddcfc36f046b61c0c05dd9df"}, 647 | ] 648 | 649 | [[package]] 650 | name = "pydantic" 651 | version = "2.11.3" 652 | extras = ["email"] 653 | requires_python = ">=3.9" 654 | summary = "Data validation using Python type hints" 655 | dependencies = [ 656 | "email-validator>=2.0.0", 657 | "pydantic==2.11.3", 658 | ] 659 | files = [ 660 | {file = "pydantic-2.11.3-py3-none-any.whl", hash = "sha256:a082753436a07f9ba1289c6ffa01cd93db3548776088aa917cc43b63f68fa60f"}, 661 | {file = "pydantic-2.11.3.tar.gz", hash = "sha256:7471657138c16adad9322fe3070c0116dd6c3ad8d649300e3cbdfe91f4db4ec3"}, 662 | ] 663 | 664 | [[package]] 665 | name = "pyjwt" 666 | version = "2.10.1" 667 | requires_python = ">=3.9" 668 | summary = "JSON Web Token implementation in Python" 669 | files = [ 670 | {file = "PyJWT-2.10.1-py3-none-any.whl", hash = "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb"}, 671 | {file = "pyjwt-2.10.1.tar.gz", hash = "sha256:3cc5772eb20009233caf06e9d8a0577824723b44e6648ee0a2aedb6cf9381953"}, 672 | ] 673 | 674 | [[package]] 675 | name = "python-multipart" 676 | version = "0.0.20" 677 | requires_python = ">=3.8" 678 | summary = "A streaming multipart parser for Python" 679 | files = [ 680 | {file = "python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104"}, 681 | {file = "python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13"}, 682 | ] 683 | 684 | [[package]] 685 | name = "sniffio" 686 | version = "1.3.1" 687 | requires_python = ">=3.7" 688 | summary = "Sniff out which async library your code is running under" 689 | files = [ 690 | {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, 691 | {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, 692 | ] 693 | 694 | [[package]] 695 | name = "starlette" 696 | version = "0.46.2" 697 | requires_python = ">=3.9" 698 | summary = "The little ASGI library that shines." 699 | dependencies = [ 700 | "anyio<5,>=3.6.2", 701 | "typing-extensions>=3.10.0; python_version < \"3.10\"", 702 | ] 703 | files = [ 704 | {file = "starlette-0.46.2-py3-none-any.whl", hash = "sha256:595633ce89f8ffa71a015caed34a5b2dc1c0cdb3f0f1fbd1e69339cf2abeec35"}, 705 | {file = "starlette-0.46.2.tar.gz", hash = "sha256:7f7361f34eed179294600af672f565727419830b54b7b084efe44bb82d2fccd5"}, 706 | ] 707 | 708 | [[package]] 709 | name = "targ" 710 | version = "0.4.0" 711 | requires_python = ">=3.8.0" 712 | summary = "Build a Python CLI for your app, just using type hints and docstrings." 713 | dependencies = [ 714 | "colorama==0.4.*", 715 | "docstring-parser>=0.12", 716 | "typing-inspect>=0.6.0; python_version < \"3.8\"", 717 | ] 718 | files = [ 719 | {file = "targ-0.4.0-py3-none-any.whl", hash = "sha256:5237524323661ffa899158d668468b5c94bb84e2d988bd216981932844da63eb"}, 720 | {file = "targ-0.4.0.tar.gz", hash = "sha256:dcdb57945bffe5bc59570d2e41bb1adc6280c5460332c5daf300729bc88d1aba"}, 721 | ] 722 | 723 | [[package]] 724 | name = "taskgroup" 725 | version = "0.2.2" 726 | summary = "backport of asyncio.TaskGroup, asyncio.Runner and asyncio.timeout" 727 | dependencies = [ 728 | "exceptiongroup", 729 | "typing-extensions<5,>=4.12.2", 730 | ] 731 | files = [ 732 | {file = "taskgroup-0.2.2-py2.py3-none-any.whl", hash = "sha256:e2c53121609f4ae97303e9ea1524304b4de6faf9eb2c9280c7f87976479a52fb"}, 733 | {file = "taskgroup-0.2.2.tar.gz", hash = "sha256:078483ac3e78f2e3f973e2edbf6941374fbea81b9c5d0a96f51d297717f4752d"}, 734 | ] 735 | 736 | [[package]] 737 | name = "tomli" 738 | version = "2.2.1" 739 | requires_python = ">=3.8" 740 | summary = "A lil' TOML parser" 741 | files = [ 742 | {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, 743 | {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, 744 | {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"}, 745 | {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"}, 746 | {file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"}, 747 | {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"}, 748 | {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"}, 749 | {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"}, 750 | {file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"}, 751 | {file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"}, 752 | {file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"}, 753 | {file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"}, 754 | {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"}, 755 | {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"}, 756 | {file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"}, 757 | {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"}, 758 | {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"}, 759 | {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"}, 760 | {file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"}, 761 | {file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"}, 762 | {file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"}, 763 | {file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"}, 764 | {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"}, 765 | {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"}, 766 | {file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"}, 767 | {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"}, 768 | {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"}, 769 | {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"}, 770 | {file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"}, 771 | {file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"}, 772 | {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"}, 773 | {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, 774 | ] 775 | 776 | [[package]] 777 | name = "typing-extensions" 778 | version = "4.13.2" 779 | requires_python = ">=3.8" 780 | summary = "Backported and Experimental Type Hints for Python 3.8+" 781 | files = [ 782 | {file = "typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c"}, 783 | {file = "typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef"}, 784 | ] 785 | 786 | [[package]] 787 | name = "typing-inspection" 788 | version = "0.4.0" 789 | requires_python = ">=3.9" 790 | summary = "Runtime typing introspection tools" 791 | dependencies = [ 792 | "typing-extensions>=4.12.0", 793 | ] 794 | files = [ 795 | {file = "typing_inspection-0.4.0-py3-none-any.whl", hash = "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f"}, 796 | {file = "typing_inspection-0.4.0.tar.gz", hash = "sha256:9765c87de36671694a67904bf2c96e395be9c6439bb6c87b5142569dcdd65122"}, 797 | ] 798 | 799 | [[package]] 800 | name = "uvicorn" 801 | version = "0.34.2" 802 | requires_python = ">=3.9" 803 | summary = "The lightning-fast ASGI server." 804 | dependencies = [ 805 | "click>=7.0", 806 | "h11>=0.8", 807 | "typing-extensions>=4.0; python_version < \"3.11\"", 808 | ] 809 | files = [ 810 | {file = "uvicorn-0.34.2-py3-none-any.whl", hash = "sha256:deb49af569084536d269fe0a6d67e3754f104cf03aba7c11c40f01aadf33c403"}, 811 | {file = "uvicorn-0.34.2.tar.gz", hash = "sha256:0e929828f6186353a80b58ea719861d2629d766293b6d19baf086ba31d4f3328"}, 812 | ] 813 | 814 | [[package]] 815 | name = "wsproto" 816 | version = "1.2.0" 817 | requires_python = ">=3.7.0" 818 | summary = "WebSockets state-machine based protocol implementation" 819 | dependencies = [ 820 | "h11<1,>=0.9.0", 821 | ] 822 | files = [ 823 | {file = "wsproto-1.2.0-py3-none-any.whl", hash = "sha256:b9acddd652b585d75b20477888c56642fdade28bdfd3579aa24a4d2c037dd736"}, 824 | {file = "wsproto-1.2.0.tar.gz", hash = "sha256:ad565f26ecb92588a3e43bc3d96164de84cd9902482b130d0ddbaa9664a85065"}, 825 | ] 826 | -------------------------------------------------------------------------------- /piccolo_conf.py: -------------------------------------------------------------------------------- 1 | from piccolo.conf.apps import AppRegistry 2 | from piccolo.engine.postgres import PostgresEngine 3 | 4 | from settings import DB_HOST, DB_NAME, DB_PASSWORD, DB_PORT, DB_USER 5 | 6 | config = { 7 | "database": DB_NAME, 8 | "user": DB_USER, 9 | "password": DB_PASSWORD, 10 | "host": DB_HOST, 11 | "port": DB_PORT, 12 | } 13 | DB = PostgresEngine(config) 14 | 15 | APP_REGISTRY = AppRegistry( 16 | apps=[ 17 | "home.piccolo_app", 18 | "piccolo_admin.piccolo_app", 19 | "accounts.piccolo_app", 20 | ] 21 | ) 22 | -------------------------------------------------------------------------------- /piccolo_conf_test.py: -------------------------------------------------------------------------------- 1 | from piccolo_conf import * # noqa 2 | 3 | from settings import ( 4 | DB_TEST_NAME, 5 | DB_TEST_USER, 6 | DB_TEST_PASSWORD, 7 | DB_TEST_HOST, 8 | DB_TEST_PORT, 9 | ) 10 | 11 | config = { 12 | "database": DB_TEST_NAME, 13 | "user": DB_TEST_USER, 14 | "password": DB_TEST_PASSWORD, 15 | "host": DB_TEST_HOST, 16 | "port": DB_TEST_PORT, 17 | } 18 | DB = PostgresEngine(config) 19 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "FastAPi-Piccolo-Template" 3 | version = "0.1.0" 4 | description = "" 5 | authors = [ 6 | { name = "ali", email = "ali.sayyah2@gmail.com" }, 7 | ] 8 | dependencies = [ 9 | "asyncpg>=0.26.0", 10 | "black>=22.6.0", 11 | "fastapi>=0.79.0", 12 | "piccolo>=0.82.0", 13 | "piccolo-admin>=0.28.0", 14 | "piccolo-api>=0.40.0", 15 | "uvicorn>=0.18.2", 16 | ] 17 | requires-python = ">=3.10" 18 | license = { text = "MIT" } 19 | 20 | [build-system] 21 | requires = ["pdm-pep517>=1.0.0"] 22 | build-backend = "pdm.pep517.api" 23 | 24 | 25 | [tool] 26 | [tool.pdm] 27 | 28 | [tool.pdm.scripts] 29 | test = "piccolo tester run" -------------------------------------------------------------------------------- /sample.env: -------------------------------------------------------------------------------- 1 | # main database 2 | DB_USER= 3 | DB_PASSWORD= 4 | DB_NAME= 5 | DB_HOST= 6 | DB_PORT=5432 7 | 8 | # test database 9 | DB_TEST_USER= 10 | DB_TEST_PASSWORD= 11 | DB_TEST_NAME= 12 | 13 | # Password hashing secret key 14 | SECRET_KEY= 15 | 16 | # pgadmin 17 | PGADMIN_DEFAULT_EMAIL= 18 | PGADMIN_DEFAULT_PASSWORD= -------------------------------------------------------------------------------- /settings.py: -------------------------------------------------------------------------------- 1 | from starlette.config import Config 2 | 3 | 4 | def generate_secret_key(): 5 | import secrets 6 | 7 | return secrets.token_urlsafe(32) 8 | 9 | 10 | # Configuration from environment variables or '.env' file. 11 | config = Config(".env") 12 | 13 | # main database 14 | DB_NAME = config("DB_NAME", default="postgres") 15 | DB_USER = config("DB_USER", default="postgres") 16 | DB_PASSWORD = config("DB_PASSWORD", default="postgres") 17 | DB_HOST = config("DB_HOST", default="db") 18 | DB_PORT = config("DB_PORT", default=5432) 19 | 20 | # test database 21 | DB_TEST_USER = config("DB_TEST_USER", default="postgres") 22 | DB_TEST_PASSWORD = config("DB_TEST_PASSWORD", default="postgres") 23 | DB_TEST_NAME = config("DB_TEST_NAME", default="test") 24 | DB_TEST_HOST = config("DB_TEST_HOST", default=DB_HOST) 25 | DB_TEST_PORT = config("DB_TEST_PORT", default=DB_PORT) 26 | 27 | SECRET_KEY = config("SECRET_KEY", default=generate_secret_key()) 28 | RELOAD = config("RELOAD", default=True) 29 | ALGORITHM = "HS256" 30 | ACCESS_TOKEN_EXPIRE_MINUTES = 30 31 | ORIGINS = [ 32 | "http://localhost:8000", 33 | ] 34 | -------------------------------------------------------------------------------- /static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AliSayyah/FastAPI-Piccolo-Template/426399074fe3db98237fc30b18586fb677a1ab41/static/favicon.ico -------------------------------------------------------------------------------- /static/main.css: -------------------------------------------------------------------------------- 1 | body, html { 2 | padding: 0; 3 | margin: 0; 4 | } 5 | 6 | body { 7 | background-color: #f0f7fd; 8 | color: #2b475f; 9 | font-family: 'Open Sans', sans-serif; 10 | } 11 | 12 | div.hero { 13 | background-color: #4C89C8; 14 | box-sizing: border-box; 15 | padding: 5rem; 16 | } 17 | 18 | a { 19 | color: #4C89C8; 20 | text-decoration: none; 21 | } 22 | 23 | div.hero h1 { 24 | color: white; 25 | font-weight: normal; 26 | text-align: center; 27 | } 28 | 29 | section { 30 | padding-bottom: 2rem; 31 | } 32 | 33 | div.content { 34 | background-color: white; 35 | border-radius: 0.5rem; 36 | box-sizing: border-box; 37 | margin: 1rem auto; 38 | max-width: 50rem; 39 | padding: 2rem; 40 | transform: translateY(-4rem); 41 | box-shadow: 0px 1px 1px 1px rgb(0,0,0,0.05); 42 | } 43 | 44 | div.content h2, div.content h3 { 45 | font-weight: normal; 46 | } 47 | 48 | div.content code { 49 | padding: 2px 4px; 50 | background-color: #f0f7fd; 51 | border-radius: 0.2rem; 52 | } 53 | 54 | p.code { 55 | background-color: #233d58; 56 | color: white; 57 | font-family: monospace; 58 | padding: 1rem; 59 | margin: 0; 60 | display: block; 61 | border-radius: 0.2rem; 62 | } 63 | 64 | p.code span { 65 | display: block; 66 | padding: 0.5rem; 67 | } --------------------------------------------------------------------------------