Repository: napari/napari

Issue:
Deprecate/Remove unused arguments from utils functions in `napari/napari/utils/misc.py` ?
## 🧰 Task
In `napari/utils/misc.py` both `def ensure_iterable` and `def is_iterable` seem to have keywords arguments that are never used (`color=False`, and `allow_none=False`), leading to non-covered branches. 

I suggest to deprecate passing the keyword arguments, then open an issue to remove branches.
Deprecate/Remove unused arguments from utils functions in `napari/napari/utils/misc.py` ?
## 🧰 Task
In `napari/utils/misc.py` both `def ensure_iterable` and `def is_iterable` seem to have keywords arguments that are never used (`color=False`, and `allow_none=False`), leading to non-covered branches. 

I suggest to deprecate passing the keyword arguments, then open an issue to remove branches.


Discussion:

Resolving patch:
diff --git a/napari/utils/misc.py b/napari/utils/misc.py
--- a/napari/utils/misc.py
+++ b/napari/utils/misc.py
@@ -1,5 +1,7 @@
 """Miscellaneous utility functions."""
 
+from __future__ import annotations
+
 import builtins
 import collections.abc
 import contextlib
@@ -28,6 +30,8 @@
 
 from napari.utils.translations import trans
 
+_sentinel = object()
+
 if TYPE_CHECKING:
     import packaging.version
 
@@ -35,7 +39,7 @@
 ROOT_DIR = os_path.dirname(os_path.dirname(__file__))
 
 
-def parse_version(v: str) -> 'packaging.version._BaseVersion':
+def parse_version(v: str) -> packaging.version._BaseVersion:
     """Parse a version string and return a packaging.version.Version obj."""
     import packaging.version
 
@@ -127,13 +131,25 @@ def str_to_rgb(arg: str) -> list[int]:
 
 
 def ensure_iterable(
-    arg: Union[None, str, Enum, float, list, npt.NDArray], color: bool = False
+    arg: Union[None, str, Enum, float, list, npt.NDArray],
+    color: object | bool = _sentinel,
 ):
     """Ensure an argument is an iterable. Useful when an input argument
-    can either be a single value or a list. If a color is passed then it
-    will be treated specially to determine if it is iterable.
+    can either be a single value or a list.
+    Argument color is deprecated since version 0.5.0 and will be removed in 0.6.0.
     """
-    if is_iterable(arg, color=color):
+    # deprecate color
+    if color is not _sentinel:
+        warnings.warn(
+            trans._(
+                'Argument color is deprecated since version 0.5.0 and will be removed in 0.6.0.',
+            ),
+            category=DeprecationWarning,
+            stacklevel=2,  # not sure what level to use here
+        )
+    if is_iterable(
+        arg, color=color
+    ):  # argumnet color is to be removed in 0.6.0
         return arg
 
     return itertools.repeat(arg)
@@ -141,20 +157,31 @@ def ensure_iterable(
 
 def is_iterable(
     arg: Union[None, str, Enum, float, list, npt.NDArray],
-    color: bool = False,
+    color: object | bool = _sentinel,
     allow_none: bool = False,
 ) -> bool:
-    """Determine if a single argument is an iterable. If a color is being
-    provided and the argument is a 1-D array of length 3 or 4 then the input
-    is taken to not be iterable. If allow_none is True, `None` is considered iterable.
+    """Determine if a single argument is an iterable.
+    Argument color is deprecated since version 0.5.0 and will be removed in 0.6.0.
     """
+    # deprecate color
+    if color is not _sentinel:
+        warnings.warn(
+            trans._(
+                'Argument color is deprecated since version 0.5.0 and will be removed in 0.6.0.',
+            ),
+            category=DeprecationWarning,
+            stacklevel=2,  # not sure what level to use here
+        )
+
     if arg is None:
         return allow_none
-    if isinstance(arg, (str, Enum)):
-        return False
-    if np.isscalar(arg):
+
+    # Here if arg is None it used to return allow_none
+    if isinstance(arg, (str, Enum)) or np.isscalar(arg):
         return False
-    if color and isinstance(arg, (list, np.ndarray)):
+
+    # this is to be removed in 0.6.0, coloer is never set True
+    if color is True and isinstance(arg, (list, np.ndarray)):
         return np.array(arg).ndim != 1 or len(arg) not in [3, 4]
 
     return isinstance(arg, collections.abc.Iterable)


Test patch: