Repository: BlueBrain/NeuroM

Issue:
Inappropriate `warnings.simplefilter`
in `neurom.utils.warn_deprecated` `warnings.simplefilter` is used to enforce that a deprecation warning is shown:
https://github.com/BlueBrain/NeuroM/blob/c570a2faa18f4e417fcdb743f6487beee0c60c3e/neurom/utils.py#L40-L44

This is problematic for several reasons
* It's not the business of a library to configure how the warnings are shown
* There is no way for the library user to easily disable those warnings
* By calling `warnings.simplefilter` `neurom` modifies the global state and has the side-effect of permanently setting ` warnings.simplefilter('default', DeprecationWarning)`. E.g
  ```py
  import warnings
  import neurom as nm
  
  print("before:", warnings.filters)
  nm.load_neuron(some_path)  # this triggers the warning `neurom.io.utils.load_neuron` is deprecated
  print("after:", warnings.filters)
  ```
  output:
  ```
  before: []
  after: [('default', None, <class 'DeprecationWarning'>, None, 0), ('always', None, <class 'DeprecationWarning'>, None, 0)]
  ```

I would strongly advocate for removing the two `warnings.simplefilter` lines.

P.S.: since `warnings.warn` is wrapped into `warn_deprecated` setting `stacklevel=3` should be considered.

Comments:

Patch:
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 4d0b572..21e19fe 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -4,6 +4,7 @@ Changelog
 Version 3.2.0
 -------------
 
+- Fix warning system so that it doesn't change the pre-existing warnings configuration
 - Fix ``neurom.features.bifurcation.partition_asymmetry`` Uylings variant to not throw
   for bifurcations with leaves.
 - Fix ``neurom.features.neurite.principal_direction_extents`` to remove duplicate points
diff --git a/neurom/__init__.py b/neurom/__init__.py
index 90290b5..2b2f24a 100644
--- a/neurom/__init__.py
+++ b/neurom/__init__.py
@@ -55,7 +55,6 @@ Examples:
     >>> mapping = lambda n : len(n.points)
     >>> n_points = [n for n in nm.iter_neurites(pop, mapping, filter)]
 """
-
 from neurom.core.dataformat import COLS
 from neurom.core.types import NeuriteType, NeuriteIter, NEURITES as NEURITE_TYPES
 from neurom.core.morphology import graft_morphology, iter_neurites, iter_sections, iter_segments
@@ -63,6 +62,7 @@ from neurom.core.morphology import graft_morphology, iter_neurites, iter_section
 from neurom.features import get
 from neurom.io.utils import MorphLoader, load_morphology, load_morphologies
 from neurom.io.utils import load_neuron, load_neurons
+from neurom.exceptions import NeuroMDeprecationWarning
 
 APICAL_DENDRITE = NeuriteType.apical_dendrite
 BASAL_DENDRITE = NeuriteType.basal_dendrite
diff --git a/neurom/exceptions.py b/neurom/exceptions.py
index fac38b2..7eba4c8 100644
--- a/neurom/exceptions.py
+++ b/neurom/exceptions.py
@@ -35,3 +35,7 @@ class NeuroMError(Exception):
 
 class ConfigError(NeuroMError):
     """Exception class for configuration data in apps errors."""
+
+
+class NeuroMDeprecationWarning(DeprecationWarning):
+    """NeuroM deprecation warning for users."""
diff --git a/neurom/utils.py b/neurom/utils.py
index 5ccf9a8..e8786f3 100644
--- a/neurom/utils.py
+++ b/neurom/utils.py
@@ -35,13 +35,12 @@ from functools import wraps
 import numpy as np
 
 from neurom.core.dataformat import COLS
+from neurom.exceptions import NeuroMDeprecationWarning
 
 
 def warn_deprecated(msg):
     """Issue a deprecation warning."""
-    warnings.simplefilter('always', DeprecationWarning)
-    warnings.warn(msg, category=DeprecationWarning, stacklevel=2)
-    warnings.simplefilter('default', DeprecationWarning)
+    warnings.warn(msg, category=NeuroMDeprecationWarning, stacklevel=3)
 
 
 def deprecated(fun_name=None, msg=""):


Test patch:
diff --git a/tests/test_utils.py b/tests/test_utils.py
index f01a172..a8dd018 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -29,11 +29,20 @@
 """Test neurom.utils."""
 import json
 import warnings
+from copy import deepcopy
 
 import numpy as np
 from neurom import utils as nu
 import pytest
 
+from neurom.exceptions import NeuroMDeprecationWarning
+
+
+def test_warn_deprecated():
+
+    with pytest.warns(NeuroMDeprecationWarning, match="foo"):
+        nu.warn_deprecated(msg="foo")
+
 
 def test_deprecated():
     @nu.deprecated(msg='Hello')