Repository: jupyter/jupyter_client (python)

Issue:
Cannot handle message data with date type
Example using plotly 6:

```python
from datetime import date, datetime
import polars as pl
import plotly.express as px
from plotly.graph_objs import FigureWidget

df=pl.DataFrame({'x': [date(2025, 1, 1), date(2025, 1, 2)], 'y': [1, 2]})
fig=px.line(df, x='x', y='y')

fig.show() # renders correctly

FigureWidget(fig).show() # fails
```

The last line results in:

```
ValueError: Can't clean for JSON: datetime.date(2025, 1, 1)
```

coming from
```
File ~/dev/jupyter_client/jupyter_client/jsonutil.py:195, in json_clean(obj)
    192     return obj.strftime(ISO8601)
    194 # we don't understand it, it's probably an unserializable object
--> 195 raise ValueError("Can't clean for JSON: %r" % obj)
```


Pull request description:
Support date serialization in messages
Resolves #1058.

fwiw the (original?) copy of json_clean in https://github.com/ipython/ipykernel/blob/2c212648775646f1c92e8f3e977806dea14186da/ipykernel/jsonutil.py#L160 has date support.

Patch:
diff --git a/jupyter_client/jsonutil.py b/jupyter_client/jsonutil.py
index 6e6bac8..157fc4f 100644
--- a/jupyter_client/jsonutil.py
+++ b/jupyter_client/jsonutil.py
@@ -8,7 +8,7 @@ import types
 import warnings
 from binascii import b2a_base64
 from collections.abc import Iterable
-from datetime import datetime
+from datetime import date, datetime
 from typing import Any, Optional, Union
 
 from dateutil.parser import isoparse as _dateutil_parse
@@ -110,6 +110,9 @@ def json_default(obj: Any) -> Any:
         obj = _ensure_tzinfo(obj)
         return obj.isoformat().replace("+00:00", "Z")
 
+    if isinstance(obj, date):
+        return obj.isoformat()
+
     if isinstance(obj, bytes):
         return b2a_base64(obj, newline=False).decode("ascii")
 
@@ -185,7 +188,7 @@ def json_clean(obj: Any) -> Any:
             out[str(k)] = json_clean(v)
         return out
 
-    if isinstance(obj, datetime):
+    if isinstance(obj, (datetime, date)):
         return obj.strftime(ISO8601)
 
     # we don't understand it, it's probably an unserializable object


Test patch:
diff --git a/tests/test_jsonutil.py b/tests/test_jsonutil.py
index 992eb87..d7d9ae9 100644
--- a/tests/test_jsonutil.py
+++ b/tests/test_jsonutil.py
@@ -139,7 +139,7 @@ def test_parse_ms_precision():
             assert isinstance(parsed, str)
 
 
-def test_json_default_date():
+def test_json_default_datetime():
     naive = datetime.datetime.now()  # noqa
     local = tzoffset("Local", -8 * 3600)
     other = tzoffset("Other", 2 * 3600)
@@ -178,6 +178,7 @@ def test_json_default():
         (iter([1, 2]), [1, 2]),
         (MyFloat(), 3.14),
         (MyInt(), 389),
+        (datetime.date(2025, 4, 8), "2025-04-08"),
     ]
 
     for val, jval in pairs: