-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
add mypy option to automatically infer no "Optional" for nullable=False #6201
Comments
hiya - May I ask that you read https://docs.sqlalchemy.org/en/14/orm/extensions/mypy.html#introspection-of-columns-based-on-typeengine and come back to me with your comments? There is a documented rationale for the current behavior. |
Hi, Thanks for the link to the documentation, I missed that rationale. That said, I'd like to advocate for adapting this to respect the nullability of the column. To my knowledge, nullable columns are only This covers the usage of the column as expression, but opens for errors when used as a left-hand side of an assignment statement. The column will now accept from sqlalchemy import create_engine, select
from sqlalchemy.orm import Session, registry
from sqlalchemy.schema import Column
from sqlalchemy.types import Integer
mapper_registry: registry = registry()
engine = create_engine('sqlite:///foo.db')
@mapper_registry.mapped
class A:
__tablename__ = "a"
id = Column(Integer, primary_key=True, nullable=False)
a = Column(Integer, nullable=False)
with Session(engine) as session:
a: A = session.execute(select(A)).scalar_one()
a.a = None
session.commit() Resulting in This default behaviour can be overwritten by manually marking the column with type. To be safe from these assignments, all declared classes would need manual type annotations. I have no data to support it, however I feel that accidentally assigning Looking forward to your opinion! |
it depends on what people are doing. It would be wrong for the plugin to infer that the attribute is never None, when it certainly can be. This is why it's also very easy to override, if you don't want to worry about the None-ness by using an explicit "Mapped[type]" annotation. I'd rather have users opt-in to that so they know what they are doing.
Keeping in mind that I understand people are going to want this behavior and it is planned to add an option to setup.cfg that will allow the old plugin's behavior in this area (e.g., your problem will be solved in the way you want it to), I still think this is wrong. This is asking the mypy plugin, which is tasked with checking for Python type safety, to serve as a database-level validation tool, which is not its purpose. From a Python pov the code is valid. The database then does its job of telling you that on that end, it's not valid. A key philosophy of SQLAlchemy is to not duplicate the work that the database already does. Code that explicitly sets a non nullable col to None will fail immediately and assuming the developer is running rudimentary unit tests against their sofware will find this problem immediately. once again, when I made this decision, I fully expected people to want the "old" behavior and am totally fine adding a flag in setup.cfg to do so.
SQLAlchemy doesn't enforce non-nullability at the object level, so mypy plugins default behavior is to match this. if SQLAlchemy did in fact create a declarative constructor that checked for "None" and also automatically attached validators to Python attributes that detected nullability, then this would be more appropriate. No problem adding an option to do this but from my pov it's not a complete solution unless SQLAlchemy moved to fully interpret "nullable=False" within the ORM attribute and constructor system. |
note my idea at sqlalchemy/sqlalchemy2-stubs#170 (comment) for a new overlay on declarative that would allow declarative to introspect from annotations the way dataclasses does, that would be the more "shorthand" way of doing mappings and IIRC it also skips most of the mypy plugin. |
This is now implemented for SQLAlchemy 2.0 using mapped_column, which provides a new approach to mapping columns that is typing-fluent. the mypy plugin is now legacy. |
Is your feature request related to a problem? Please describe.
While trying to switch from dropbox/sqlalchemy mypy plugin to the official sqlalchemy plugin, I found that in these stubs
nullable
andprimary_key
have no effect on the Colum types.Describe the solution you'd like
Currently the inferred type is always a
Union[..., None]
, however based onnullable
andprimary_key
properties of theColumn
we could infer that columns should never containNone
.Describe alternatives you've considered
N/A
Additional context
Results from
plugins = sqlalchemy.ext.mypy.plugin
:Results from
plugins = sqlmypy
:Have a nice day!
The text was updated successfully, but these errors were encountered: