Repository: spectrochempy/spectrochempy

Issue:
[Bug]: serialization error  for tuple containing a ndarray
### What happened?

a json serialization error was raised when one passes a tuple containing a ndarray is passed  to scp objects, e.g. for MCRAL in the following example.



### What did you expect to happen?

nadarray in tuples opr lists should be correctly serialized.

### Minimal Complete Verifiable Example

```python
mcr = scp.MCRALS()
mcr.argsGetConc = (np.array(0.0),)
```


### Relevant log output

```python
Traceback (most recent call last):
  File "C:\Miniconda3\envs\scp-s-12\Lib\site-packages\IPython\core\interactiveshell.py", line 3577, in run_code
    exec(code_obj, self.user_global_ns, self.user_ns)
  File "<ipython-input-5-3b1f2d849601>", line 2, in <module>
    mcr.argsGetConc = (np.array(0.0),)
    ^^^^^^^^^^^^^^^
  File "C:\Miniconda3\envs\scp-s-12\Lib\site-packages\traitlets\traitlets.py", line 716, in __set__
    self.set(obj, value)
  File "C:\Miniconda3\envs\scp-s-12\Lib\site-packages\traitlets\traitlets.py", line 706, in set
    obj._notify_trait(self.name, old_value, new_value)
  File "C:\Miniconda3\envs\scp-s-12\Lib\site-packages\traitlets\traitlets.py", line 1513, in _notify_trait
    self.notify_change(
  File "C:\Miniconda3\envs\scp-s-12\Lib\site-packages\traitlets\traitlets.py", line 1525, in notify_change
    return self._notify_observers(change)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Miniconda3\envs\scp-s-12\Lib\site-packages\traitlets\traitlets.py", line 1568, in _notify_observers
    c(event)
  File "C:\Miniconda3\envs\scp-s-12\Lib\site-packages\spectrochempy\utils\metaconfigurable.py", line 179, in _anytrait_changed
    self.cfg.update(
  File "C:\Miniconda3\envs\scp-s-12\Lib\site-packages\traitlets\config\manager.py", line 81, in update
    data = self.get(section_name)
           ^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Miniconda3\envs\scp-s-12\Lib\site-packages\traitlets\config\manager.py", line 64, in get
    return json.load(f)
           ^^^^^^^^^^^^
  File "C:\Miniconda3\envs\scp-s-12\Lib\json\__init__.py", line 293, in load
    return loads(fp.read(),
           ^^^^^^^^^^^^^^^^
  File "C:\Miniconda3\envs\scp-s-12\Lib\json\__init__.py", line 346, in loads
    return _default_decoder.decode(s)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Miniconda3\envs\scp-s-12\Lib\json\decoder.py", line 337, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Miniconda3\envs\scp-s-12\Lib\json\decoder.py", line 355, in raw_decode
    raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 60 column 7 (char 1947)
mcr = scp.MCRALS()
mcr.argsGetConc = (np.array(0.0),)
Traceback (most recent call last):
  File "C:\Miniconda3\envs\scp-s-12\Lib\site-packages\IPython\core\interactiveshell.py", line 3577, in run_code
    exec(code_obj, self.user_global_ns, self.user_ns)
  File "<ipython-input-6-3b1f2d849601>", line 2, in <module>
    mcr.argsGetConc = (np.array(0.0),)
    ^^^^^^^^^^^^^^^
  File "C:\Miniconda3\envs\scp-s-12\Lib\site-packages\traitlets\traitlets.py", line 716, in __set__
    self.set(obj, value)
  File "C:\Miniconda3\envs\scp-s-12\Lib\site-packages\traitlets\traitlets.py", line 706, in set
    obj._notify_trait(self.name, old_value, new_value)
  File "C:\Miniconda3\envs\scp-s-12\Lib\site-packages\traitlets\traitlets.py", line 1513, in _notify_trait
    self.notify_change(
  File "C:\Miniconda3\envs\scp-s-12\Lib\site-packages\traitlets\traitlets.py", line 1525, in notify_change
    return self._notify_observers(change)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Miniconda3\envs\scp-s-12\Lib\site-packages\traitlets\traitlets.py", line 1568, in _notify_observers
    c(event)
  File "C:\Miniconda3\envs\scp-s-12\Lib\site-packages\spectrochempy\utils\metaconfigurable.py", line 179, in _anytrait_changed
    self.cfg.update(
  File "C:\Miniconda3\envs\scp-s-12\Lib\site-packages\traitlets\config\manager.py", line 83, in update
    self.set(section_name, data)
  File "C:\Miniconda3\envs\scp-s-12\Lib\site-packages\traitlets\config\manager.py", line 74, in set
    json.dump(data, f, indent=2)
  File "C:\Miniconda3\envs\scp-s-12\Lib\json\__init__.py", line 179, in dump
    for chunk in iterable:
  File "C:\Miniconda3\envs\scp-s-12\Lib\json\encoder.py", line 432, in _iterencode
    yield from _iterencode_dict(o, _current_indent_level)
  File "C:\Miniconda3\envs\scp-s-12\Lib\json\encoder.py", line 406, in _iterencode_dict
    yield from chunks
  File "C:\Miniconda3\envs\scp-s-12\Lib\json\encoder.py", line 406, in _iterencode_dict
    yield from chunks
  File "C:\Miniconda3\envs\scp-s-12\Lib\json\encoder.py", line 326, in _iterencode_list
    yield from chunks
  File "C:\Miniconda3\envs\scp-s-12\Lib\json\encoder.py", line 439, in _iterencode
    o = _default(o)
        ^^^^^^^^^^^
  File "C:\Miniconda3\envs\scp-s-12\Lib\json\encoder.py", line 180, in default
    raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type ndarray is not JSON serializable
```


### Anything else we need to know?

_No response_

### Environment

_
[Bug]: serialization error  for tuple containing a ndarray
### What happened?

a json serialization error was raised when one passes a tuple containing a ndarray is passed  to scp objects, e.g. for MCRAL in the following example.



### What did you expect to happen?

nadarray in tuples opr lists should be correctly serialized.

### Minimal Complete Verifiable Example

```python
mcr = scp.MCRALS()
mcr.argsGetConc = (np.array(0.0),)
```


### Relevant log output

```python
Traceback (most recent call last):
  File "C:\Miniconda3\envs\scp-s-12\Lib\site-packages\IPython\core\interactiveshell.py", line 3577, in run_code
    exec(code_obj, self.user_global_ns, self.user_ns)
  File "<ipython-input-5-3b1f2d849601>", line 2, in <module>
    mcr.argsGetConc = (np.array(0.0),)
    ^^^^^^^^^^^^^^^
  File "C:\Miniconda3\envs\scp-s-12\Lib\site-packages\traitlets\traitlets.py", line 716, in __set__
    self.set(obj, value)
  File "C:\Miniconda3\envs\scp-s-12\Lib\site-packages\traitlets\traitlets.py", line 706, in set
    obj._notify_trait(self.name, old_value, new_value)
  File "C:\Miniconda3\envs\scp-s-12\Lib\site-packages\traitlets\traitlets.py", line 1513, in _notify_trait
    self.notify_change(
  File "C:\Miniconda3\envs\scp-s-12\Lib\site-packages\traitlets\traitlets.py", line 1525, in notify_change
    return self._notify_observers(change)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Miniconda3\envs\scp-s-12\Lib\site-packages\traitlets\traitlets.py", line 1568, in _notify_observers
    c(event)
  File "C:\Miniconda3\envs\scp-s-12\Lib\site-packages\spectrochempy\utils\metaconfigurable.py", line 179, in _anytrait_changed
    self.cfg.update(
  File "C:\Miniconda3\envs\scp-s-12\Lib\site-packages\traitlets\config\manager.py", line 81, in update
    data = self.get(section_name)
           ^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Miniconda3\envs\scp-s-12\Lib\site-packages\traitlets\config\manager.py", line 64, in get
    return json.load(f)
           ^^^^^^^^^^^^
  File "C:\Miniconda3\envs\scp-s-12\Lib\json\__init__.py", line 293, in load
    return loads(fp.read(),
           ^^^^^^^^^^^^^^^^
  File "C:\Miniconda3\envs\scp-s-12\Lib\json\__init__.py", line 346, in loads
    return _default_decoder.decode(s)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Miniconda3\envs\scp-s-12\Lib\json\decoder.py", line 337, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Miniconda3\envs\scp-s-12\Lib\json\decoder.py", line 355, in raw_decode
    raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 60 column 7 (char 1947)
mcr = scp.MCRALS()
mcr.argsGetConc = (np.array(0.0),)
Traceback (most recent call last):
  File "C:\Miniconda3\envs\scp-s-12\Lib\site-packages\IPython\core\interactiveshell.py", line 3577, in run_code
    exec(code_obj, self.user_global_ns, self.user_ns)
  File "<ipython-input-6-3b1f2d849601>", line 2, in <module>
    mcr.argsGetConc = (np.array(0.0),)
    ^^^^^^^^^^^^^^^
  File "C:\Miniconda3\envs\scp-s-12\Lib\site-packages\traitlets\traitlets.py", line 716, in __set__
    self.set(obj, value)
  File "C:\Miniconda3\envs\scp-s-12\Lib\site-packages\traitlets\traitlets.py", line 706, in set
    obj._notify_trait(self.name, old_value, new_value)
  File "C:\Miniconda3\envs\scp-s-12\Lib\site-packages\traitlets\traitlets.py", line 1513, in _notify_trait
    self.notify_change(
  File "C:\Miniconda3\envs\scp-s-12\Lib\site-packages\traitlets\traitlets.py", line 1525, in notify_change
    return self._notify_observers(change)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Miniconda3\envs\scp-s-12\Lib\site-packages\traitlets\traitlets.py", line 1568, in _notify_observers
    c(event)
  File "C:\Miniconda3\envs\scp-s-12\Lib\site-packages\spectrochempy\utils\metaconfigurable.py", line 179, in _anytrait_changed
    self.cfg.update(
  File "C:\Miniconda3\envs\scp-s-12\Lib\site-packages\traitlets\config\manager.py", line 83, in update
    self.set(section_name, data)
  File "C:\Miniconda3\envs\scp-s-12\Lib\site-packages\traitlets\config\manager.py", line 74, in set
    json.dump(data, f, indent=2)
  File "C:\Miniconda3\envs\scp-s-12\Lib\json\__init__.py", line 179, in dump
    for chunk in iterable:
  File "C:\Miniconda3\envs\scp-s-12\Lib\json\encoder.py", line 432, in _iterencode
    yield from _iterencode_dict(o, _current_indent_level)
  File "C:\Miniconda3\envs\scp-s-12\Lib\json\encoder.py", line 406, in _iterencode_dict
    yield from chunks
  File "C:\Miniconda3\envs\scp-s-12\Lib\json\encoder.py", line 406, in _iterencode_dict
    yield from chunks
  File "C:\Miniconda3\envs\scp-s-12\Lib\json\encoder.py", line 326, in _iterencode_list
    yield from chunks
  File "C:\Miniconda3\envs\scp-s-12\Lib\json\encoder.py", line 439, in _iterencode
    o = _default(o)
        ^^^^^^^^^^^
  File "C:\Miniconda3\envs\scp-s-12\Lib\json\encoder.py", line 180, in default
    raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type ndarray is not JSON serializable
```


### Anything else we need to know?

_No response_

### Environment

_


Discussion:

Resolving patch:
diff --git a/spectrochempy/utils/metaconfigurable.py b/spectrochempy/utils/metaconfigurable.py
--- a/spectrochempy/utils/metaconfigurable.py
+++ b/spectrochempy/utils/metaconfigurable.py
@@ -175,6 +175,7 @@ def _anytrait_changed(self, change):
                 # replace other serializable value by an equivalent
                 value = [str(v) if isinstance(v, Path) else v for v in value]
                 value = [str(v) if isinstance(v, Quantity) else v for v in value]
+                value = [v.tolist() if isinstance(v, np.ndarray) else v for v in value]
 
             self.cfg.update(
                 self.name,


Test patch: