Comments (11)
I was just trying out the new stubs on an existing project if mine, and it looks like you did some awesome work there - I can get rid of a lot of workarounds. Thanks! Nevertheless, there's one issue I wanted to share with you.
Describe the bug
There are two ways to pass the first parameter of a relationship: by class or by str. When using the class directly everything works as expected, the type is detected when usinguselist
orrelationsship
. And I don't find any false positives afterwards.When I use a str to configure my relationship things get pretty messy. First, I need to type the annotations manually, I wasn't able to find any way to configure it such that auto-detection works.
at the moment I'm not sure if auto-detection is going to be possible when the given arg is a string because the mypy plugin can't replicate how SQLAlchemy's registry does the lookups. That said there are ongoing bugs in the "string" version of things here anyway so not sure if @bryanforbes would find a way to manufacture a type for that case based on the string.
[SQLAlchemy Mypy plugin] Can't infer type from ORM mapped expression assigned to attribute 'addresses'; please specify a Python type or Mapped[<python type>] on the left hand side.
yes, so, I feel like if this is as good as we can get with that, this is still pretty good because it means you could have all your relationships passing under mypy. but onto where you talk about the bugs which looks to be things we already know about.
Second, even when annotating it manually, I'll have to cast it every single time I use it (when it's some kind of Iterable as shown in the example below).
error: "List?[Address?]" has no attribute "iter" (not iterable)
error: "List?[Address?]" has no attribute "append"
yeah this is the bug. this is sqlalchemy/sqlalchemy#6255 so this report is a dupe.
**Expected behavior** Type detection works for relationship independent of type of first argument. **To Reproduce** ```python from __future__ import annotations from typing import List from sqlalchemy import Integer, Column, String, ForeignKey from sqlalchemy.orm import declarative_base, relationship Base = declarative_base() class User(Base): __tablename__ = "users" id = Column(Integer, primary_key=True) name = Column(String) addresses: List[Address] = relationship("Address", back_populates="user", uselist=True) def clone(self) -> User: new_user = User(name=self.name) for existing_addr in self.addresses: new_user.addresses.append( Address(address=existing_addr.address) ) return new_user class Address(Base): __tablename__ = "addresses" id = Column(Integer, primary_key=True) address = Column(String) user_id = Column(Integer, ForeignKey("users.id")) user = relationship(User, back_populates="addresses", uselist=False)
thanks for the test case! unfortunately we are in this space with two different repos for the plugin/stubs so it's hard to find existing issues but this is the current big problem we are hoping to solve since there is no reaonable workaround.
from sqlalchemy2-stubs.
Thanks for the explanations. Is there any possibility to use the class directly in the relationsships?
from sqlalchemy2-stubs.
not sure what you mean. if you have relationship(Class), then it works. relationship("Class") is when it isn't importable yet. do you mean the mypy plugin could change the argument? overall the issue is that the plugin isn't able to tel mypy strongly enough that "yes, this is the type, please use it", so maybe. not really sure why it doesnt work already, mypy is very difficult to understand.
from sqlalchemy2-stubs.
No, that's not what I meant. The question was, if you are aware of any option to use the class when it isn't importable yet, some kind of "hack". I'm working a lot with back_populates
- this means I'd have to use casts a lot in my code.
from sqlalchemy2-stubs.
As a workaround, you might be able to use something like:
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from models import Address
else:
Address = "Address"
During type checking circular imports are not a problem.
from sqlalchemy2-stubs.
@MaicoTimmerman Thanks for the idea, I had something similar in mind. Your example brings us closer to a solution:
from typing import List, TYPE_CHECKING
from sqlalchemy import Integer, Column, String, ForeignKey
from sqlalchemy.orm import declarative_base, relationship
Base = declarative_base()
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True)
name = Column(String)
if not TYPE_CHECKING:
Address = "Address"
addresses: List[Address] = relationship(
Address, back_populates="user", uselist=True
)
def clone(self) -> User:
new_user = User(name=self.name)
for existing_addr in self.addresses:
new_user.addresses.append(Address(address=existing_addr.address))
return new_user
class Address(Base):
__tablename__ = "addresses"
id = Column(Integer, primary_key=True)
address = Column(String)
user_id = Column(Integer, ForeignKey("users.id"))
user = relationship(User, back_populates="addresses", uselist=False)
Nevertheless, one issue persists:
Incompatible types in assignment (expression has type "RelationshipProperty[<nothing>]", variable has type "List[Address]")
from sqlalchemy2-stubs.
OK here's a workaround:
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True)
name = Column(String)
if TYPE_CHECKING:
addresses: Mapped[List[Address]]
else:
addresses: List[Address] = relationship("Address", back_populates="user", uselist=True)
def clone(self) -> User:
new_user = User(name=self.name)
for existing_addr in self.addresses:
new_user.addresses.append(
Address(address=existing_addr.address)
)
return new_user
what's super frustrating is I cant get the plugin to do that, if i change the class to express that code exactly it still fails.
from sqlalchemy2-stubs.
good news I foudn where it's breaking. stay tuned
from sqlalchemy2-stubs.
I'm totally excited. ;)
Thanks for looking into it!
from sqlalchemy2-stubs.
I'm also in the same boat - I have a lot of back_populates
in use and so a lot of strings used as types. Any update would be much appreciated :)
from sqlalchemy2-stubs.
this seems to be fixed now
from sqlalchemy2-stubs.
Related Issues (20)
- Incorrect keyword argument name 'parameters' for AsyncSession.scalars, AsyncEngine.scalars HOT 1
- Improve declarative_mixin stub
- TupleType missing from sqlalchemy/types.pyi
- Make Column and relationship inherit from Mapped HOT 5
- MemoizedHasCacheKey import path is wrong
- Using compiles leads to untyped decorator HOT 1
- Type error when attempting to use a Literal[True] with sa.and_() HOT 1
- Error importing plugin "sqlalchemy.ext.mypy.plugin": cannot import name 'Optional' from 'mypy.plugin' HOT 1
- Invalid base class "Base" when returned by a function HOT 9
- Incomplete typing for Engine.dispose's close parameter HOT 1
- TypeEngine.with_variant should accept TypeEngine for type argument instances as well as Type[TypeEngine] HOT 1
- Cannot access member "add" for type "AsyncSession" (pylance) SQLAlchemy 1.4 HOT 2
- pre-commit config generates 100s of errors HOT 1
- Cannot access "sqlalchemy.orm.mapped_column" HOT 1
- MutableBase.coerce incorrectly returns None HOT 1
- Type checking fails when using `joins_implicitly` with `column_valued()` HOT 1
- [0.0.2a36 regression] `.column_valued()` without a `name` is no longer accepted
- TypeError: format_type() missing 1 required positional argument: 'options' HOT 1
- Pyright: Mapped is considered unhashable
- Incompatible types in assignment when base declaration is in a separate file HOT 7
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from sqlalchemy2-stubs.