Hello!
As I was trying to introduce strict typing to my project I ran into an issue when integrating pytz with marshmallow. It seems that marshmallow might be requesting too strict of types. The request for only datetime timezones prevents me from using pytz with the project (even though they are compatible). The mypy maintainer suggested that marshmallow needs to integrate protocol types to be able to solve this problem, though I'll admit I'm not too sure what that means in this case. For a rundown of the issue please see:
The base class is tzinfo.
Would it make any difference if we specified tzinfo rather than datetime in the typing info?
>>> issubclass(dt.timezone, dt.tzinfo)
True
>>> isinstance(pytz.timezone('Europe/Paris'), dt.tzinfo)
True
>>> isinstance(pytz.timezone('Europe/Paris'), dt.timezone)
False
@lafrech I think what needs to be specified is some subclass of dt.tzinfo, not sure how you would go about that.
I think is the solution here: https://stackoverflow.com/questions/46092104/subclass-in-type-hinting
Hmmm... I think the argument is a tzinfo instance, so no need for this.
Would you like to try? Install marshmallow locally, change the typing info to specify tzinfo and see how it goes with mypy in your project.
A cast or a
# type: ignoreare the ways to go, or you can implement a helper
function that does the (no-op) type conversion. There is no supported way to
say that two types should be treated as equivalent.
😕 The point of casting is to declare that "two types should be treated as equivalent".
This returns the value unchanged. To the type checker this signals that the return value has the designated type, but at runtime we intentionally don’t check anything
https://docs.python.org/3/library/typing.html#typing.cast
If you can guarantee that pytz.UTC provides a superset of the interface provided by timezone, then you can cast it.
from marshmallow import Schema, fields
from typing import cast
from datetime import timezone
import pytz
UTC = cast(timezone, pytz.UTC)
class ExampleSchema(Schema):
schema_date = fields.AwareDateTime(default_timezone=UTC)
# Success: no issues found in 1 source file
The mypy maintainer suggested that marshmallow needs to integrate protocol types to be able to solve this problem
maybe marshmallow should use protocol types to define timezone types?
I think it is reasonable to expect libraries to inherit from the builtin type they are extending. Structural interfaces are slower to type check, and I don't think libraries should maintain a mirror of builtin interfaces.
My point was that perhaps we should specify datetime.tzinfo as argument type rather than datetime.timezone.
def __init__(
self, format: str = None, *, default_timezone: dt.tzinfo = None, **kwargs
):
What we actually expect is a tzinfo, not specifically a dt.timezone.
dt.timezone is a convenience class provided by Python core to implement basic timezones. pytz timezones also inherit from tzinfo, which is the right thing to do, not dt.timezone.
The change above should fix the issue IIUC.
I would mark this issue as a bug @deckar01 I'll try to implement @lafrech patch and submit a PR when I get a chance