Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

InstrumentedAttribute should also be covariant on its input type #11112

Open
2 tasks done
ejlangev opened this issue Mar 6, 2024 · 3 comments
Open
2 tasks done

InstrumentedAttribute should also be covariant on its input type #11112

ejlangev opened this issue Mar 6, 2024 · 3 comments
Labels
PRs (with tests!) welcome a fix or feature which is appropriate to be implemented by volunteers typing pep -484 typing issues. independent of "mypy"

Comments

@ejlangev
Copy link
Contributor

ejlangev commented Mar 6, 2024

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

InstrumentedAttribute is defined as

class InstrumentedAttribute(QueryableAttribute[_T):
    ...

In #10289, many of the types related to Mapped were made to be covariant which is helpful for user facing types. In my case I want to define a function that operates on relationship fields of my models generically. Roughly this:

_BaseModelT = TypeVar("_BaseModelT", bound=BaseModel)

def my_relationship_function(relationships: list[InstrumentedAttribute[_BaseModelT | Optional[_BaseModelT] | Sequence[_BaseModelT]]]) -> int:
    return len(relationships)

But I can't do that because the TypeVar passed into the InstrumentedAttribute class is not covariant. Allowing this I think should be as simple as changing class InstrumentedAttribute(QueryableAttribute[_T]): into class InstrumentedAttribute(QueryableAttribute[_T_co]):

To Reproduce

from typing import Optional, Sequence, TypeVar
from sqlalchemy.orm import DeclarativeBase, InstrumentedAttribute, Mapped, relationship

class BaseModel(DeclarativeBase):
    pass

class MyParent(BaseModel):
    __tablename__ = "my_parents"
    id: Mapped[int]
    name: Mapped[str]

    children: Mapped[Sequence["MyChild"]] = relationship(back_populates="parent")

class MyChild(BaseModel):
    __tablename__ = "my_children"

    id: Mapped[int]
    last_name: Mapped[str]
    parent_id: Mapped[int]

    parent: Mapped["MyParent"]  = relationship(back_populates="children")

_BaseModelT = TypeVar("_BaseModelT", bound=BaseModel)

def my_relationship_function(relationships: list[InstrumentedAttribute[_BaseModelT | Optional[_BaseModelT] | Sequence[_BaseModelT]]]) -> int:
    return len(relationships)

# This is a type error but should not be
my_relationship_function([MyParent.children])
# This is also a type error and should be but is an error
# for the wrong reasons
my_relationship_function([MyParent.name])

Error

Argument of type "list[InstrumentedAttribute[Sequence[MyChild]]]" cannot be assigned to parameter "relationships" of type "list[InstrumentedAttribute[_BaseModelT@my_relationship_function | Sequence[_BaseModelT@my_relationship_function] | None]]" in function "my_relationship_function"
  "InstrumentedAttribute[Sequence[MyChild]]" is incompatible with "InstrumentedAttribute[_BaseModelT@my_relationship_function | Sequence[_BaseModelT@my_relationship_function] | None]"
    Type parameter "_T@InstrumentedAttribute" is invariant, but "Sequence[MyChild]" is not the same as "_BaseModelT@my_relationship_function | Sequence[_BaseModelT@my_relationship_function] | None

Versions

  • OS: MacOS
  • Python: 3.11.6
  • SQLAlchemy: 2.0.28
  • Type checker (eg: mypy 0.991, pyright 1.1.290, etc): pyright 1.1.345

Additional context

The type checker is correct here, the type definitions are (I think) unnecessarily limiting.

@ejlangev ejlangev added requires triage New issue that requires categorization typing pep -484 typing issues. independent of "mypy" labels Mar 6, 2024
@zzzeek zzzeek added PRs (with tests!) welcome a fix or feature which is appropriate to be implemented by volunteers and removed requires triage New issue that requires categorization labels Mar 6, 2024
@zzzeek
Copy link
Member

zzzeek commented Mar 6, 2024

feel free to send a PR, including a test as you see in the change you refer to. I'd put it in the same mapped_covariant file for succinctness

@ejlangev
Copy link
Contributor Author

ejlangev commented Mar 6, 2024

feel free to send a PR, including a test as you see in the change you refer to.

Will do!

@ejlangev
Copy link
Contributor Author

ejlangev commented Mar 6, 2024

Filed: #11113

ejlangev added a commit to ejlangev/sqlalchemy that referenced this issue Mar 6, 2024
ejlangev added a commit to ejlangev/sqlalchemy that referenced this issue Mar 7, 2024
ejlangev added a commit to ejlangev/sqlalchemy that referenced this issue Mar 7, 2024
sqlalchemy-bot pushed a commit that referenced this issue Mar 13, 2024
<!-- Provide a general summary of your proposed changes in the Title field above -->

Allows mapped relationships to use covariant types which makes it possible to define methods that operate on relationships in a typesafe way

### Description

See: #11112 for a more in depth explanation.

Just changed the type parameter in `InstrumentedAttribute` from `_T` to `_T_co`.

### Checklist
<!-- go over following points. check them with an `x` if they do apply, (they turn into clickable checkboxes once the PR is submitted, so no need to do everything at once)

-->

This pull request is:

- [ ] A documentation / typographical / small typing error fix
	- Good to go, no issue or tests are needed
- [x] A short code fix
	- please include the issue number, and create an issue if none exists, which
	  must include a complete example of the issue.  one line code fixes without an
	  issue and demonstration will not be accepted.
	- Please include: `Fixes: #<issue number>` in the commit message
	- please include tests.   one line code fixes without tests will not be accepted.
- [ ] A new feature implementation
	- please include the issue number, and create an issue if none exists, which must
	  include a complete example of how the feature would look.
	- Please include: `Fixes: #<issue number>` in the commit message
	- please include tests.

**Have a nice day!**

Closes: #11113
Pull-request: #11113
Pull-request-sha: 3c100f2

Change-Id: Iff715c24f1556d5604dcd33661a0ee7232b9404b
sqlalchemy-bot pushed a commit that referenced this issue Mar 13, 2024
<!-- Provide a general summary of your proposed changes in the Title field above -->

Allows mapped relationships to use covariant types which makes it possible to define methods that operate on relationships in a typesafe way

### Description

See: #11112 for a more in depth explanation.

Just changed the type parameter in `InstrumentedAttribute` from `_T` to `_T_co`.

### Checklist
<!-- go over following points. check them with an `x` if they do apply, (they turn into clickable checkboxes once the PR is submitted, so no need to do everything at once)

-->

This pull request is:

- [ ] A documentation / typographical / small typing error fix
	- Good to go, no issue or tests are needed
- [x] A short code fix
	- please include the issue number, and create an issue if none exists, which
	  must include a complete example of the issue.  one line code fixes without an
	  issue and demonstration will not be accepted.
	- Please include: `Fixes: #<issue number>` in the commit message
	- please include tests.   one line code fixes without tests will not be accepted.
- [ ] A new feature implementation
	- please include the issue number, and create an issue if none exists, which must
	  include a complete example of how the feature would look.
	- Please include: `Fixes: #<issue number>` in the commit message
	- please include tests.

**Have a nice day!**

Closes: #11113
Pull-request: #11113
Pull-request-sha: 3c100f2

Change-Id: Iff715c24f1556d5604dcd33661a0ee7232b9404b
(cherry picked from commit 058e10f)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
PRs (with tests!) welcome a fix or feature which is appropriate to be implemented by volunteers typing pep -484 typing issues. independent of "mypy"
Projects
None yet
Development

No branches or pull requests

2 participants