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

Type annotations for sqlalchemy.sql.elements #9143

Open
wants to merge 20 commits into
base: main
Choose a base branch
from

Conversation

jazzthief
Copy link
Contributor

Description

Added type annotations for sql.elements (issue #6810).
There is also a tiny change to sql.base - it solves a problem in elements.

Checklist

This pull request is:

  • A documentation / typographical 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!

@@ -1837,7 +1853,7 @@ def _dedupe_anon_label_idx(self, idx: int) -> str:
return self._dedupe_anon_tq_label_idx(idx)

@property
def _proxy_key(self):
def _proxy_key(self) -> Optional[str]:
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changing @property to @HasMemoized.memoized_attribute (superclass definition has it) removes the error. This isn't the only case, guessing there's an access-related conflict somewhere between @property, @HasMemoized.memoized_attribute and @util.memoized_property from mypy's point of view.

Copy link
Member

Choose a reason for hiding this comment

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

the @property situation with mypy is very difficult. it's not in general safe to change a @property to a memoized so I'd have to look at this.

return True

@HasMemoized.memoized_attribute
def _non_anon_label(self):
def _non_anon_label(self) -> Optional[str]:
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Replacing @HasMemoized.memoized_attribute with @property resolves the error.

self, against: Optional[OperatorType] = None
) -> ClauseElement:
if (
against
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Some of the self_group() overrides require against argument because of a call to is_precedent(), which conflicts with Optional arg annotation - added against to the conditional to deal with that without changing the function signature.

@@ -2223,13 +2264,13 @@ def _select_iterable(self) -> _SelectIterable:
_allow_label_resolve = False

@property
def _is_star(self):
def _is_star(self) -> bool:
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Mypy doesn't like overriding writable attribute with read-only property. My first thought was that it's mypy's general weirdness with @property, or a bug - but looks like this has been fixed. Here a guy makes a point about how this might become a legitimate problem. Anyway, not sure how to proceed, and this isn't the only case.

@@ -4155,7 +4267,7 @@ def over(self, partition_by=None, order_by=None, range_=None, rows=None):
)

@util.memoized_property
def type(self):
def type(self) -> TypeEngine[_T]:
wgt = self.element.within_group_type(self)
if wgt is not None:
return wgt
Copy link
Contributor Author

Choose a reason for hiding this comment

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

within_group_type() returns None by default, it's likely meant to be overridden - so mypy infers Any for wgt here.

@@ -3869,7 +3955,7 @@ def __init__(self, start, stop, step, _name=None):
)
self.type = type_api.NULLTYPE

def self_group(self, against=None):
def self_group(self, against: Optional[OperatorType] = None) -> Slice:
assert against is operator.getitem
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Getting a non-overlapping identity check in the assert. operator.getitem() is interpreted as an overloaded function by mypy. Hovewer the overloads it sees are from typeshed-fallback/stdlib/_operator.pyi stub. Guess it shouldn't work that way

return self

def _negate(self):
def _negate(self) -> Union[AsBoolean, False_, True_]:
Copy link
Contributor Author

Choose a reason for hiding this comment

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

In _negate() return mypy infers Optional[OperatorType] for the 3rd argument to AsBoolean when OperatorType is expected. There is no Optional in AsBoolean.__init__, so guessing mypy treats it that way because of superclass UnaryExpression Optional annotation.

def columns(self, *cols, **types):
def columns(
self, *cols: ColumnElement[Any], **types: TypeEngine[_T]
) -> util.preloaded.sql_selectable.TextualSelect:
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Line 2546: mypy expects List[ColumnClause[Any]] for TextualSelect.__init__ - as per recently merged sql.selectable annotations. However, a columns() description says its *cols argument is "a series of _expression.ColumnElement objects, typically". Not sure which is the best way to resolve this.

@jazzthief
Copy link
Contributor Author

Lines 3150-3151: for and_/or_ mypy complained about no such attribures here, looked like it didn't like BooleanClauseList.__init__ - according to Mike's previous comments, removed __init__ return annotation altogether and that fixed it. Hope that's ok, and that comment didn't refer to one particular case.

@jazzthief
Copy link
Contributor Author

jazzthief commented Jan 25, 2023

Didn't # type: ignore anything so that it's quicker to see all the issues. There are quite a few problems with overrides incompatible with supertype, and decorators issues which might require some changes to the source - which I stayed away from.

Also, mypy showed a few issues inside sql.selectable - didn't seem like I could fix any of these, will look into it some more.

@zzzeek
Copy link
Member

zzzeek commented Jan 25, 2023

a lot of these require that I think pretty long and hard about what should be done, so I would say if you have most everything annotated here, just hand off to me and I can figure out what to do with the thorny issues because these are right at the core of things I really had to spend a lot of time getting to work originally, and there may need to be bigger changes.

@jazzthief jazzthief marked this pull request as ready for review January 25, 2023 16:22
@jazzthief
Copy link
Contributor Author

@zzzeek Sure - that's my thoughts exactly, so I didn't touch a lot of these. Still, I am interested in knowing how some of these issues are going to be resolved, especially @property/decorator thing - feels like it is important to understand. I'll keep an eye on the module, please hit me up if you need anything.

@CaselIT
Copy link
Member

CaselIT commented Mar 13, 2023

Hi @jazzthief sorry for the delay. Can you update the change to remove the conflicts?

thanks!

@jazzthief jazzthief force-pushed the issue_6810_type_annotate_sqlalchemy.sql.elements branch from 2d84434 to cb068fd Compare March 14, 2023 14:34
@jazzthief
Copy link
Contributor Author

@CaselIT The conflicts are resolved, left other changes as-is like we discussed with Mike above

@CaselIT
Copy link
Member

CaselIT commented Mar 14, 2023

great thank you.

I'll move it to gerrit

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 CaselIT to try to get revision cb068fd 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 cb068fd: https://gerrit.sqlalchemy.org/c/sqlalchemy/sqlalchemy/+/4512

@sqla-tester
Copy link
Collaborator

Federico Caselli (CaselIT) wrote:

Mike I think this is at the point where you can take it over to fix the last type errors

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

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.

mike bayer (zzzeek) wrote:

just some notes so far

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

@@ -80,12 +79,20 @@
from ..util.typing import Self

if typing.TYPE_CHECKING:
from re import Match

from mypy_extensions import NoReturn
Copy link
Collaborator

Choose a reason for hiding this comment

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

mike bayer (zzzeek) wrote:

NoReturn should be in typing or sqlalchemy.util.typing

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


from mypy_extensions import NoReturn

from ._py_util import cache_anon_map
Copy link
Collaborator

Choose a reason for hiding this comment

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

mike bayer (zzzeek) wrote:

import this from visitors

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

anon_map: cache_anon_map,
bindparams: List[BindParameter[_T]],
) -> Optional[
Union[
Copy link
Collaborator

Choose a reason for hiding this comment

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

mike bayer (zzzeek) wrote:

this whole thing I think overall is just Tuple[Any], it wasn't intended to be specific to what's in the tuple.

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

self, against: Optional[OperatorType] = None
) -> ClauseElement:
if (
against
Copy link
Collaborator

Choose a reason for hiding this comment

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

mike bayer (zzzeek) wrote:

this is a logic change

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

if (
self.group
against
Copy link
Collaborator

Choose a reason for hiding this comment

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

mike bayer (zzzeek) wrote:

this looks like a logic change

there's a lot of sudden test failures so I'd guess these changes are why

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're right, removed it

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 CaselIT to try to get revision c85b4b9 of this pull request into gerrit so we can run tests and reviews and stuff

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 CaselIT to try to get revision c85b4b9 of this pull request into gerrit so we can run tests and reviews and stuff

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 CaselIT to try to get revision c85b4b9 of this pull request into gerrit so we can run tests and reviews and stuff

@sqla-tester
Copy link
Collaborator

Patchset c85b4b9 added to existing Gerrit review https://gerrit.sqlalchemy.org/c/sqlalchemy/sqlalchemy/+/4512

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 CaselIT to try to get revision c85b4b9 of this pull request into gerrit so we can run tests and reviews and stuff

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 CaselIT to try to get revision c85b4b9 of this pull request into gerrit so we can run tests and reviews and stuff

@sqla-tester
Copy link
Collaborator

Patchset c85b4b9 added to existing Gerrit review https://gerrit.sqlalchemy.org/c/sqlalchemy/sqlalchemy/+/4512

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.

None yet

4 participants