Repository: python-visualization/folium

Issue:
Gradient parameter in heatmap in newest version can't work-'float' object has no attribute 'split'
The gradient parameter in heatmap seems can't work in version since`0.19.0`. To reproduce, use the following code, or in [colab](https://colab.research.google.com/drive/1NA9FW8e8MXFvePm6Zi59kEn9AxchAV5x?usp=sharing)

```python

data = (
    np.random.normal(size=(100, 3)) * np.array([[1, 1, 1]]) + np.array([[48, 5, 1]])
).tolist()

import folium
from folium.plugins import HeatMap

m = folium.Map([48.0, 5.0], zoom_start=6)

HeatMap(data,
        gradient = {0.4: "blue", 0.6: "cyan",
                    0.7: "lime", 0.8: "yellow", 1: "red"}
        ).add_to(m)

m

```
In newest version `0.19.4` the error is show below,

```
AttributeError                            Traceback (most recent call last)
[/usr/local/lib/python3.11/dist-packages/IPython/core/formatters.py](https://localhost:8080/#) in __call__(self, obj)
    343             method = get_real_method(obj, self.print_method)
    344             if method is not None:
--> 345                 return method()
    346             return None
    347         else:

13 frames
<template> in macro(l_1_this, l_1_kwargs)

[/usr/local/lib/python3.11/dist-packages/folium/utilities.py](https://localhost:8080/#) in camelize(key)
    347     'variableName'
    348     """
--> 349     return "".join(x.capitalize() if i > 0 else x for i, x in enumerate(key.split("_")))
    350 
    351 
AttributeError: 'float' object has no attribute 'split'
<folium.folium.Map at 0x7dff47d57990>
```

Discussion:

Patch:
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index f1829174..c7d6ae4d 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -15,7 +15,7 @@ repos:
       files: requirements-dev.txt
 
 - repo: https://github.com/astral-sh/ruff-pre-commit
-  rev: v0.9.4
+  rev: v0.9.9
   hooks:
     - id: ruff
 
diff --git a/folium/template.py b/folium/template.py
index 4dcd01f0..1d3cb6aa 100644
--- a/folium/template.py
+++ b/folium/template.py
@@ -15,7 +15,10 @@ def tojavascript(obj: Union[str, JsCode, dict, list, Element]) -> str:
     elif isinstance(obj, dict):
         out = ["{\n"]
         for key, value in obj.items():
-            out.append(f'  "{camelize(key)}": ')
+            if isinstance(key, str):
+                out.append(f'  "{camelize(key)}": ')
+            else:
+                out.append(f"  {key}: ")
             out.append(tojavascript(value))
             out.append(",\n")
         out.append("}")
diff --git a/requirements-dev.txt b/requirements-dev.txt
index ead0a887..3834e1e8 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -32,7 +32,7 @@ pytest
 scipy
 selenium<4.27.0
 setuptools_scm
-sphinx==7.3.7
+sphinx
 types-requests
 vega_datasets
 vincent


Test patch:
diff --git a/tests/test_template.py b/tests/test_template.py
index c91c75ac..09c57ce4 100644
--- a/tests/test_template.py
+++ b/tests/test_template.py
@@ -19,6 +19,12 @@ def test_tojavascript_with_dict():
     assert tojavascript(dict_obj) == '{\n  "key": "value",\n}'
 
 
+def test_tojavascript_with_dict_with_mixed_key_types():
+    dict_obj = {"key": "value", 1: "another value", 3.14: "pi"}
+    expected = '{\n  "key": "value",\n  1: "another value",\n  3.14: "pi",\n}'
+    assert tojavascript(dict_obj) == expected
+
+
 def test_tojavascript_with_list():
     list_obj = ["value1", "value2"]
     assert tojavascript(list_obj) == '[\n"value1",\n"value2",\n]'