-
Notifications
You must be signed in to change notification settings - Fork 238
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
Annotate decorators that wrap Document
methods (#679)
#886
Annotate decorators that wrap Document
methods (#679)
#886
Conversation
9372fd4
to
d3a5959
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've seen tests/typing
folder, but not sure what's the process for it. It seems the idea that mypy will error if there is anything wrong with typing in the test, right? Are they run as regular tests as well?
Let me know what kind of test you'd like to see and if simple usage of insert
, save
and replace
is enough
@@ -605,12 +612,12 @@ async def save( | |||
@wrap_with_actions(EventTypes.SAVE_CHANGES) | |||
@validate_self_before | |||
async def save_changes( | |||
self, | |||
self: DocType, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI typing_extensions
has Self
which is intended for this purpose. I can replace usages of DocType
in Document
class, if it isn't too out of scope. Or submit a new PR.
There is no difference between using two, though.
result = await f( | ||
self, | ||
*args, | ||
skip_actions=skip_actions, # type: ignore[arg-type] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Type annotation spec prohibits using named arguments along with typed **kwargs
arguments.
See typing docs and PEP 612
One way to avoid using type: ignore
is to write something like:
@wraps(f)
async def wrapper(
self: "Document",
*args: P.args,
**kwargs: P.kwargs,
) -> R:
skip_actions = cast(Optional[List[Union[ActionDirections, str]]], kwargs.get("skip_actions"))
if skip_actions is None:
skip_actions = []
if skip_actions in kwargs:
kwargs["skip_actions"] = skip_actions
await ActionRegistry.run_actions(
self,
event_type=event_type,
action_direction=ActionDirections.BEFORE,
exclude=skip_actions,
)
result = await f(
self,
*args,
**kwargs,
)
await ActionRegistry.run_actions(
self,
event_type=event_type,
action_direction=ActionDirections.AFTER,
exclude=skip_actions,
)
return result
But that doesn't look better in any way.
Hi @bedlamzd, Thank you very much for your work! Should this case be added to the mypy test suite, or was it a Pylance catch? I use this test module for testing typing in usage: https://github.com/roman-right/beanie/tree/main/tests/typing. |
@roman-right That's what I was wondering in the first comment I don't remember how I've noticed it, likely an IDE check or pyright (I use it mostly). Mypy doesn't show errors because it infers from typing import (
Awaitable,
Protocol,
assert_type,
)
from pymongo.client_session import ClientSession
from beanie import Document, WriteRules
from beanie.odm.actions import ActionDirections
from tests.typing.models import Test
class DocumentInsertSignature(Protocol):
def __call__(
self,
*,
link_rule: WriteRules = ...,
session: ClientSession | None = ...,
skip_actions: list[ActionDirections | str] | None = ...,
) -> Awaitable[Document]:
...
async def test_insert_signature() -> None:
test = Test(
foo="foo",
bar="bar",
baz="baz",
)
assert_type(test.insert, DocumentInsertSignature) and mypy will do the check You might've noticed that in the above signature returns Also, protocol's |
Turns out it's a lot simpler than I thought. This works as well and I think is better because works both for mypy and pyright class DocumentClsInsertSignature(Protocol):
def __call__(
self,
self_: DocType,
/,
*,
link_rule: WriteRules = ...,
session: ClientSession | None = ...,
skip_actions: list[ActionDirections | str] | None = ...,
) -> Awaitable[DocType]:
...
def test_insert_signature() -> None:
test_insert: DocumentClsInsertSignature = Test.insert # noqa: F841 Suggested here I will add tests a bit later |
Type checkers were complaining about missing `self` argument in decorated `Document` methods. This was caused by incomplete annotations of used decorators.
6d82ae3
to
6e231eb
Compare
@roman-right so while writing tests I found out that overloads don't work well with mypy, it always chooses async case. So I rewrote annotations and just ignore type errors in async wrappers. You can see the changes in the fixup commit. Let me know when you've reviewed everything and I will squash it. Overall I consider this ready, let me know if you want me to change anything. |
0ac26c9
to
b18dd1a
Compare
Removed sync/async overload in favour of ignoring errors in wrappers because mypy confused them and always expected async function.
b18dd1a
to
64b7790
Compare
@bedlamzd Thanks a lot for the PR ! |
Hi @bedlamzd and @tristandeborde , |
hey any plan on releasing this? |
Hi everyone! |
Type checkers were complaining about missing
self
argument in decoratedDocument
methods. This was caused by incomplete annotations of used decorators.