[Flask-Cors] Update type annotation for origins to also allow Pattern#15825
[Flask-Cors] Update type annotation for origins to also allow Pattern#15825wvanbergen wants to merge 2 commits into
Conversation
This comment has been minimized.
This comment has been minimized.
ed1a6d6 to
555f3da
Compare
|
According to mypy_primer, this change has no effect on the checked open source code. 🤖🎉 |
brianschubert
left a comment
There was a problem hiding this comment.
Thanks! See remarks below. This change looks good, but I'm noticing some inconsistencies with the previous annotations that we should fix while we're here.
| origins: str | Pattern[str] | Iterable[str | Pattern[str]] | None | ||
| methods: str | list[str] | None | ||
| expose_headers: str | list[str] | None | ||
| allow_headers: str | list[str] | None |
There was a problem hiding this comment.
Unless I'm missing something, it appears that these function assume that the options have already been processed with serialize_options, which wraps a single string or pattern object in a list to ensure that origins is iterable. They'll raise a TypeError if you attempt to use a plain pattern object.
It also doesn't appear that None (or [None]) is a valid value (will also raise a TypeError). I'm guessing that that was added since the options are accessed with options.get(...), which suggests that None could be returned. But in actuality the options dict is pre-populated with the values from DEFAULT_OPTIONS, which sets non-None defaults, so I don't think a None value is ever valid?
A few examples of what I'm talking about:
# Non-iterable origins wrapped in list
$ uv run -w flask_cors python3 -c 'import flask_cors.core as c; print(c.serialize_options({"origins": "foo"}))'
{'origins': ['foo'], 'allow_headers': [None]}
# Raise on plain pattern object for origins
$ uv run -w flask_cors python3 -c 'import flask_cors.core as c; import re; print(c.get_cors_origins({"origins": re.compile("foo")}, "foo"))'
TypeError: argument of type 're.Pattern' is not a container or iterable
# Raise on None for origins
$ uv run -w flask_cors python3 -c 'import flask_cors.core as c; import re; print(c.get_cors_origins({"origins": None}, "foo"))'
TypeError: argument of type 'NoneType' is not a container or iterable
# Raise on [None] for origins
$ uv run -w flask_cors python3 -c 'import flask_cors.core as c; import re; print(c.get_cors_origins({"origins": [None]}, "foo"))'
TypeError: argument of type 'NoneType' is not a container or iterableDoes that match your experience?
The same appears to be true for allow_headers, so I'd suggest using the same type for it. It also appears that some of the other options can't be None in practice -- up to you if you want to try to fix those while we're here.
| origins: str | Pattern[str] | Iterable[str | Pattern[str]] | None | |
| methods: str | list[str] | None | |
| expose_headers: str | list[str] | None | |
| allow_headers: str | list[str] | None | |
| origins: Iterable[str | Pattern[str]] | |
| methods: str | list[str] | None | |
| expose_headers: str | list[str] | None | |
| allow_headers: Iterable[str | Pattern[str]] |
| *, | ||
| resources: dict[str, dict[str, Any]] | list[str] | str | None = ..., | ||
| origins: str | list[str] | None = ..., | ||
| origins: str | Pattern[str] | Iterable[str | Pattern[str]] | None = ..., |
There was a problem hiding this comment.
Per above, it doesn't appear that passing None is valid.
| origins: str | Pattern[str] | Iterable[str | Pattern[str]] | None = ..., | |
| origins: str | Pattern[str] | Iterable[str | Pattern[str]] = ..., |
Small fix: this updates the type annotation for the
originsargument forCORS()andCORS.init_appto also acceptPatterns. Also, useIterablerather thanlistto not cause compatibility issues.originsargument of thecross_origindecorator.