-
-
Notifications
You must be signed in to change notification settings - Fork 74
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 depends and accept_arguments decorators #962
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,46 @@ | ||
from __future__ import annotations | ||
|
||
import inspect | ||
|
||
from collections import defaultdict | ||
from functools import wraps | ||
from typing import TYPE_CHECKING, TypeVar, Callable, Protocol, TypedDict, overload | ||
|
||
from .parameterized import ( | ||
Parameter, Parameterized, ParameterizedMetaclass, transform_reference, | ||
) | ||
from ._utils import accept_arguments, iscoroutinefunction | ||
|
||
if TYPE_CHECKING: | ||
CallableT = TypeVar("CallableT", bound=Callable) | ||
Dependency = Parameter | str | ||
|
||
class DependencyInfo(TypedDict): | ||
dependencies: tuple[Dependency, ...] | ||
kw: dict[str, Dependency] | ||
watch: bool | ||
on_init: bool | ||
|
||
class DependsFunc(Protocol[CallableT]): | ||
_dinfo: DependencyInfo | ||
__call__: CallableT | ||
|
||
@overload | ||
def depends( | ||
*dependencies: str, watch: bool = ..., on_init: bool = ... | ||
) -> Callable[[CallableT], DependsFunc[CallableT]]: | ||
... | ||
|
||
@overload | ||
def depends( | ||
*dependencies: Parameter, watch: bool = ..., on_init: bool = ..., **kw: Parameter | ||
) -> Callable[[CallableT], DependsFunc[CallableT]]: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The intention with these overloads is to prevent usage that violates the validation that @depends("test", foobar=Parameter())
def test(a: int, b: str) -> None:
print("foobar")
While this works: @depends(Parameter(), foobar=Parameter())
def test(a: int, b: str) -> None:
print("foobar") |
||
... | ||
|
||
@accept_arguments | ||
def depends(func, *dependencies, watch=False, on_init=False, **kw): | ||
def depends( | ||
func: CallableT, /, *dependencies: Dependency, watch: bool = False, on_init: bool = False, **kw: Parameter | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Without the
This looks like a Mypy bug; variadic arguments via |
||
) -> Callable[[CallableT], DependsFunc[CallableT]]: | ||
"""Annotates a function or Parameterized method to express its dependencies. | ||
|
||
The specified dependencies can be either be Parameter instances or if a | ||
|
@@ -117,6 +147,6 @@ def cb(*events): | |
_dinfo.update({'dependencies': dependencies, | ||
'kw': kw, 'watch': watch, 'on_init': on_init}) | ||
|
||
_depends._dinfo = _dinfo | ||
_depends._dinfo = _dinfo # type: ignore[attr-defined] | ||
|
||
return _depends |
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.
Why do you use underscore here? Is this the norm?
It is "hidden" behind
TYPE_CHECKING
, so it will be private for users. It also adds extra overhead to reading the type hinting itself.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.
It is typical for
typeshed
, for example: https://github.com/python/typeshed/blob/main/stdlib/urllib/request.pyi#L51Not if they are also using
TYPE_CHECKING
:And regardless of
TYPE_CHECKING
it still becomes visible in IDE auto-complete, interspersed with symbols that are part of the public API (as opposed to being namespaced with an underscore prefix):(Granted, underscore is just a convention, they could just as well import it anyway.)
That said, I don't really care either way, so I'll just remove the underscores
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.
Thank you for the clarification.
Whatever the norm should be done, and if you say it is making them private, then so be it.
If you want to avoid repeating complex types, you are welcome to create a
_typing.py
file, though it does not need to be in this PR.Do you have a preferred way to test type hints? If possible, I would like your help in setting it up.
And like Philipp said, I also appreciate your work on these PRs! 💯
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.
For these PRs I generally am just running
mypy path/to/file/changed.py --follow-imports=silent
and making sure that the errors after are a strict subset of the errors that existed before. I also just create some dummy code that uses the decorator to see whether it works as expected.For something more automated - and I'm mostly thinking in the context of
panel
- we could set up a Mypy run that's part of CI. We could start with some subset of theexamples
folder that we can get zero issues on, then extend to all the examples, and after we reach 100% no issues, we can switch to Mypystrict
and repeat. The point would be to prioritize the public API - whichexamples
would encompass - over the internal library code.I figure
examples
would work a lot better than me manually creating functions in test code and applying@param.depends
etc. on it.Only after that would I try to Mypy
panel
orparam
internal code as that is a much bigger lift and could require some actual code changes to be type-compatible, etc. So we'd probably want to run the examples CI check with--follow-imports=silent
so that it doesn't prematurely start checking internal code.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 have opened a PR for Panel here holoviz/panel#7151