Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add FastAPI example and functionalities guidelines #1

Merged
merged 1 commit into from
Oct 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions examples/fastAPI/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
SHELL := sh
.ONESHELL:
.SHELLFLAGS := -eu -c
.DELETE_ON_ERROR:

SOURCES_PATH := src

# load environment config from .env if able
-include .env

ifndef PYTHON_ALIAS
PYTHON_ALIAS := python
endif

ifndef INSTALL_OPTIONS
INSTALL_OPTIONS := .[dev]
endif

ifndef UV_VERSION
UV_VERSION := 0.4.22
endif

.PHONY: venv sync lock update format lint test run

# Setup virtual environment for local development.
venv:
@echo '# Preparing development environment...'
@echo '...installing uv...'
@curl -LsSf https://github.com/astral-sh/uv/releases/download/$(UV_VERSION)/uv-installer.sh | sh
@echo '...preparing venv...'
@$(PYTHON_ALIAS) -m venv .venv --prompt="VENV[DEV]" --clear --upgrade-deps
@. ./.venv/bin/activate && pip install --upgrade pip && uv pip install --editable $(INSTALL_OPTIONS) --constraint constraints
@echo '...development environment ready! Activate venv using `. ./.venv/bin/activate`.'

# Sync environment with uv based on constraints
sync:
@echo '# Synchronizing dependencies...'
@$(if $(findstring $(UV_VERSION), $(shell uv --version | head -n1 | cut -d" " -f2)), , @echo '...updating uv...' && curl -LsSf https://github.com/astral-sh/uv/releases/download/$(UV_VERSION)/uv-installer.sh | sh)
@uv pip install --editable $(INSTALL_OPTIONS) --constraint constraints
@echo '...finished!'

# Generate a set of locked dependencies from pyproject.toml
lock:
@echo '# Locking dependencies...'
@uv pip compile pyproject.toml -o constraints --all-extras
@echo '...finished!'

# Update and lock dependencies from pyproject.toml
update:
@echo '# Updating dependencies...'
@$(if $(shell printf '%s\n%s\n' "$(UV_VERSION)" "$$(uv --version | head -n1 | cut -d' ' -f2)" | sort -V | head -n1 | grep -q "$(UV_VERSION)"), , @echo '...updating uv...' && curl -LsSf https://github.com/astral-sh/uv/releases/download/$(UV_VERSION)/uv-installer.sh | sh)
# @$(if $(findstring $(UV_VERSION), $(shell uv --version | head -n1 | cut -d" " -f2)), , @echo '...updating uv...' && curl -LsSf https://github.com/astral-sh/uv/releases/download/$(UV_VERSION)/uv-installer.sh | sh)
@uv --no-cache pip compile pyproject.toml -o constraints --all-extras --upgrade
@uv pip install --editable $(INSTALL_OPTIONS) --constraint constraints
@echo '...finished!'

# Run formatter.
format:
@ruff check --quiet --fix $(SOURCES_PATH)
@ruff format --quiet $(SOURCES_PATH)

# Run linters and code checks.
lint:
@bandit -r $(SOURCES_PATH)
@ruff check $(SOURCES_PATH)
@pyright --project ./

# Run the server
run:
@python -m server
1 change: 1 addition & 0 deletions examples/fastAPI/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
## Haiway FastAPI Example
72 changes: 72 additions & 0 deletions examples/fastAPI/constraints
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# This file was autogenerated by uv via the following command:
# uv --no-cache pip compile pyproject.toml -o constraints --all-extras
annotated-types==0.7.0
# via pydantic
anyio==4.6.2.post1
# via
# httpx
# starlette
asyncpg==0.29.0
# via haiway-fastapi (pyproject.toml)
bandit==1.7.10
# via haiway-fastapi (pyproject.toml)
certifi==2024.8.30
# via
# httpcore
# httpx
click==8.1.7
# via uvicorn
fastapi-slim==0.115.2
# via haiway-fastapi (pyproject.toml)
h11==0.14.0
# via
# httpcore
# uvicorn
haiway==0.1.0
# via haiway-fastapi (pyproject.toml)
httpcore==1.0.6
# via httpx
httpx==0.25.2
# via haiway-fastapi (pyproject.toml)
idna==3.10
# via
# anyio
# httpx
markdown-it-py==3.0.0
# via rich
mdurl==0.1.2
# via markdown-it-py
nodeenv==1.9.1
# via pyright
pbr==6.1.0
# via stevedore
pydantic==2.9.2
# via fastapi-slim
pydantic-core==2.23.4
# via pydantic
pygments==2.18.0
# via rich
pyright==1.1.385
# via haiway-fastapi (pyproject.toml)
pyyaml==6.0.2
# via bandit
rich==13.9.2
# via bandit
ruff==0.5.7
# via haiway-fastapi (pyproject.toml)
sniffio==1.3.1
# via
# anyio
# httpx
starlette==0.40.0
# via fastapi-slim
stevedore==5.3.0
# via bandit
typing-extensions==4.12.2
# via
# fastapi-slim
# pydantic
# pydantic-core
# pyright
uvicorn==0.32.0
# via haiway-fastapi (pyproject.toml)
61 changes: 61 additions & 0 deletions examples/fastAPI/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"

[project]
name = "haiway_fastapi"
description = "Example of haiway usage with fastapi."
version = "0.1.0"
readme = "README.md"
maintainers = [
{ name = "Kacper Kaliński", email = "[email protected]" },
]
requires-python = ">=3.12"
dependencies = [
"haiway~=0.1.0",
"fastapi-slim~=0.115",
"asyncpg~=0.29",
"httpx~=0.25.0",
]

[project.urls]
Homepage = "https://miquido.com"

[project.optional-dependencies]
dev = [
"uvicorn~=0.30",
"ruff~=0.5.0",
"pyright~=1.1",
"bandit~=1.7",
]

[tool.ruff]
target-version = "py312"
line-length = 100
extend-exclude = [".venv", ".git", ".cache"]
lint.select = ["E", "F", "A", "I", "B", "PL", "W", "C", "RUF", "UP"]
lint.ignore = []

[tool.ruff.lint.per-file-ignores]
"__init__.py" = ["F401", "E402"]
"./tests/*.py" = ["PLR2004"]

[tool.pyright]
pythonVersion = "3.12"
venvPath = "."
venv = ".venv"
include = ["./src"]
exclude = ["**/node_modules", "**/__pycache__"]
ignore = []
stubPath = "./stubs"
reportMissingImports = true
reportMissingTypeStubs = true
typeCheckingMode = "strict"
userFileIndexingLimit = -1
useLibraryCodeForTypes = true

[tool.setuptools]
include-package-data = true

[tool.setuptools.packages.find]
where = ["src"]
Empty file.
5 changes: 5 additions & 0 deletions examples/fastAPI/src/features/todos/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from features.todos.calls import complete_todo

__all__ = [
"complete_todo",
]
15 changes: 15 additions & 0 deletions examples/fastAPI/src/features/todos/calls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from uuid import UUID

from features.todos.state import Todos
from haiway import ctx

__all__ = [
"complete_todo",
]


async def complete_todo(
*,
identifier: UUID,
) -> None:
await ctx.state(Todos).complete(identifier=identifier)
1 change: 1 addition & 0 deletions examples/fastAPI/src/features/todos/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# empty
11 changes: 11 additions & 0 deletions examples/fastAPI/src/features/todos/state.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from features.todos.types import TodoCompletion
from features.todos.user_tasks import complete_todo_task
from haiway import Structure

__all__ = [
"Todos",
]


class Todos(Structure):
complete: TodoCompletion = complete_todo_task
15 changes: 15 additions & 0 deletions examples/fastAPI/src/features/todos/types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from typing import Protocol, runtime_checkable
from uuid import UUID

__all__ = [
"TodoCompletion",
]


@runtime_checkable
class TodoCompletion(Protocol):
async def __call__(
self,
*,
identifier: UUID,
) -> None: ...
16 changes: 16 additions & 0 deletions examples/fastAPI/src/features/todos/user_tasks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from uuid import UUID

from haiway import ctx
from solutions.user_tasks import UserTask, UserTasks

__all__ = [
"complete_todo_task",
]


async def complete_todo_task(
*,
identifier: UUID,
) -> None:
task: UserTask = await ctx.state(UserTasks).fetch(identifier=identifier)
await ctx.state(UserTasks).update(task=task.updated(completed=True))
Empty file.
7 changes: 7 additions & 0 deletions examples/fastAPI/src/integrations/postgres/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from integrations.postgres.client import PostgresClient
from integrations.postgres.types import PostgresClientException

__all__ = [
"PostgresClient",
"PostgresClientException",
]
35 changes: 35 additions & 0 deletions examples/fastAPI/src/integrations/postgres/client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from typing import Self

from haiway import Dependency

from integrations.postgres.config import (
POSTGRES_DATABASE,
POSTGRES_HOST,
POSTGRES_PASSWORD,
POSTGRES_PORT,
POSTGRES_SSLMODE,
POSTGRES_USER,
)
from integrations.postgres.session import PostgresClientSession

__all__ = [
"PostgresClient",
]


class PostgresClient(
PostgresClientSession,
Dependency,
):
@classmethod
async def prepare(cls) -> Self:
instance: Self = cls(
host=POSTGRES_HOST,
port=POSTGRES_PORT,
database=POSTGRES_DATABASE,
user=POSTGRES_USER,
password=POSTGRES_PASSWORD,
ssl=POSTGRES_SSLMODE,
)
await instance.initialize()
return instance
19 changes: 19 additions & 0 deletions examples/fastAPI/src/integrations/postgres/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from typing import Final

from haiway import getenv_str

__all__ = [
"POSTGRES_DATABASE",
"POSTGRES_HOST",
"POSTGRES_PASSWORD",
"POSTGRES_PORT",
"POSTGRES_SSLMODE",
"POSTGRES_USER",
]

POSTGRES_DATABASE: Final[str] = getenv_str("POSTGRES_DATABASE", default="postgres")
POSTGRES_HOST: Final[str] = getenv_str("POSTGRES_HOST", default="localhost")
POSTGRES_PORT: Final[str] = getenv_str("POSTGRES_PORT", default="5432")
POSTGRES_USER: Final[str] = getenv_str("POSTGRES_USER", default="postgres")
POSTGRES_PASSWORD: Final[str] = getenv_str("POSTGRES_PASSWORD", default="postgres")
POSTGRES_SSLMODE: Final[str] = getenv_str("POSTGRES_SSLMODE", default="prefer")
Loading
Loading