Repository: dask/dask

Issue:
plan_rechunk sometimes adds a no-op step
I noticed this in production code, but it can be demonstrated by adding a test to `dask/array/tests/test_rechunk.py::test_plan_rechunk`:

```python
    steps = _plan((c, c), (f, f), threshold=1)
    _assert_steps(steps, [(f, f)])
```
The actual plan that's recreated is [(c, c), (f, f)] i.e. the first step is to change nothing.

I'll submit a PR to avoid performing this step.

Discussion:

Patch:
diff --git a/dask/array/rechunk.py b/dask/array/rechunk.py
index 9e71b7d8e..c39ebbd97 100644
--- a/dask/array/rechunk.py
+++ b/dask/array/rechunk.py
@@ -511,7 +511,8 @@ def plan_rechunk(
         )
         if (chunks == current_chunks and not first_pass) or chunks == new_chunks:
             break
-        steps.append(chunks)
+        if chunks != current_chunks:
+            steps.append(chunks)
         current_chunks = chunks
         if not memory_limit_hit:
             break
diff --git a/dask/core.py b/dask/core.py
index 59d9fd921..153c39a4d 100644
--- a/dask/core.py
+++ b/dask/core.py
@@ -463,6 +463,6 @@ def quote(x):
     >>> quote((add, 1, 2))  # doctest: +SKIP
     (literal<type=tuple>,)
     """
-    if istask(x) or type(x) is list:
+    if istask(x) or type(x) is list or type(x) is dict:
         return (literal(x),)
     return x


Test patch:
diff --git a/dask/array/tests/test_rechunk.py b/dask/array/tests/test_rechunk.py
index a3a775a7a..3ae128eee 100644
--- a/dask/array/tests/test_rechunk.py
+++ b/dask/array/tests/test_rechunk.py
@@ -280,9 +280,13 @@ def test_merge_to_number():
     assert chunks == (9,)
 
 
-def _plan(old_chunks, new_chunks, itemsize=1, block_size_limit=1e7):
+def _plan(old_chunks, new_chunks, itemsize=1, block_size_limit=1e7, threshold=4):
     return plan_rechunk(
-        old_chunks, new_chunks, itemsize=itemsize, block_size_limit=block_size_limit
+        old_chunks,
+        new_chunks,
+        itemsize=itemsize,
+        block_size_limit=block_size_limit,
+        threshold=threshold,
     )
 
 
@@ -326,6 +330,10 @@ def test_plan_rechunk():
     steps = _plan((nc + nf, c + c, c + f), (nc + nf, f + f, c + c))
     _assert_steps(steps, steps)
 
+    # Regression test for #5908
+    steps = _plan((c, c), (f, f), threshold=1)
+    _assert_steps(steps, [(f, f)])
+
     # Just at the memory limit => an intermediate is used
     steps = _plan((f, c), (c, f), block_size_limit=400)
     _assert_steps(steps, [(c, c), (c, f)])
diff --git a/dask/tests/test_core.py b/dask/tests/test_core.py
index 8ebe0acd3..1f8eacb88 100644
--- a/dask/tests/test_core.py
+++ b/dask/tests/test_core.py
@@ -250,7 +250,7 @@ def test_subs_unexpected_hashable_key():
 
 
 def test_quote():
-    literals = [[1, 2, 3], (add, 1, 2), [1, [2, 3]], (add, 1, (add, 2, 3))]
+    literals = [[1, 2, 3], (add, 1, 2), [1, [2, 3]], (add, 1, (add, 2, 3)), {"x": "x"}]
 
     for l in literals:
         assert core.get({"x": quote(l)}, "x") == l