Skip to content

Commit

Permalink
fix: Allow user overwritte default widget opts
Browse files Browse the repository at this point in the history
  • Loading branch information
Czaki committed Oct 20, 2023
1 parent 2fa477d commit a185c85
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 27 deletions.
38 changes: 19 additions & 19 deletions src/magicgui/type_map/_type_map.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,8 +214,8 @@ def _pick_widget_type(
raise_on_unknown: bool = True,
) -> WidgetTuple:
"""Pick the appropriate widget type for ``value`` with ``annotation``."""
annotation, _options = _split_annotated_type(annotation)
options = {**_options, **(options or {})}
annotation, options_ = _split_annotated_type(annotation)
options = {**options_, **(options or {})}
choices = options.get("choices")

if is_result and annotation is inspect.Parameter.empty:
Expand All @@ -229,9 +229,9 @@ def _pick_widget_type(
):
return widgets.EmptyWidget, {"visible": False, **options}

_type, optional = _type_optional(value, annotation)
type_, optional = _type_optional(value, annotation)
options.setdefault("nullable", optional)
choices = choices or (isinstance(_type, EnumMeta) and _type)
choices = choices or (isinstance(type_, EnumMeta) and type_)
literal_choices, nullable = _literal_choices(annotation)
if literal_choices is not None:
choices = literal_choices
Expand All @@ -243,7 +243,7 @@ def _pick_widget_type(
if widget_type == "RadioButton":
widget_type = "RadioButtons"
warnings.warn(
f"widget_type of 'RadioButton' (with dtype {_type}) is"
f"widget_type of 'RadioButton' (with dtype {type_}) is"
" being coerced to 'RadioButtons' due to choices or Enum type.",
stacklevel=2,
)
Expand All @@ -252,15 +252,15 @@ def _pick_widget_type(

# look for subclasses
for registered_type in _TYPE_DEFS:
if _type == registered_type or safe_issubclass(_type, registered_type):
_cls, opts = _TYPE_DEFS[registered_type]
return _cls, {**options, **opts}
if type_ == registered_type or safe_issubclass(type_, registered_type):
cls_, opts = _TYPE_DEFS[registered_type]
return cls_, {**options, **opts}

if is_result:
_widget_type = match_return_type(_type)
if _widget_type:
_cls, opts = _widget_type
return _cls, {**options, **opts}
widget_type_ = match_return_type(type_)
if widget_type_:
cls_, opts = widget_type_
return cls_, {**opts, **options}
# Chosen for backwards/test compatibility
return widgets.LineEdit, {"gui_only": True}

Expand All @@ -269,14 +269,14 @@ def _pick_widget_type(
wdg = widgets.Select if options.get("allow_multiple") else widgets.ComboBox
return wdg, options

_widget_type = match_type(_type, value)
if _widget_type:
_cls, opts = _widget_type
return _cls, {**options, **opts}
widget_type_ = match_type(type_, value)
if widget_type_:
cls_, opts = widget_type_
return cls_, {**opts, **options}

if raise_on_unknown:
raise ValueError(
f"No widget found for type {_type} and annotation {annotation!r}"
f"No widget found for type {type_} and annotation {annotation!r}"
)

options["visible"] = False
Expand Down Expand Up @@ -328,7 +328,7 @@ def get_widget_class(
The WidgetClass, and dict that can be used for params. dict
may be different than the options passed in.
"""
widget_type, _options = _pick_widget_type(
widget_type, options_ = _pick_widget_type(
value, annotation, options, is_result, raise_on_unknown
)

Expand All @@ -340,7 +340,7 @@ def get_widget_class(
if not safe_issubclass(widget_class, widgets.bases.Widget):
assert_protocol(widget_class, WidgetProtocol)

return widget_class, _options
return widget_class, options_


def _import_wdg_class(class_name: str) -> WidgetClass:
Expand Down
16 changes: 8 additions & 8 deletions src/magicgui/widgets/bases/_create_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ def create_widget(
assert wdg.value == ""
```
"""
_options = options.copy() if options is not None else {}
options_ = options.copy() if options is not None else {}
kwargs = {
"value": value,
"annotation": annotation,
Expand All @@ -109,21 +109,21 @@ def create_widget(
from magicgui.type_map import get_widget_class

if widget_type:
_options["widget_type"] = widget_type
options_["widget_type"] = widget_type
# special case parameters named "password" with annotation of str
if (
not _options.get("widget_type")
not options_.get("widget_type")
and (name or "").lower() == "password"
and annotation is str
):
_options["widget_type"] = "Password"
options_["widget_type"] = "Password"

Check warning on line 119 in src/magicgui/widgets/bases/_create_widget.py

View check run for this annotation

Codecov / codecov/patch

src/magicgui/widgets/bases/_create_widget.py#L119

Added line #L119 was not covered by tests

wdg_class, opts = get_widget_class(
value, annotation, _options, is_result, raise_on_unknown
value, annotation, options_, is_result, raise_on_unknown
)

if issubclass(wdg_class, Widget):
widget = wdg_class(**{**kwargs, **opts, **_options})
widget = wdg_class(**{**kwargs, **opts, **options_})
if param_kind:
widget.param_kind = param_kind # type: ignore
return widget
Expand All @@ -133,9 +133,9 @@ def create_widget(
for p in ("Categorical", "Ranged", "Button", "Value", ""):
prot = getattr(protocols, f"{p}WidgetProtocol")
if isinstance(wdg_class, prot):
_options = kwargs.pop("options", None)
options_ = kwargs.pop("options", None)
cls = getattr(bases, f"{p}Widget")
widget = cls(**{**kwargs, **(_options or {}), "widget_type": wdg_class})
widget = cls(**{**kwargs, **(options_ or {}), "widget_type": wdg_class})
if param_kind:
widget.param_kind = param_kind # type: ignore
return widget
Expand Down
12 changes: 12 additions & 0 deletions tests/test_widgets.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import datetime
import inspect
import typing
from enum import Enum
from pathlib import Path
from typing import Optional, Tuple
Expand Down Expand Up @@ -110,6 +111,17 @@ def test_create_widget_annotation(annotation, expected_type):
wdg.close()


def test_create_widget_annotation_overwritte_parrams():
wdg1 = widgets.create_widget(annotation=widgets.ProgressBar)
assert isinstance(wdg1, widgets.ProgressBar)
assert wdg1.visible
wdg2 = widgets.create_widget(
annotation=typing.Annotated[widgets.ProgressBar, {"visible": False}]
)
assert isinstance(wdg2, widgets.ProgressBar)
assert not wdg2.visible


# fmt: off
class MyBadWidget:
"""INCOMPLETE widget implementation and will error."""
Expand Down

0 comments on commit a185c85

Please sign in to comment.