pylance-release
pylance-release copied to clipboard
Incorrect or unhelpful error: "ClassVar" is not allowed in this context
Environment data
- Language Server version: 2024.4.1 (pyright version 1.1.356, commit 6652c4a8)
- OS and version: Lunux
- Python version (& distribution if applicable, e.g. Anaconda): 3.11.2
Code Snippet
from typing import ClassVar
import sqlalchemy as sa
from sqlalchemy.orm import declarative_base, declarative_mixin, declared_attr
base = declarative_base()
@declarative_mixin
class Foo(base):
__abstract__ = True
__tablename__ = "foo"
id = sa.Column(sa.Integer, primary_key=True)
@declared_attr
def baz_id(self) -> ClassVar[sa.Column]:
return sa.Column(
sa.ForeignKey("baz.baz.id"),
comment="Keys to baz.baz.id",
)
class FooBar(Foo):
__table_args__ = {"schema": "foo"}
Repro Steps
- Have a Python environment with either SqlAlchemy 1.4 or 2.0
- Have VS Code open with Pylance installed
- Paste the above code into a Python file
Expected behavior
Apologies, this is very SqlAlchemy specific, so maybe this is something that Pylance can not support or something SqlAlchemy must fix.
But I would expect VS Code to not throw an error, or give more helpful error that "ClassVar" is not allowed in this context. Specifically I don't know what "allowed" means, this code runs fine and I think is the required way to get this specific feature to work in both SqlAlchemy 1.4 and 2.0.
If there is a mismatch between the way SqlAlchemy is using type hints and Pylance is understanding them, I would expect Pylance to give the option to supress this warning.
Actual behavior
For the part ClassVar[sa.Column] Pylance gives the error "ClassVar" is not allowed in this context
ClassVar is a type qualifier that can be used only in specific contexts as specified in the Python typing spec. You're using it outside of an allowed context.
In short, ClassVar has a valid meaning only when used to define a class-scoped variable. It's used to distinguish between an instance variable and a class variable within a class body. I'm not sure what you're trying to do here by using ClassVar in a return type annotation, but it has no valid meaning here, and the typing spec specifically states that a type checker should generate an error in this case.
I beleive in this case that "baz_id" does indeed become a class variable.
Although I would need to dig deeper into the SqlAlchemy machanics to understand how.
I will create an issue on the SqlAlchemy side and see why they require ClassVar in this case. But I still find the wording "not allowed in this context" confusing, as there is no runtime error and no "Quick Fix" option to supress this as a type hint issue.
no "Quick Fix" option to supress this as a type hint issue.
ya, I think we should fix this part. but for now, you can add one manually # type: ignore until we fix it.
I'm not sure how else to phrase the error. The typing spec is clear that ClassVar can be used only in certain contexts. You're using it in a context that is invalid according to the spec. ClassVar has no defined meaning in this context, so it's appropriate for a type checker to tell you "hey, this isn't doing what you think it's doing".
I don't think a quick fix makes sense here because this is a very unusual use case, and it's not clear what the "fix" would be other than to remove the ClassVar from the return type annotation.
Suppressing this error wouldn't be a good idea because ClassVar doesn't make any sense here, and it's not doing what you intended for it to do.
Please let us know if you discover what the original intent was. We may be able to help suggest an approach that works within the bounds of the typing spec.
I don't think a quick fix makes sense here because this is a very unusual use case, and it's not clear what the "fix" would be other than to remove the
ClassVarfrom the return type annotation.
In many other cases the type hinter offers the option # type: ignore, as sometimes the runtime requirements of a library do not match the typing spec,
I do agree, upon reading the spec, the wording is very difficult here. The confusion on my end though comes from the fact that Pylance will warn about things that aren't strictly type hint errors, so when I read "not allowed" I assumed this code would cause a runtime error.
Suppressing this error wouldn't be a good idea because
ClassVardoesn't make any sense here, and it's not doing what you intended for it to do.
SqlAlchemy is using the annotations at runtime to define an ORM, I have confirmed the correct ORM gets produced with ClassVar in SqlAlchemy 1.4 and 2.0, but if I ommit ClassVar SqlAlchemy 2.0 produces a runtime error.
As the runtime behavior is more important to me than the type hint behavior, it is doing what I intend it to do.
If SqlAlchemy is using this in a way that is disallowed by the current spec, they should either fix their usage or work with the typing community to come to an agreement about its intended meaning when ClassVar is used in this manner. The typing spec can be amended to accommodate new use cases where they make sense. The Python typing forum is a good place to start a discussion.
Yeah, I will create an issue with SqlAlchemy and see what their opinion is first, perhaps my understanding of how SqlAlchemy is handeling this is wrong.
FYI, SqlAlchemy authors reccomend not to use runtime type hints for this example: https://github.com/sqlalchemy/sqlalchemy/discussions/11377#discussioncomment-9386564