[project] name = "backend" dynamic = ["version"] description = "decentralized music streaming platform built on ATProto" authors = [{ name = "zzstoatzz", email = "thrast36@gmail.com" }] dependencies = [ "fastapi>=0.115.0", "uvicorn[standard]>=0.34.0", "httpx>=0.28.0", "pydantic>=2.11.0", "pydantic-settings>=2.7.0", "python-dotenv>=1.1.0", "sqlalchemy>=2.0.36", "alembic>=1.14.0", "asyncpg>=0.30.0", "atproto @ git+https://github.com/zzstoatzz/atproto@main", "boto3>=1.37.0", "python-multipart>=0.0.20", "python-jose[cryptography]>=3.3.0", "passlib[bcrypt]>=1.7.4", "psycopg[binary]>=3.2.12", "greenlet>=3.2.4", "logfire[fastapi,sqlalchemy]>=4.14.2", "cachetools>=6.2.1", "pytest-asyncio>=0.25.3", "aioboto3>=15.5.0", "slowapi @ git+https://github.com/zzstoatzz/slowapi.git@fix-deprecation", "orjson>=3.11.4", "mutagen>=1.47.0", "pydocket>=0.15.2", "redis>=7.1.0", "beartype>=0.22.8", ] requires-python = ">=3.11" readme = "README.md" license = "Apache-2.0" keywords = ["music", "streaming", "atproto", "bluesky", "decentralized"] [dependency-groups] dev = [ "dirty-equals>=0.9.0", "ipython>=8.12.3", "pdbpp>=0.10.3", "prek>=0.2.13", "pytest>=8.3.3", "pytest-asyncio>=1.0.0", "pytest-cov>=6.1.1", "pytest-env>=1.1.5", "pytest-timeout>=2.4.0", "pytest-xdist>=3.6.1", "ruff>=0.12.0", "httpx>=0.28.0", "ty>=0.0.1a25", ] [build-system] requires = ["hatchling", "uv-dynamic-versioning>=0.7.0"] build-backend = "hatchling.build" [tool.hatch.version] source = "uv-dynamic-versioning" [tool.hatch.metadata] allow-direct-references = true [tool.uv-dynamic-versioning] vcs = "git" style = "pep440" bump = true fallback-version = "0.0.0" [tool.pytest.ini_options] asyncio_mode = "auto" asyncio_default_fixture_loop_scope = "function" timeout = 10 env = [ "RELAY_TEST_MODE=1", "OAUTH_ENCRYPTION_KEY=hnSkDmgbbuK0rt7Ab3eJHAktb18gmebsdwKdTmq9mes=", "LOGFIRE_IGNORE_NO_CONFIG=1", # reduce connection pool for tests to avoid exhausting Neon's connection limit "DATABASE_POOL_SIZE=2", "DATABASE_MAX_OVERFLOW=0", # redis URL for cache tests (uses test-redis from docker-compose) # D: prefix means don't override if already set (e.g., by CI workflow) "D:DOCKET_URL=redis://localhost:6380/0", # disable automatic perpetual task scheduling in tests to avoid event loop issues "DOCKET_SCHEDULE_AUTOMATIC_TASKS=false", ] markers = [ "integration: marks tests as integration tests (deselect with '-m \"not integration\"')", ] pythonpath = ["."] testpaths = ["tests"] python_files = ["test_*.py", "*_test.py"] python_classes = ["Test*"] python_functions = ["test_*"] filterwarnings = [ "ignore::pydantic.warnings.UnsupportedFieldAttributeWarning", ] [tool.ruff.lint] fixable = ["ALL"] ignore = [ "COM812", "PLR0913", # Too many arguments "SIM102", # Dont require combining if statements ] extend-select = [ "B", # flake8-bugbear "C4", # flake8-comprehensions "I", # isort "PIE", # flake8-pie "RUF", # Ruff-specific "SIM", # flake8-simplify "UP", # pyupgrade ] [tool.ruff.lint.per-file-ignores] "__init__.py" = ["F401", "I001"] "tests/**/*.py" = ["S101"] # Allow assert in tests "src/backend/api/**/*.py" = [ "B008", ] # Allow Depends() and File() in FastAPI route defaults "src/backend/main.py" = ["E402"] # Need warnings filter before imports [tool.coverage.run] source = ["src"] omit = ["tests/*", "sandbox/*"] [tool.coverage.report] exclude_lines = [ "pragma: no cover", "def __repr__", "raise AssertionError", "raise NotImplementedError", "if __name__ == .__main__.:", "if TYPE_CHECKING:", ] [tool.ty.src] include = ["src", "tests"] exclude = [ "**/node_modules", "**/__pycache__", ".venv", ".git", "dist", "frontend", ] [tool.ty.environment] python-version = "3.11" [tool.ty.rules] # start with basic checks, can tighten later unknown-argument = "ignore" no-matching-overload = "ignore"