sqlalchemy / sqlalchemy/sqlalchemy

Unexpected relationship kwarg with mypy from a mixin

Open
#11,686 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

dataclasses typing
Dominant language
Python
Stars
12.2k
Forks
1.8k
PR merge metrics
No merged PRs in 30d

Description

Ensure stubs packages are not installed
  • No sqlalchemy stub packages is installed (both sqlalchemy-stubs and sqlalchemy2-stubs are not compatible with v2)
Verify if the api is typed
  • The api is not in a module listed in #6810 so it should pass type checking
Describe the typing issue

An unexpected kwarg error is emitted by mypy when creating a class that uses a mixin with a relationship.

To Reproduce
from sqlalchemy import ForeignKey, create_engine
from sqlalchemy.orm import (
    DeclarativeBase,
    Mapped,
    MappedAsDataclass,
    Session,
    declarative_mixin,
    declared_attr,
    mapped_column,
    relationship,
)


class Base(MappedAsDataclass, DeclarativeBase):
    pass


class User(Base):
    __tablename__ = "users"
    id: Mapped[int] = mapped_column(primary_key=True, init=False)
    name: Mapped[str]


@declarative_mixin
class UserMixin:
    user_id = mapped_column(ForeignKey("users.id"))

    @declared_attr
    def user(cls) -> Mapped["User"]:
        return relationship("User", kw_only=True)


class Task(UserMixin, Base):
    __tablename__ = "tasks"
    id: Mapped[int] = mapped_column(primary_key=True, init=False)
    name: Mapped[str]


# None is added as the return value to make sure mypy checks this
# From mypy: By default the bodies of untyped functions are not checked, consider using --check-untyped-defs  [annotation-unchecked]
def main() -> None:
    engine = create_engine("sqlite://", echo=True)
    Base.metadata.create_all(engine)
    with Session(engine) as session:
        user = User(name="user")

        # error: Unexpected keyword argument "user" for "Task"  [call-arg]
        task = Task(name="Submit issue", user=user)

        session.add_all([user, task])
        session.commit()


if __name__ == "__main__":
    main()
Error
main.py:48: error: Unexpected keyword argument "user" for "Task"  [call-arg]
Found 1 error in 1 file (checked 1 source file)
Versions
  • OS: macOS 14.5
  • Python: 3.12.2
  • SQLAlchemy: 2.0.31
  • Type checker (eg: mypy 0.991, pyright 1.1.290, etc): mypy: 1.11.1
Additional context

Repo here: https://github.com/Vafilor/sqlalchemy-mypy.

I appreciate you.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the main.py reproduction and run mypy against the shown SQLAlchemy 2.0.31 example. Trace the typing behavior for declarative_mixin, declared_attr, relationship, and MappedAsDataclass; done means Task accepts user=user without the unexpected keyword argument error.

Written by the indexing model from the issue text.

Assessment

Tech stack
python, sqlalchemy
Domain
databases
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.