Repository: dask-contrib/dask-expr (python)

Issue:
RecursionError after assigning empty list of columns
Ran into this during some unittesting. Although a bit odd, this code runs fine with query-planning disabled
```python
import dask
dask.config.set({'dataframe.query-planning': False})
import dask.dataframe as dd
import pandas as pd

ddf = dd.from_pandas(pd.DataFrame({'a': [1]}), npartitions=1)
print(ddf.columns)
ddf[[]] = ddf[[]].astype(str)
print(ddf.columns)
```
>Index(['a'], dtype='object')
>Index(['a'], dtype='object')

But with query-planning enabled, it throws a recursion error when trying to access the columns the second time.
```python
import dask
dask.config.set({'dataframe.query-planning': True})
import dask.dataframe as dd
import pandas as pd

ddf = dd.from_pandas(pd.DataFrame({'a': [1]}), npartitions=1)
print(ddf.columns)
ddf[[]] = ddf[[]].astype(str)
print(ddf.columns)
```
>Index(['a'], dtype='object')
Traceback (most recent call last):
  File ".../dev.py", line 9, in <module>
    print(ddf.columns)
  File ".../python3.10/site-packages/dask_expr/_collection.py", line 350, in columns
    return self._meta.columns
  File ".../python3.10/site-packages/dask_expr/_collection.py", line 297, in _meta
    return self.expr._meta
  File ".../python3.10/site-packages/dask_expr/_collection.py", line 297, in _meta
    return self.expr._meta
  File ".../python3.10/site-packages/dask_expr/_collection.py", line 297, in _meta
    return self.expr._meta
  [Previous line repeated 995 more times]
RecursionError: maximum recursion depth exceeded

Versions:
* dask == 2024.2.1
* dask-expr == 0.5.2
* pandas == 2.1.4

Pull request description:
Fix assign for empty indexer
closes #900

Patch:
diff --git a/dask_expr/_collection.py b/dask_expr/_collection.py
index 2094e7d..2d697a7 100644
--- a/dask_expr/_collection.py
+++ b/dask_expr/_collection.py
@@ -2455,9 +2455,9 @@ class DataFrame(FrameBase):
             args.extend([k, v])
 
         if len(args) > 0:
-            result = expr.Assign(result, *args)
+            result = new_collection(expr.Assign(result, *args))
 
-        return new_collection(result)
+        return result
 
     @derived_from(pd.DataFrame)
     def clip(self, lower=None, upper=None, axis=None, **kwargs):


Test patch:
diff --git a/dask_expr/tests/test_collection.py b/dask_expr/tests/test_collection.py
index c113432..4e317b5 100644
--- a/dask_expr/tests/test_collection.py
+++ b/dask_expr/tests/test_collection.py
@@ -270,6 +270,13 @@ def test_filter_pushdown_reducer_in_predicate():
     assert q.expr.frame.frame.frame.columns == ["a", "b"]
 
 
+def test_assign_empty_columns(df, pdf):
+    df[[]] = df[[]].astype("float64")
+    assert list(df.columns) == list(pdf.columns)
+    pdf[[]] = pdf[[]].astype("float64")
+    assert_eq(df, pdf)
+
+
 def test_index_divisions():
     df = pd.DataFrame({"x": [1, 2, 3, 4, 5]})
     ddf = from_pandas(df, npartitions=2)