build: add ruff and format code
This commit is contained in:
		
							parent
							
								
									afadffbaac
								
							
						
					
					
						commit
						9d24f79436
					
				
					 20 changed files with 122 additions and 60 deletions
				
			
		|  | @ -1,10 +1,8 @@ | |||
| from logging.config import fileConfig | ||||
| 
 | ||||
| from sqlalchemy import engine_from_config | ||||
| from sqlalchemy import pool | ||||
| from sqlalchemy import engine_from_config, pool | ||||
| 
 | ||||
| from alembic import context | ||||
| 
 | ||||
| from foxnouns.db import Base | ||||
| from foxnouns.db.sync import SYNC_DATABASE_URL | ||||
| 
 | ||||
|  | @ -70,9 +68,7 @@ def run_migrations_online() -> None: | |||
|     ) | ||||
| 
 | ||||
|     with connectable.connect() as connection: | ||||
|         context.configure( | ||||
|             connection=connection, target_metadata=target_metadata | ||||
|         ) | ||||
|         context.configure(connection=connection, target_metadata=target_metadata) | ||||
| 
 | ||||
|         with context.begin_transaction(): | ||||
|             context.run_migrations() | ||||
|  |  | |||
|  | @ -5,11 +5,12 @@ Revises: | |||
| Create Date: 2024-03-09 16:32:28.590145 | ||||
| 
 | ||||
| """ | ||||
| 
 | ||||
| from typing import Sequence, Union | ||||
| 
 | ||||
| from alembic import op | ||||
| import sqlalchemy as sa | ||||
| 
 | ||||
| from alembic import op | ||||
| 
 | ||||
| # revision identifiers, used by Alembic. | ||||
| revision: str = "b39613fd7327" | ||||
|  |  | |||
|  | @ -5,12 +5,14 @@ Revises: b39613fd7327 | |||
| Create Date: 2024-03-13 17:01:50.434602 | ||||
| 
 | ||||
| """ | ||||
| 
 | ||||
| from typing import Sequence, Union | ||||
| 
 | ||||
| from alembic import op | ||||
| import sqlalchemy as sa | ||||
| from sqlalchemy.dialects import postgresql | ||||
| 
 | ||||
| from alembic import op | ||||
| 
 | ||||
| # revision identifiers, used by Alembic. | ||||
| revision: str = "0b63f7c8ab96" | ||||
| down_revision: Union[str, None] = "b39613fd7327" | ||||
|  |  | |||
|  | @ -5,12 +5,14 @@ Revises: 0b63f7c8ab96 | |||
| Create Date: 2024-03-20 15:36:08.756635 | ||||
| 
 | ||||
| """ | ||||
| 
 | ||||
| from typing import Sequence, Union | ||||
| 
 | ||||
| from alembic import op | ||||
| import sqlalchemy as sa | ||||
| from sqlalchemy.dialects import postgresql | ||||
| 
 | ||||
| from alembic import op | ||||
| 
 | ||||
| # revision identifiers, used by Alembic. | ||||
| revision: str = "1d8f8443a7f5" | ||||
| down_revision: Union[str, None] = "0b63f7c8ab96" | ||||
|  |  | |||
|  | @ -5,37 +5,43 @@ Revises: 1d8f8443a7f5 | |||
| Create Date: 2024-03-20 16:00:59.251354 | ||||
| 
 | ||||
| """ | ||||
| 
 | ||||
| from typing import Sequence, Union | ||||
| 
 | ||||
| from alembic import op | ||||
| import sqlalchemy as sa | ||||
| from sqlalchemy.dialects import postgresql | ||||
| 
 | ||||
| from alembic import op | ||||
| 
 | ||||
| # revision identifiers, used by Alembic. | ||||
| revision: str = '17cc8cb77be5' | ||||
| down_revision: Union[str, None] = '1d8f8443a7f5' | ||||
| revision: str = "17cc8cb77be5" | ||||
| down_revision: Union[str, None] = "1d8f8443a7f5" | ||||
| branch_labels: Union[str, Sequence[str], None] = None | ||||
| depends_on: Union[str, Sequence[str], None] = None | ||||
| 
 | ||||
| 
 | ||||
| def upgrade() -> None: | ||||
|     # ### commands auto generated by Alembic - please adjust! ### | ||||
|     op.create_table('members', | ||||
|     sa.Column('id', sa.BigInteger(), nullable=False), | ||||
|     sa.Column('name', sa.Text(), nullable=False), | ||||
|     sa.Column('display_name', sa.Text(), nullable=True), | ||||
|     sa.Column('bio', sa.Text(), nullable=True), | ||||
|     sa.Column('names', postgresql.JSONB(astext_type=sa.Text()), nullable=False), | ||||
|     sa.Column('pronouns', postgresql.JSONB(astext_type=sa.Text()), nullable=False), | ||||
|     sa.Column('fields', postgresql.JSONB(astext_type=sa.Text()), nullable=False), | ||||
|     sa.Column('user_id', sa.BigInteger(), nullable=False), | ||||
|     sa.ForeignKeyConstraint(['user_id'], ['users.id'], ), | ||||
|     sa.PrimaryKeyConstraint('id') | ||||
|     op.create_table( | ||||
|         "members", | ||||
|         sa.Column("id", sa.BigInteger(), nullable=False), | ||||
|         sa.Column("name", sa.Text(), nullable=False), | ||||
|         sa.Column("display_name", sa.Text(), nullable=True), | ||||
|         sa.Column("bio", sa.Text(), nullable=True), | ||||
|         sa.Column("names", postgresql.JSONB(astext_type=sa.Text()), nullable=False), | ||||
|         sa.Column("pronouns", postgresql.JSONB(astext_type=sa.Text()), nullable=False), | ||||
|         sa.Column("fields", postgresql.JSONB(astext_type=sa.Text()), nullable=False), | ||||
|         sa.Column("user_id", sa.BigInteger(), nullable=False), | ||||
|         sa.ForeignKeyConstraint( | ||||
|             ["user_id"], | ||||
|             ["users.id"], | ||||
|         ), | ||||
|         sa.PrimaryKeyConstraint("id"), | ||||
|     ) | ||||
|     # ### end Alembic commands ### | ||||
| 
 | ||||
| 
 | ||||
| def downgrade() -> None: | ||||
|     # ### commands auto generated by Alembic - please adjust! ### | ||||
|     op.drop_table('members') | ||||
|     op.drop_table("members") | ||||
|     # ### end Alembic commands ### | ||||
|  |  | |||
|  | @ -5,11 +5,12 @@ Revises: 17cc8cb77be5 | |||
| Create Date: 2024-03-21 15:52:09.403257 | ||||
| 
 | ||||
| """ | ||||
| 
 | ||||
| from typing import Sequence, Union | ||||
| 
 | ||||
| from alembic import op | ||||
| import sqlalchemy as sa | ||||
| 
 | ||||
| from alembic import op | ||||
| 
 | ||||
| # revision identifiers, used by Alembic. | ||||
| revision: str = "a000d800f45f" | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| from quart import Quart, request, g | ||||
| from quart import Quart, g, request | ||||
| from quart_schema import QuartSchema, RequestSchemaValidationError | ||||
| 
 | ||||
| from .blueprints import users_blueprint, members_blueprint | ||||
| from .blueprints import members_blueprint, users_blueprint | ||||
| from .db.aio import async_session | ||||
| from .db.util import validate_token | ||||
| from .exceptions import ExpectedError | ||||
|  |  | |||
|  | @ -1,14 +1,15 @@ | |||
| from functools import wraps | ||||
| 
 | ||||
| from quart import g | ||||
| 
 | ||||
| from foxnouns.exceptions import ForbiddenError, ErrorCode | ||||
| from foxnouns.exceptions import ErrorCode, ForbiddenError | ||||
| 
 | ||||
| 
 | ||||
| def require_auth(*, scope: str | None = None): | ||||
|     def decorator(func): | ||||
|         @wraps(func) | ||||
|         async def wrapper(*args, **kwargs): | ||||
|             if not ("user" in g) or not ("token" in g): | ||||
|             if "user" not in g or "token" not in g: | ||||
|                 raise ForbiddenError("Not authenticated", type=ErrorCode.Forbidden) | ||||
| 
 | ||||
|             if scope and not g.token.has_scope(scope): | ||||
|  |  | |||
|  | @ -1,2 +1,4 @@ | |||
| from .v2.users import bp as users_blueprint | ||||
| from .v2.members import bp as members_blueprint | ||||
| from .v2.users import bp as users_blueprint | ||||
| 
 | ||||
| __all__ = [users_blueprint, members_blueprint] | ||||
|  |  | |||
|  | @ -1,13 +1,12 @@ | |||
| from pydantic import Field | ||||
| from quart import Blueprint, g | ||||
| from quart_schema import validate_response, validate_request | ||||
| from sqlalchemy import select | ||||
| from quart_schema import validate_request, validate_response | ||||
| 
 | ||||
| from foxnouns.auth import require_auth | ||||
| from foxnouns.db import Member | ||||
| from foxnouns.db.aio import async_session | ||||
| from foxnouns.db.util import user_from_ref, is_self | ||||
| from foxnouns.exceptions import NotFoundError, ErrorCode | ||||
| from foxnouns.db.util import user_from_ref | ||||
| from foxnouns.exceptions import ErrorCode, NotFoundError | ||||
| from foxnouns.models.member import FullMemberModel, MemberPatchModel | ||||
| from foxnouns.settings import BASE_DOMAIN | ||||
| 
 | ||||
|  | @ -50,6 +49,9 @@ async def create_member(data: MemberCreateModel): | |||
| 
 | ||||
|         session.add(member) | ||||
|         await session.commit() | ||||
|         # This has to be fetched before we can pass the model to Pydantic. | ||||
|         # In a normal SELECT this is automatically fetched, but because we just created the object, | ||||
|         # we have to do it manually. | ||||
|         await member.awaitable_attrs.user | ||||
| 
 | ||||
|         return FullMemberModel.model_validate(member) | ||||
|  |  | |||
|  | @ -1,16 +1,16 @@ | |||
| from pydantic import Field, field_validator | ||||
| from quart import Blueprint, g | ||||
| from quart_schema import validate_response, validate_request | ||||
| from quart_schema import validate_request, validate_response | ||||
| from sqlalchemy import select | ||||
| 
 | ||||
| from foxnouns.auth import require_auth | ||||
| from foxnouns.db import User | ||||
| from foxnouns.db.aio import async_session | ||||
| from foxnouns.db.snowflake import Snowflake | ||||
| from foxnouns.db.util import user_from_ref, is_self, create_token, generate_token | ||||
| from foxnouns.exceptions import NotFoundError, ErrorCode | ||||
| from foxnouns.db.util import create_token, generate_token, is_self, user_from_ref | ||||
| from foxnouns.exceptions import ErrorCode, NotFoundError | ||||
| from foxnouns.models import BasePatchModel | ||||
| from foxnouns.models.user import UserModel, SelfUserModel, check_username | ||||
| from foxnouns.models.user import SelfUserModel, UserModel, check_username | ||||
| from foxnouns.settings import BASE_DOMAIN | ||||
| 
 | ||||
| bp = Blueprint("users_v2", __name__) | ||||
|  |  | |||
|  | @ -1,3 +1,5 @@ | |||
| from .base import Base | ||||
| from .user import User, Token, AuthMethod, FediverseApp | ||||
| from .member import Member | ||||
| from .user import AuthMethod, FediverseApp, Token, User | ||||
| 
 | ||||
| __all__ = [Base, User, Token, AuthMethod, FediverseApp, Member] | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| from sqlalchemy import URL | ||||
| from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker | ||||
| from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine | ||||
| 
 | ||||
| from foxnouns.settings import DATABASE, ECHO_SQL | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| from typing import Any | ||||
| 
 | ||||
| from sqlalchemy import Text, BigInteger, ForeignKey, Index, func, text | ||||
| from sqlalchemy import BigInteger, ForeignKey, Index, Text, func, text | ||||
| from sqlalchemy.dialects.postgresql import JSONB | ||||
| from sqlalchemy.orm import Mapped, mapped_column, relationship | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,8 +1,8 @@ | |||
| from datetime import datetime | ||||
| import enum | ||||
| from datetime import datetime | ||||
| from typing import Any | ||||
| 
 | ||||
| from sqlalchemy import Text, Integer, BigInteger, ForeignKey, DateTime | ||||
| from sqlalchemy import BigInteger, DateTime, ForeignKey, Integer, Text | ||||
| from sqlalchemy.dialects.postgresql import ARRAY, JSONB | ||||
| from sqlalchemy.orm import Mapped, mapped_column, relationship | ||||
| 
 | ||||
|  | @ -38,7 +38,9 @@ class User(Base): | |||
|         return f"User(id={self.id!r}, username={self.username!r})" | ||||
| 
 | ||||
| 
 | ||||
| from .member import Member | ||||
| # Import Member here--it's needed for the back reference in User, but Member references User, so we can only import it | ||||
| # after User is initialized. | ||||
| from .member import Member  # noqa: E402 | ||||
| 
 | ||||
| 
 | ||||
| class Token(Base): | ||||
|  |  | |||
|  | @ -2,16 +2,17 @@ import datetime | |||
| 
 | ||||
| from itsdangerous import BadSignature | ||||
| from itsdangerous.url_safe import URLSafeTimedSerializer | ||||
| from sqlalchemy.ext.asyncio import AsyncSession | ||||
| from sqlalchemy import select, insert | ||||
| from sqlalchemy.orm import selectinload | ||||
| from quart import g | ||||
| from sqlalchemy import insert, select | ||||
| from sqlalchemy.ext.asyncio import AsyncSession | ||||
| from sqlalchemy.orm import selectinload | ||||
| 
 | ||||
| from .user import User, Token | ||||
| from .member import Member | ||||
| from foxnouns.exceptions import ForbiddenError, ErrorCode | ||||
| from foxnouns.exceptions import ErrorCode, ForbiddenError | ||||
| from foxnouns.settings import SECRET_KEY | ||||
| 
 | ||||
| from .member import Member | ||||
| from .user import Token, User | ||||
| 
 | ||||
| 
 | ||||
| async def user_from_ref(session: AsyncSession, user_ref: str): | ||||
|     """Returns a user from a `user_ref` value. If `user_ref` is `@me`, returns the current user. | ||||
|  | @ -25,7 +26,7 @@ async def user_from_ref(session: AsyncSession, user_ref: str): | |||
|                 query = query.where(User.id == g.user.id) | ||||
|             else: | ||||
|                 raise ForbiddenError( | ||||
|                     f"Missing scope 'user.read'", type=ErrorCode.MissingScope | ||||
|                     "Missing scope 'user.read'", type=ErrorCode.MissingScope | ||||
|                 ) | ||||
|         else: | ||||
|             raise ForbiddenError("Not authenticated") | ||||
|  | @ -63,7 +64,7 @@ async def create_token(session: AsyncSession, user: User, scopes: list[str] = [" | |||
|     return await session.scalar(query) | ||||
| 
 | ||||
| 
 | ||||
| async def validate_token(session: AsyncSession, header: str) -> (Token, User): | ||||
| async def validate_token(session: AsyncSession, header: str) -> tuple[Token, User]: | ||||
|     try: | ||||
|         token_id = serializer.loads(header) | ||||
|     except BadSignature: | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| from pydantic import Field | ||||
| 
 | ||||
| from . import BaseSnowflakeModel | ||||
| from .fields import ProfileField, FieldEntry, PronounEntry | ||||
| from .fields import FieldEntry, ProfileField, PronounEntry | ||||
| 
 | ||||
| 
 | ||||
| class BaseUserModel(BaseSnowflakeModel): | ||||
|  |  | |||
							
								
								
									
										29
									
								
								poetry.lock
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										29
									
								
								poetry.lock
									
										
									
										generated
									
									
									
								
							|  | @ -1010,6 +1010,33 @@ async-timeout = {version = ">=4.0.3", markers = "python_full_version < \"3.11.3\ | |||
| hiredis = ["hiredis (>=1.0.0)"] | ||||
| ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==20.0.1)", "requests (>=2.26.0)"] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "ruff" | ||||
| version = "0.3.4" | ||||
| description = "An extremely fast Python linter and code formatter, written in Rust." | ||||
| category = "dev" | ||||
| optional = false | ||||
| python-versions = ">=3.7" | ||||
| files = [ | ||||
|     {file = "ruff-0.3.4-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:60c870a7d46efcbc8385d27ec07fe534ac32f3b251e4fc44b3cbfd9e09609ef4"}, | ||||
|     {file = "ruff-0.3.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6fc14fa742e1d8f24910e1fff0bd5e26d395b0e0e04cc1b15c7c5e5fe5b4af91"}, | ||||
|     {file = "ruff-0.3.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3ee7880f653cc03749a3bfea720cf2a192e4f884925b0cf7eecce82f0ce5854"}, | ||||
|     {file = "ruff-0.3.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cf133dd744f2470b347f602452a88e70dadfbe0fcfb5fd46e093d55da65f82f7"}, | ||||
|     {file = "ruff-0.3.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3f3860057590e810c7ffea75669bdc6927bfd91e29b4baa9258fd48b540a4365"}, | ||||
|     {file = "ruff-0.3.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:986f2377f7cf12efac1f515fc1a5b753c000ed1e0a6de96747cdf2da20a1b369"}, | ||||
|     {file = "ruff-0.3.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4fd98e85869603e65f554fdc5cddf0712e352fe6e61d29d5a6fe087ec82b76c"}, | ||||
|     {file = "ruff-0.3.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:64abeed785dad51801b423fa51840b1764b35d6c461ea8caef9cf9e5e5ab34d9"}, | ||||
|     {file = "ruff-0.3.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df52972138318bc7546d92348a1ee58449bc3f9eaf0db278906eb511889c4b50"}, | ||||
|     {file = "ruff-0.3.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:98e98300056445ba2cc27d0b325fd044dc17fcc38e4e4d2c7711585bd0a958ed"}, | ||||
|     {file = "ruff-0.3.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:519cf6a0ebed244dce1dc8aecd3dc99add7a2ee15bb68cf19588bb5bf58e0488"}, | ||||
|     {file = "ruff-0.3.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:bb0acfb921030d00070539c038cd24bb1df73a2981e9f55942514af8b17be94e"}, | ||||
|     {file = "ruff-0.3.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:cf187a7e7098233d0d0c71175375c5162f880126c4c716fa28a8ac418dcf3378"}, | ||||
|     {file = "ruff-0.3.4-py3-none-win32.whl", hash = "sha256:af27ac187c0a331e8ef91d84bf1c3c6a5dea97e912a7560ac0cef25c526a4102"}, | ||||
|     {file = "ruff-0.3.4-py3-none-win_amd64.whl", hash = "sha256:de0d5069b165e5a32b3c6ffbb81c350b1e3d3483347196ffdf86dc0ef9e37dd6"}, | ||||
|     {file = "ruff-0.3.4-py3-none-win_arm64.whl", hash = "sha256:6810563cc08ad0096b57c717bd78aeac888a1bfd38654d9113cb3dc4d3f74232"}, | ||||
|     {file = "ruff-0.3.4.tar.gz", hash = "sha256:f0f4484c6541a99862b693e13a151435a279b271cff20e37101116a21e2a1ad1"}, | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "six" | ||||
| version = "1.16.0" | ||||
|  | @ -1213,4 +1240,4 @@ h11 = ">=0.9.0,<1" | |||
| [metadata] | ||||
| lock-version = "2.0" | ||||
| python-versions = "^3.11" | ||||
| content-hash = "c4719965b47a0a85c480b6a26e0c7fd84d61e281a4bb012049fac9c61cd813df" | ||||
| content-hash = "a04cf866b1b5efd1f9484114b0e5924947a4044c7b7e70127f042778246c0ce6" | ||||
|  |  | |||
|  | @ -23,6 +23,12 @@ asyncpg = "^0.29.0" | |||
| environs = "^11.0.0" | ||||
| alembic = "^1.13.1" | ||||
| 
 | ||||
| [tool.poetry.group.dev] | ||||
| optional = true | ||||
| 
 | ||||
| [tool.poetry.group.dev.dependencies] | ||||
| ruff = "^0.3.4" | ||||
| 
 | ||||
| [tool.poetry.group.test] | ||||
| optional = true | ||||
| 
 | ||||
|  | @ -30,11 +36,23 @@ optional = true | |||
| pytest = "^8.0.2" | ||||
| pytest-asyncio = "^0.23.5.post1" | ||||
| 
 | ||||
| [tool.poe.tasks.dev] | ||||
| help = "Run a development server with auto-reload" | ||||
| cmd = "env QUART_APP=foxnouns.app:app quart --debug run --reload" | ||||
| 
 | ||||
| [tool.poe.tasks.server] | ||||
| help = "Run a production server" | ||||
| cmd = "uvicorn 'foxnouns.app:app'" | ||||
| 
 | ||||
| [tool.poe.tasks.migrate] | ||||
| help = "Migrate the database to the latest revision" | ||||
| cmd = "alembic upgrade head" | ||||
| 
 | ||||
| [tool.poe.tasks] | ||||
| dev = "env QUART_APP=foxnouns.app:app quart --debug run --reload" | ||||
| server = "uvicorn 'foxnouns.app:app'" | ||||
| migrate = "alembic upgrade head" | ||||
| test = "pytest" | ||||
| lint = "ruff check" | ||||
| format = "ruff format" | ||||
| "sort-imports" = "ruff check --select I --fix " | ||||
| 
 | ||||
| [tool.pytest.ini_options] | ||||
| addopts = ["--import-mode=importlib"] | ||||
|  |  | |||
|  | @ -1,11 +1,10 @@ | |||
| import pytest | ||||
| import pytest_asyncio | ||||
| from sqlalchemy import text, delete | ||||
| from sqlalchemy import delete, text | ||||
| 
 | ||||
| from foxnouns.db import Base | ||||
| from foxnouns.settings import DATABASE | ||||
| 
 | ||||
| 
 | ||||
| # Override the database name to the testing database | ||||
| DATABASE["NAME"] = f"{DATABASE['NAME']}_test" | ||||
| 
 | ||||
|  | @ -25,8 +24,8 @@ def pytest_collection_modifyitems(items): | |||
| def setup(): | ||||
|     """Migrate the testing database to the latest migration, and once the tests complete, clear the database again.""" | ||||
| 
 | ||||
|     from foxnouns.db.sync import engine | ||||
|     from alembic import command, config | ||||
|     from foxnouns.db.sync import engine | ||||
| 
 | ||||
|     cfg = config.Config("alembic.ini") | ||||
|     cfg.attributes["connection"] = engine.connect() | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue