sqlalchemy / sqlalchemy2-stubs

PEP-484 typing stubs for SQLAlchemy 1.4
MIT License
158 stars 41 forks source link

Can't infer type from @declared_attr on function __tablename__ #127

Open mdczaplicki opened 3 years ago

mdczaplicki commented 3 years ago

Describe the bug Receiving an error when using @declared_attr decorator on __tablenane__ class method.

Expected behavior Not receiving an error.

To Reproduce

from uuid import UUID, uuid4

from sqlalchemy import Column
from sqlalchemy.orm import as_declarative, declared_attr
from sqlalchemy_utils import Timestamp, UUIDType, generic_repr

@as_declarative()
@generic_repr
class BaseModel(Timestamp):
    __name__: str
    uuid: UUID = Column(
        UUIDType(native=True),
        primary_key=True,
        default=uuid4,
        unique=True,
        index=True,
    )

    @declared_attr
    def __tablename__(cls) -> str:
        return f"{cls.__name__}s"

Error

[SQLAlchemy Mypy plugin] Can't infer type from @declared_attr on function '__tablename__';  please specify a return type from this function that is one of: Mapped[<python type>], relationship[<target class>], Column[<TypeEngine>], MapperProperty[<python type>]

Versions.

Additional context It also happens for columns with UUID (same example) if you don't specify a type on left hand side. It happens when using UUIDType from sqlalchemy_utils or UUID from sqlalchemy.dialects.postgresql.

Have a nice day!

CaselIT commented 2 years ago

This may a plugin issue. cc @bryanforbes

omBratteng commented 2 years ago

Getting the same error on this class

import re
from typing import Any

from sqlalchemy.orm import as_declarative, declared_attr

@as_declarative()
class Base:
    id: Any
    __name__: str

    # Generate __tablename__ automatically
    @declared_attr
    def __tablename__(cls) -> str:
        name = cls.__name__
        # It is a regex that matches CamelCase and replaces it with snake_case
        # https://stackoverflow.com/questions/1175208/elegant-python-function-to-convert-camelcase-to-snake-case
        return re.sub(r'(?<!^)(?=[A-Z])', '_', name).lower()

Installed via SQLAlchemy = {extras = ["mypy"], version = "^1.4.23"}

Lacrymology commented 1 month ago

Getting the same error even using Mapped as the error message suggests with

class TimestampModelMixin:
    @declared_attr
    def created_at(cls) -> datetime:
        return Column(types.DateTime(timezone=True), server_default=text("CURRENT_TIMESTAMP"), nullable=False)

    @declared_attr
    def updated_at(cls) -> Mapped[datetime]:
        return Column(
            types.DateTime(timezone=True),
            server_default=text("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"),
            nullable=False,
        )