Skip to content

Conversation

@k4nar
Copy link
Contributor

@k4nar k4nar commented Nov 4, 2025

Description

When func.coalesce is used with a hybrid property, the return type inferred is wrong. This can lead to suprising mypy errors, such as this:

class Foo(Base):
    __tablename__ = "foo"

    _bar: Mapped[int | None] = mapped_column("bar")
    
    @hybrid_property
    def bar(self) -> int | None:
        return self._bar
    
    @bar.inplace.expression
    @classmethod
    def _bar_expression(cls) -> ColumnElement[int]:
        return func.coalesce(cls._bar, 42)

Mypy output:

error: Argument 1 to "coalesce" of "_FunctionGenerator" has incompatible type "InstrumentedAttribute[int | None]"; expected "ColumnElement[int] | _HasClauseElement[int] | SQLCoreOperations[int] | ExpressionElementRole[int] | TypedColumnsClauseRole[int] | Callable[[], ColumnElement[int]] | LambdaElement"  [arg-type]

This PR aims to fix this issue.

Checklist

This pull request is:

  • A documentation / typographical / small typing error fix
    • Good to go, no issue or tests are needed
  • 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!

@overload
def coalesce(
self,
col: ColumnElement[_T],
Copy link
Contributor Author

@k4nar k4nar Nov 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: we cannot add Optional[_T] here, otherwise the inferred type of the result will always be coalese[Optional[_T]]. But without any change everything seems to be working as expected, and it’s covered by the typing tests.

@zzzeek zzzeek requested a review from sqla-tester November 4, 2025 14:00
Copy link
Collaborator

@sqla-tester sqla-tester left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, this is sqla-tester setting up my work on behalf of zzzeek to try to get revision 9102d5c of this pull request into gerrit so we can run tests and reviews and stuff

@sqla-tester
Copy link
Collaborator

New Gerrit review created for change 9102d5c: https://gerrit.sqlalchemy.org/c/sqlalchemy/sqlalchemy/+/6343

@zzzeek
Copy link
Member

zzzeek commented Nov 4, 2025

i have to fix pep484 github hook, because it uses py3.14 , let me see hwat i can do

@k4nar
Copy link
Contributor Author

k4nar commented Nov 4, 2025

Thanks!

In the meantime I’ve fixed the flake8 errors in my branch, but now I see that this code is actually generated. I’ll look into it.

@zzzeek
Copy link
Member

zzzeek commented Nov 4, 2025

if you rebase on main, that pep484 should be working now

@k4nar k4nar force-pushed the mypy-fix-coalesce-hybrid-property branch from 7005373 to 05d0d97 Compare November 4, 2025 16:31
@zzzeek zzzeek requested a review from sqla-tester November 4, 2025 17:57
Copy link
Collaborator

@sqla-tester sqla-tester left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, this is sqla-tester setting up my work on behalf of zzzeek to try to get revision 05d0d97 of this pull request into gerrit so we can run tests and reviews and stuff

@sqla-tester
Copy link
Collaborator

Patchset 05d0d97 added to existing Gerrit review https://gerrit.sqlalchemy.org/c/sqlalchemy/sqlalchemy/+/6343

Copy link
Collaborator

@sqla-tester sqla-tester left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Federico Caselli (CaselIT) wrote:

code review left on gerrit

View this in Gerrit at https://gerrit.sqlalchemy.org/c/sqlalchemy/sqlalchemy/+/6343

def coalesce(
self,
col: _ColumnExpressionArgument[_T],
col: _ColumnExpressionArgument[Optional[_T]],
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Federico Caselli (CaselIT) wrote:

not 100% correct but I guess it's fine in 99% of cases where coalesce is used

View this in Gerrit at https://gerrit.sqlalchemy.org/c/sqlalchemy/sqlalchemy/+/6343

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Michael Bayer (zzzeek) wrote:

Done

View this in Gerrit at https://gerrit.sqlalchemy.org/c/sqlalchemy/sqlalchemy/+/6343

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Federico Caselli (CaselIT) wrote:

Done

View this in Gerrit at https://gerrit.sqlalchemy.org/c/sqlalchemy/sqlalchemy/+/6343

Copy link
Collaborator

@sqla-tester sqla-tester left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Michael Bayer (zzzeek) wrote:

code review left on gerrit

View this in Gerrit at https://gerrit.sqlalchemy.org/c/sqlalchemy/sqlalchemy/+/6343

builtins = set(dir(__builtins__))
for key, fn_class in _fns_in_deterministic_order():
is_reserved_word = key in builtins
is_first_arg_optional = fn_class.__name__ == "coalesce"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Michael Bayer (zzzeek) wrote:

oh, i dont like this part. hardcoding to a name like that. let me see what else can be done

View this in Gerrit at https://gerrit.sqlalchemy.org/c/sqlalchemy/sqlalchemy/+/6343

@sqla-tester
Copy link
Collaborator

Federico Caselli (CaselIT) wrote:

code review left on gerrit

View this in Gerrit at https://gerrit.sqlalchemy.org/c/sqlalchemy/sqlalchemy/+/6343

@sqla-tester
Copy link
Collaborator

Gerrit review https://gerrit.sqlalchemy.org/c/sqlalchemy/sqlalchemy/+/6343 has been merged. Congratulations! :)

@sqla-tester
Copy link
Collaborator

Gerrit review https://gerrit.sqlalchemy.org/c/sqlalchemy/sqlalchemy/+/6426 has been merged. Congratulations! :)

sqlalchemy-bot pushed a commit that referenced this pull request Nov 12, 2025
Fixed typing issue where :class:`.coalesce` would not return the correct
return type when a nullable form of that argument were passed, even though
this function is meant to select the non-null entry among possibly null
arguments.  Pull request courtesy Yannick PÉROUX.

Closes: #12963
Pull-request: #12963
Pull-request-sha: 05d0d97

Change-Id: Ife83a384ea57faf446c1fdb542df14627348f40f
(cherry picked from commit d160cb5)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants