-
Notifications
You must be signed in to change notification settings - Fork 85
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
feat: Move navbar_options
into single argument with helper navbar_options()
#1822
base: main
Are you sure you want to change the base?
Conversation
navbar_options()
into single argument with helpernavbar_options
into single argument with helper navbar_options()
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.
@schloerke Can you review the changes in this file for me? I wanted an object that operates like MISSING
but gives a better hint to users in the function signature, so I created a new sentinel DEPRECATED
object. Look at page_navbar()
or navset_bar()
(in _navs.py
, which might be hidden for being a large diff) to see how this is used.
|
||
# Sentinel value - indicates a missing value in a function call. | ||
class MISSING_TYPE: | ||
pass | ||
|
||
|
||
MISSING: MISSING_TYPE = MISSING_TYPE() | ||
DEPRECATED: MISSING_TYPE = MISSING_TYPE() # A MISSING that communicates deprecation | ||
MaybeMissing = Union[T, MISSING_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.
I find bg: MaybeMissing[str | None] = MISSING
a bit more readable than bg: str | None | MISSING_TYPE = MISSING
, so this just provides a bit of sugar around the common pattern.
def is_missing(x: Any) -> TypeIs[MISSING_TYPE]: | ||
return x is MISSING or isinstance(x, MISSING_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.
TIL about the difference between TypeGuard
and TypeIs
. (TypeGuard
only narrows the type scope in the positive case [if it is, it is, otherwise it might still be], while TypeIs
narrows the type on both sides [it either is or it isn't].)
Anyway, this could also just be isinstance(x, MISSING_TYPE)
but it's a little more succinct when used.
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.
Maybe something like this, too
def is_missing(x: Any) -> TypeIs[MISSING_TYPE]: | |
return x is MISSING or isinstance(x, MISSING_TYPE) | |
def is_missing(x: Any) -> TypeIs[MISSING_TYPE]: | |
return isinstance(x, MISSING_TYPE) | |
@overload | |
def maybe_missing(x: MISSING_TYPE, default: T) -> T: | |
... | |
@overload | |
def maybe_missing(x: T, default: Any) -> T: | |
... | |
def maybe_missing(x: Any, default: Any) -> Any: | |
return default if is_missing(x) else x |
inverse: bool = False, | ||
underline: bool = True, | ||
collapsible: bool = True, | ||
navbar_options: Optional[NavbarOptions] = None, |
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 feel somewhat on the fence about this, but would you be open to having types.NavbarOptions
inherit from TypedDict
? The benefit being that we wouldn't need ui.navbar_options()
. FWIW, the animate
argument of input_slider()
follows that pattern.
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.
Totally open to it and it's certainly a route I thought about taking. It's a little more complicated because there are methods on NavbarOptions
, so we'd need a user-facing NavbarOptions
that inherits from TypedDict
and a counterpart NavbarOptionsResolved
(or similar name) class that resolves the user-facing options.
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 turns out it's a bit more complicated because, unlike AnimationOptions
, the navbar options are not a closed set, which means we can't just call NavbarOptionsResolved(**navbar_options)
without making the type checker angry.
Code sample in pyright playground
from typing import TypedDict
class NavbarOptions(TypedDict, total = False):
underline: bool
__extra_items__: str
def test(x: NavbarOptions) -> bool:
return x.get("underline", False)
test({"underline": True})
test({"class": "my-class"}) # we want this to be okay
#> Argument of type "dict[str, str]" cannot be assigned to parameter "x" of type "NavbarOptions" in function "test"
#> "class" is an undefined item in type "NavbarOptions" (reportArgumentType)
test({"underline": True, "class": "my-class"})
#> Argument of type "dict[str, bool | str]" cannot be assigned to parameter "x" of type "NavbarOptions" in function "test"
#> "class" is an undefined item in type "NavbarOptions" (reportArgumentType)
It turns out there's a PEP for this but it's still in draft mode: https://peps.python.org/pep-0728/
Moves navbar options
position
,bg
,inverse
,underline
andcollapsibe
into anavbar_options
argument inui.page_navbar()
andui.navset_bar()
. These options can be prepared with a new helperui.navbar_options()
. Further,inverse
is replaced withtheme
inui.navber_options()
.Reflects changes in
navbar_options()
rstudio/bslib#1141The top-level direct arguments are now deprecated, but users can continue to use them (with a deprecation warning).