question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Supporting __get__ and __set__ on Column object

See original GitHub issue

The old sqlalchemy-stubs from dropbox included an approximation for the behavior of mapped classes using declarative setup, that is

class Foo(Model):
  a = Column(DateTime)

Foo.a
# type is Column[DateTime]
Foo().a
# type is datetime

They did so by defining the get method on Column here: https://github.com/dropbox/sqlalchemy-stubs/blob/master/sqlalchemy-stubs/sql/schema.pyi#L131

Maybe the same can be done for set, so that

Foo().a = 3
# wrong type, expected datetime object

Of course the whole thing is supported already when using the mypy plugin, but for example when using pylance (vscode type engine) which does not support plugins, it gets confused when using and assigning object values as mapped columns and complains about mismatching types.

I think the same is done on relationship() property

I’m unsure of the deeper implications of adding such type hints, though

Issue Analytics

  • State:open
  • Created 2 years ago
  • Reactions:2
  • Comments:108 (87 by maintainers)

github_iconTop GitHub Comments

4reactions
zzzeekcommented, Sep 10, 2021

hi can folks here review this proposed new API / syntax? This adds a new import space “m” that has new symbols that handle typing correctly, without any lying and without the need for plugins (plugin still needed for Base class and __init__ method). this proposal would be a new system that would be soft roll out in 1.4 and then in 2.0 would be much more prominent. Where possible, it determines the python type from the SQL type (yes, that is doable). For relationships, it works the other way around by default since we usually need an annotation like “List[X]” or similar and that can’t be passed as an argument. The gerrit and PR will be updated momentarily with the initial POC for this. it’s an all new thing and would need new tests, new docs , etc, the whole deal. but it doesn’t impact anything currently present whatsoever so has no backwards incompatibility of any kind.

"""this program types in pylance / mypy etc with no plugins"""

from typing import List

from sqlalchemy import ForeignKey
from sqlalchemy import Integer
from sqlalchemy import String
from sqlalchemy.orm import m
from sqlalchemy.orm import registry

reg = registry()

@reg.mapped
class A:
    __tablename__ = "a"

    # Mapped[int] is inferred
    id = m.column(Integer, primary_key=True)

    # Mapped[str] is inferred
    data = m.column(String(50))
    
    # relationship("B", uselist=True, collection_class=list) 
    # is inferred from annotation
    bs: m.Mapped[List["B"]] = m.related(back_populates="a")

@reg.mapped
class B:
    __tablename__ = "b"

    # Mapped[int] is inferred
    id = m.column(Integer, primary_key=True)

    # no type in column(), so send explict Python type
    # (database type is inferred from ForeignKey when 
    # Table objects are created, this is not new)
    a_id: m.Mapped[int] = m.column(ForeignKey("a.id"))

    # Mapped[str] is inferred
    data = m.column(String)

    # relationship("A", uselist=False) is inferred from annotation
    a: m.Mapped["A"] = m.related(back_populates="bs")


a1 = A()

# pylance etc. knows a1.bs is List[B]
a1.bs.append(B())

b1: B = a1.bs[0]

# pylance knows a1.id is an int
a1.id = 5

# squiggly line w pylance for this type mismatch
# not sure why mypy doesn't complain
a1.id = "Asdf"


1reaction
zzzeekcommented, Jul 19, 2022

sure just apply the same trick to relationship, like this works

from typing import Any
from typing import Optional
from typing import Type
from typing import TYPE_CHECKING
from typing import TypeVar
from typing import Union

from sqlalchemy import Column
from sqlalchemy import ForeignKey
from sqlalchemy import Integer
from sqlalchemy import String
from sqlalchemy.orm import declarative_base
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import relationship
from sqlalchemy.types import TypeEngine

_T = TypeVar("_T")

if TYPE_CHECKING:

    class mapped_column(Mapped[_T]):
        def __init__(
            self,
            __typ: Optional[Union[TypeEngine[_T], Type[TypeEngine[_T]], Any]],
            *arg,
            **kw,
        ):
            ...

    def orm_relationship(
        arg: Any, **kw: Any
    ) -> Mapped[Any]:
        ...


else:
    mapped_column = Column
    orm_relationship = relationship

Base = declarative_base()


class A(Base):
    __tablename__ = "a"

    id = mapped_column(Integer, primary_key=True)
    data = mapped_column(String)
    bs: Mapped[list["B"]] = orm_relationship("B")


class B(Base):
    __tablename__ = "b"
    id = mapped_column(Integer, primary_key=True)
    a_id = mapped_column(ForeignKey("a.id"))
    data = mapped_column(String)


a1 = A()

# (variable) data: str
a1.data

# (variable) data: InstrumentedAttribute[str]
A.data

# (variable) bs: InstrumentedAttribute[list[B]]
A.bs

a1 = A()

# (variable) bs: list[B]
a1.bs
Read more comments on GitHub >

github_iconTop Results From Across the Web

Column object (ADOX) - Microsoft Learn
Access provider-specific properties with the Properties collection. Note. Not all properties of Column objects may be supported by your data ...
Read more >
ResultSet (Java Platform SE 7 ) - Oracle Help Center
Gets the value of the designated column in the current row of this ResultSet object as an Object in the Java programming language....
Read more >
Object Tagging - Snowflake Documentation
The following table lists the supported objects for tags, including columns, based on the Snowflake securable object hierarchy. A tag can be set...
Read more >
SQL Server Table Valued Parameter column is not supported ...
Have you tried converting the values to object type before you pass them to DataRow item? – Engin. Jul 29, 2015 at 18:15....
Read more >
Table (Guava: Google Core Libraries for Java 19.0 API)
When modification isn't supported, those methods will throw an ... Returns a set of column keys that have one or more values in...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found