Repository: scipy/scipy

Issue:
Deprecate `scipy.spatial.distance.kulsinski`
`kulsinski` has been superseded by `kulczynski1` in #14588.

As per https://github.com/scipy/scipy/issues/2009#issuecomment-898674427_, we should deprecate `kulsinski` starting with 1.9.


Discussion:
Can I work on this issue?
Hi @deepakdinesh1123 thanks for the proposal. You can start but we need to wait for the branching for SciPy 1.8. It should happen next week.
Thank you very much for giving me an opportunity to contribute. 
So I should deprecate the method and refactor the code so that all occurrences of the kulsinski method are replaced by  kulczynski1 right?
Pleasure 😃. Yes that's what's need to be done. In the main init (and other places) there are examples of decorator usage on how to deprecate something.

Resolving patch:
diff --git a/scipy/spatial/distance.py b/scipy/spatial/distance.py
--- a/scipy/spatial/distance.py
+++ b/scipy/spatial/distance.py
@@ -123,6 +123,7 @@
 
 from . import _distance_pybind
 
+from .._lib.deprecation import _deprecated
 
 def _copy_array_if_base_present(a):
     """Copy the array if its base points to a parent array."""
@@ -796,6 +797,9 @@ def jaccard(u, v, w=None):
     return (a / b) if b != 0 else 0
 
 
+@_deprecated("Kulsinski has been deprecated from scipy.spatial.distance"
+             " in SciPy 1.9.0 and it will be removed in SciPy 1.11.0."
+             " It is superseded by scipy.spatial.distance.kulczynski1.")
 def kulsinski(u, v, w=None):
     """
     Compute the Kulsinski dissimilarity between two boolean 1-D arrays.
@@ -812,6 +816,12 @@ def kulsinski(u, v, w=None):
     :math:`\\mathtt{u[k]} = i` and :math:`\\mathtt{v[k]} = j` for
     :math:`k < n`.
 
+    .. deprecated:: 0.12.0
+
+    Kulsinski has been deprecated from scipy.spatial.distance in SciPy 1.9.0
+    and it will be removed in SciPy 1.11.0. It is superseded by
+    scipy.spatial.distance.kulczynski1.
+
     Parameters
     ----------
     u : (N,) array_like, bool


Test patch:
diff --git a/scipy/spatial/tests/test_distance.py b/scipy/spatial/tests/test_distance.py
--- a/scipy/spatial/tests/test_distance.py
+++ b/scipy/spatial/tests/test_distance.py
@@ -398,18 +398,22 @@ def _my_metric(x, y, arg, kwarg=1, kwarg2=2):
         kwargs = {'N0tV4l1D_p4raM': 3.14, "w":np.arange(3)}
         args = [3.14] * 200
         for metric in _METRICS_NAMES:
-            assert_raises(TypeError, cdist, X1, X2,
-                          metric=metric, **kwargs)
-            assert_raises(TypeError, cdist, X1, X2,
-                          metric=eval(metric), **kwargs)
-            assert_raises(TypeError, cdist, X1, X2,
-                          metric="test_" + metric, **kwargs)
-            assert_raises(TypeError, cdist, X1, X2,
-                          metric=metric, *args)
-            assert_raises(TypeError, cdist, X1, X2,
-                          metric=eval(metric), *args)
-            assert_raises(TypeError, cdist, X1, X2,
-                          metric="test_" + metric, *args)
+            with np.testing.suppress_warnings() as sup:
+                if metric == "kulsinski":
+                    sup.filter(DeprecationWarning,
+                               "Kulsinski has been deprecated from")
+                assert_raises(TypeError, cdist, X1, X2,
+                              metric=metric, **kwargs)
+                assert_raises(TypeError, cdist, X1, X2,
+                              metric=eval(metric), **kwargs)
+                assert_raises(TypeError, cdist, X1, X2,
+                              metric="test_" + metric, **kwargs)
+                assert_raises(TypeError, cdist, X1, X2,
+                              metric=metric, *args)
+                assert_raises(TypeError, cdist, X1, X2,
+                              metric=eval(metric), *args)
+                assert_raises(TypeError, cdist, X1, X2,
+                              metric="test_" + metric, *args)
 
         assert_raises(TypeError, cdist, X1, X2, _my_metric)
         assert_raises(TypeError, cdist, X1, X2, _my_metric, *args)
@@ -528,7 +532,11 @@ def test_cdist_calling_conventions(self):
                               'kulczynski1'} and 'bool' not in eo_name:
                     # python version permits non-bools e.g. for fuzzy logic
                     continue
-                self._check_calling_conventions(X1, X2, metric)
+                with np.testing.suppress_warnings() as sup:
+                    if metric == "kulsinski":
+                        sup.filter(DeprecationWarning,
+                                   "Kulsinski has been deprecated from")
+                    self._check_calling_conventions(X1, X2, metric)
 
                 # Testing built-in metrics with extra args
                 if metric == "seuclidean":
@@ -669,15 +677,19 @@ def _my_metric(x, y, arg, kwarg=1, kwarg2=2):
         kwargs = {'N0tV4l1D_p4raM': 3.14, "w":np.arange(2)}
         args = [3.14] * 200
         for metric in _METRICS_NAMES:
-            assert_raises(TypeError, pdist, X1, metric=metric, **kwargs)
-            assert_raises(TypeError, pdist, X1,
-                          metric=eval(metric), **kwargs)
-            assert_raises(TypeError, pdist, X1,
-                          metric="test_" + metric, **kwargs)
-            assert_raises(TypeError, pdist, X1, metric=metric, *args)
-            assert_raises(TypeError, pdist, X1, metric=eval(metric), *args)
-            assert_raises(TypeError, pdist, X1,
-                          metric="test_" + metric, *args)
+            with np.testing.suppress_warnings() as sup:
+                if metric == "kulsinski":
+                    sup.filter(DeprecationWarning,
+                               "Kulsinski has been deprecated from")
+                assert_raises(TypeError, pdist, X1, metric=metric, **kwargs)
+                assert_raises(TypeError, pdist, X1,
+                              metric=eval(metric), **kwargs)
+                assert_raises(TypeError, pdist, X1,
+                              metric="test_" + metric, **kwargs)
+                assert_raises(TypeError, pdist, X1, metric=metric, *args)
+                assert_raises(TypeError, pdist, X1, metric=eval(metric), *args)
+                assert_raises(TypeError, pdist, X1,
+                              metric="test_" + metric, *args)
 
         assert_raises(TypeError, pdist, X1, _my_metric)
         assert_raises(TypeError, pdist, X1, _my_metric, *args)
@@ -1427,7 +1439,11 @@ def test_pdist_calling_conventions(self):
                               'kulczynski1'} and 'bool' not in eo_name:
                     # python version permits non-bools e.g. for fuzzy logic
                     continue
-                self._check_calling_conventions(X, metric)
+                with np.testing.suppress_warnings() as sup:
+                    if metric == "kulsinski":
+                        sup.filter(DeprecationWarning,
+                                   "Kulsinski has been deprecated from")
+                    self._check_calling_conventions(X, metric)
 
                 # Testing built-in metrics with extra args
                 if metric == "seuclidean":
@@ -2072,10 +2088,13 @@ def test_Xdist_non_negative_weights():
     for metric in _METRICS_NAMES:
         if metric in ['seuclidean', 'mahalanobis', 'jensenshannon']:
             continue
-
-        for m in [metric, eval(metric), "test_" + metric]:
-            assert_raises(ValueError, pdist, X, m, w=w)
-            assert_raises(ValueError, cdist, X, X, m, w=w)
+        with np.testing.suppress_warnings() as sup:
+            if metric == "kulsinski":
+                sup.filter(DeprecationWarning,
+                           "Kulsinski has been deprecated from")
+            for m in [metric, eval(metric), "test_" + metric]:
+                assert_raises(ValueError, pdist, X, m, w=w)
+                assert_raises(ValueError, cdist, X, X, m, w=w)
 
 
 def test__validate_vector():
@@ -2147,3 +2166,11 @@ def test_jensenshannon():
                         [0.1954288, 0.1447697, 0.1138377, 0.0927636])
     assert_almost_equal(jensenshannon(a, b, axis=1),
                         [0.1402339, 0.0399106, 0.0201815])
+
+
+def test_kulsinski_deprecation():
+    msg = ("Kulsinski has been deprecated from scipy.spatial.distance"
+           " in SciPy 1.9.0 and it will be removed in SciPy 1.11.0."
+           " It is superseded by scipy.spatial.distance.kulczynski1.")
+    with pytest.warns(DeprecationWarning, match=msg):
+        kulsinski([], [])