Bug: AttributeError in `str_escape` when handling `numpy.int64` in `sklearn.tree._export.py` in `/sklearn/tree/_export.py`

### Describe the bug


When exporting a decision tree using `sklearn.tree.export_text()` (or other related functions), an AttributeError occurs if a `numpy.int64` value is passed to `_export.py` instead of a string.

```
  File "venv/lib/python3.10/site-packages/sklearn/tree/_export.py", line 311, in node_to_str
    feature = self.str_escape(feature)
  File "venv/lib/python3.10/site-packages/sklearn/tree/_export.py", line 581, in str_escape
    return string.replace('"', r"\"")
AttributeError: 'numpy.int64' object has no attribute 'replace'
```
Causes:
The function `str_escape(feature)` expects a string but receives a `numpy.int64` value.
`numpy.int64` does not have a .replace() method, causing an AttributeError. 

## Possible Fix:
Convert feature to a string before passing it to `str_escape()` in `_export.py`.
Modify line 581 in `_export.py`:

Before (causing error):
```
return string.replace('"', r"\"")
```
## After (fixing error):

```
return str(string).replace('"', r"\"")
```
This ensures that `feature` is always a string before calling `.replace()`.



### Steps/Code to Reproduce

This piece of code triggers the error:

```
import numpy as np
from sklearn.tree import DecisionTreeClassifier, export_graphviz

X = np.array([[0, 1], [1, 0], [1, 1], [0, 0]])
y = np.array([0, 1, 1, 0])

clf = DecisionTreeClassifier().fit(X, y)

feature_names = np.array([10, 20], dtype=np.int64)  # numpy.int64 feature names

# Debugging prints
print("Feature Names:", feature_names)
print("Feature Name Types:", [type(name) for name in feature_names])

# Attempt to trigger the error
export_graphviz(clf, out_file=None, feature_names=feature_names)
```

### Expected Results

A graph in PNG format. 

### Actual Results


```
AttributeError: 'numpy.int64' object has no attribute 'replace'
```

### Versions

```shell
System:
    python: 3.10.12 (main, Jan 17 2025, 14:35:34) [GCC 11.4.0]
executable: /usr/bin/python3
   machine: Linux-6.8.0-52-generic-x86_64-with-glibc2.35

Python dependencies:
      sklearn: 1.6.1
          pip: 22.0.2
   setuptools: 59.6.0
        numpy: 2.2.3
        scipy: 1.15.1
       Cython: 3.0.11
       pandas: None
   matplotlib: 3.5.1
       joblib: 1.4.2
threadpoolctl: 3.5.0

Built with OpenMP: True

threadpoolctl info:
       user_api: blas
   internal_api: openblas
    num_threads: 8
         prefix: libscipy_openblas
       filepath: lib/python3.10/site-packages/numpy.libs/libscipy_openblas64_-6bb31eeb.so
        version: 0.3.28
threading_layer: pthreads
   architecture: SkylakeX

       user_api: blas
   internal_api: openblas
    num_threads: 8
         prefix: libscipy_openblas
       filepath: lib/python3.10/site-packages/scipy.libs/libscipy_openblas-68440149.so
        version: 0.3.28
threading_layer: pthreads
   architecture: SkylakeX

       user_api: openmp
   internal_api: openmp
    num_threads: 8
         prefix: libgomp
       filepath: lib/python3.10/site-packages/scikit_learn.libs/libgomp-a34b3233.so.1.0.0
        version: None
```

COMMENT:
Hello!

I saw that PR #30839 was linked to this issue, but based on the discussion, it seems like the suggested fix hasn't been implemented yet. I also noticed there hasn’t been much progress in the past couple of weeks, so I was wondering if anyone is currently working on it.
If not, I'd be happy to submit a PR that implements the behavior suggested by @ogrisel . Let me know if you’d like me to proceed!

Thanks!

Labels: Bug, Needs Decision