[med-svn] [python-skbio] 03/03: Apply upstream patch dealing with new pandas version
Andreas Tille
tille at debian.org
Fri Oct 6 19:19:10 UTC 2017
This is an automated email from the git hooks/post-receive script.
tille pushed a commit to branch master
in repository python-skbio.
commit e49698c2544e3dc7f4e3731b9f2474b2126cadcf
Author: Andreas Tille <tille at debian.org>
Date: Fri Oct 6 21:12:57 2017 +0200
Apply upstream patch dealing with new pandas version
---
debian/changelog | 3 +-
debian/control | 2 +-
...ry-pick-upstream-fix-for-numpy-transition.patch | 2 -
...t-with-new-pandas-and-numpydoc-fix-deprec.patch | 499 +++++++++++++++++++++
debian/patches/series | 1 +
5 files changed, 503 insertions(+), 4 deletions(-)
diff --git a/debian/changelog b/debian/changelog
index c694f62..ab97b9b 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,7 +1,8 @@
python-skbio (0.5.1-3) UNRELEASED; urgency=medium
* Standards-Version: 4.1.1
- * Versioned Build-Depends: python3-pandas (>= 0.20)
+ * Versioned Build-Depends: python3-pandas (>= 0.19.2)
+ * Apply upstream patch dealing with new pandas version
Closes: #868962
-- Andreas Tille <tille at debian.org> Fri, 06 Oct 2017 20:52:31 +0200
diff --git a/debian/control b/debian/control
index f92c223..dc15dd5 100644
--- a/debian/control
+++ b/debian/control
@@ -20,7 +20,7 @@ Build-Depends: debhelper (>= 10),
python3-nose,
python3-numpy (>= 1:1.9.2),
python3-numpydoc,
- python3-pandas (>= 0.20),
+ python3-pandas (>= 0.19.2),
python3-scipy,
python3-setuptools,
python3-sphinx,
diff --git a/debian/patches/0003-Cherry-pick-upstream-fix-for-numpy-transition.patch b/debian/patches/0003-Cherry-pick-upstream-fix-for-numpy-transition.patch
index cc30ee2..7a4340a 100644
--- a/debian/patches/0003-Cherry-pick-upstream-fix-for-numpy-transition.patch
+++ b/debian/patches/0003-Cherry-pick-upstream-fix-for-numpy-transition.patch
@@ -6,8 +6,6 @@ Subject: Cherry-pick upstream fix for numpy transition
skbio/stats/composition.py | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
-diff --git a/skbio/stats/composition.py b/skbio/stats/composition.py
-index eb6b364..95f7820 100644
--- a/skbio/stats/composition.py
+++ b/skbio/stats/composition.py
@@ -973,8 +973,8 @@ def ancom(table, grouping,
diff --git a/debian/patches/0004-MAINT-compat-with-new-pandas-and-numpydoc-fix-deprec.patch b/debian/patches/0004-MAINT-compat-with-new-pandas-and-numpydoc-fix-deprec.patch
new file mode 100644
index 0000000..c8d44fb
--- /dev/null
+++ b/debian/patches/0004-MAINT-compat-with-new-pandas-and-numpydoc-fix-deprec.patch
@@ -0,0 +1,499 @@
+Author: Jai Ram Rideout <jai.rideout at gmail.com>
+Last-Update: Fri, 6 Oct 2017 11:40:08 -0700
+Origin: Upstream
+ https://github.com/biocore/scikit-bio/issues/1531
+Bug-Debian: https://bugs.debian.org/868962
+Subject: MAINT: compat with new pandas and numpydoc; fix deprecation
+ warnings (#1535)
+ .
+ Made the following compatibility updates to fix the longstanding Travis-CI failures and make the codebase a little more futureproof:
+ - The codebase is now compatible with pandas >=0.19.2, including the latest pandas release (0.20.3), which introduced a number of backwards-incompatible changes.
+ - Fixed doc build failures by unpinning numpydoc version (latest PyPI numpydoc release is now compatible with latest Sphinx release).
+ - Fixed all deprecation warnings coming from third-party packages -- most were pandas deprecations, and one was from `scipy.stats`.
+ - Fixed docs in a couple of places that weren't compatible with numpydoc.
+
+--- a/ci/pip_requirements.txt
++++ b/ci/pip_requirements.txt
+@@ -5,5 +5,5 @@ lockfile
+ CacheControl
+ git+git://github.com/sphinx-doc/sphinx.git
+ sphinx-bootstrap-theme
+-git+git://github.com/numpy/numpydoc.git@1a848331c2cf53d4fe356f4607799524bcc577ed
++numpydoc
+ check-manifest
+--- a/doc/source/conf.py
++++ b/doc/source/conf.py
+@@ -70,20 +70,7 @@ class NewAuto(autosummary.Autosummary):
+ autosummary.Autosummary = NewAuto
+
+ import sphinx_bootstrap_theme
+-
+-# We currently rely on the latest version of numpydoc available on GitHub:
+-# git+git://github.com/numpy/numpydoc.git
+-#
+-# There isn't a way to specify this in setup.py as a dependency since this
+-# feature is being removed from pip. We also can't check the version of
+-# numpydoc installed because there isn't a numpydoc.__version__ defined.
+-try:
+- import numpydoc
+-except ImportError:
+- raise RuntimeError(
+- "numpydoc v0.6 or later required. Install it with:\n"
+- " pip install git+git://github.com/numpy/numpydoc.git@1a848331c2cf53"
+- "d4fe356f4607799524bcc577ed")
++import numpydoc
+
+ @property
+ def _extras(self):
+--- a/setup.py
++++ b/setup.py
+@@ -98,7 +98,7 @@ setup(name='scikit-bio',
+ 'matplotlib >= 1.4.3',
+ 'natsort >= 4.0.3',
+ 'numpy >= 1.9.2',
+- 'pandas >= 0.18.0',
++ 'pandas >= 0.19.2',
+ 'scipy >= 0.15.1',
+ 'nose >= 1.3.7'
+ ],
+--- a/skbio/alignment/_indexing.py
++++ b/skbio/alignment/_indexing.py
+@@ -180,7 +180,7 @@ class TabularMSALoc(_Indexing):
+ self._assert_tuple_rules(indexable)
+ if (self._has_fancy_index() and
+ type(indexable) is not tuple and
+- pd.core.common.is_list_like(indexable) and
++ pd.api.types.is_list_like(indexable) and
+ len(indexable) > 0):
+ if not self.is_scalar(indexable[0], axis=0):
+ raise TypeError("A list is used with complete labels, try"
+--- a/skbio/alignment/_tabular_msa.py
++++ b/skbio/alignment/_tabular_msa.py
+@@ -2123,6 +2123,12 @@ class TabularMSA(MetadataMixin, Position
+
+ Examples
+ --------
++ .. note:: The following examples call `.sort()` on the joined MSA
++ because there isn't a guaranteed ordering to the index. The joined
++ MSA is sorted in these examples to make the output reproducible.
++ When using this method with your own data, sorting the joined MSA is
++ not necessary.
++
+ Join MSAs by sequence:
+
+ >>> from skbio import DNA, TabularMSA
+@@ -2131,6 +2137,7 @@ class TabularMSA(MetadataMixin, Position
+ >>> msa2 = TabularMSA([DNA('G-T'),
+ ... DNA('T--')])
+ >>> joined = msa1.join(msa2)
++ >>> joined.sort() # unnecessary in practice, see note above
+ >>> joined
+ TabularMSA[DNA]
+ ---------------------
+@@ -2148,6 +2155,7 @@ class TabularMSA(MetadataMixin, Position
+ >>> msa2 = TabularMSA([DNA('G-T'),
+ ... DNA('T--')], index=['b', 'a'])
+ >>> joined = msa1.join(msa2)
++ >>> joined.sort() # unnecessary in practice, see note above
+ >>> joined
+ TabularMSA[DNA]
+ ---------------------
+@@ -2174,6 +2182,7 @@ class TabularMSA(MetadataMixin, Position
+ ... positional_metadata={'col2': [3, 4, 5],
+ ... 'col3': ['f', 'o', 'o']})
+ >>> joined = msa1.join(msa2, how='inner')
++ >>> joined.sort() # unnecessary in practice, see note above
+ >>> joined
+ TabularMSA[DNA]
+ --------------------------
+@@ -2183,10 +2192,10 @@ class TabularMSA(MetadataMixin, Position
+ sequence count: 2
+ position count: 5
+ --------------------------
+- A-G-T
+ ACT--
++ A-G-T
+ >>> joined.index
+- Index(['b', 'a'], dtype='object')
++ Index(['a', 'b'], dtype='object')
+ >>> joined.positional_metadata
+ col2
+ 0 1
+@@ -2200,6 +2209,7 @@ class TabularMSA(MetadataMixin, Position
+ ``positional_metadata`` columns are padded with NaN:
+
+ >>> joined = msa1.join(msa2, how='outer')
++ >>> joined.sort() # unnecessary in practice, see note above
+ >>> joined
+ TabularMSA[DNA]
+ ----------------------------
+@@ -2284,12 +2294,12 @@ class TabularMSA(MetadataMixin, Position
+
+ def _get_join_index(self, other, how):
+ if how == 'strict':
+- diff = self.index.sym_diff(other.index)
++ diff = self.index.symmetric_difference(other.index)
+ if len(diff) > 0:
+ raise ValueError(
+ "Index labels must all match with `how='strict'`")
+
+- diff = self.positional_metadata.columns.sym_diff(
++ diff = self.positional_metadata.columns.symmetric_difference(
+ other.positional_metadata.columns)
+
+ if not self.has_positional_metadata():
+--- a/skbio/alignment/tests/test_tabular_msa.py
++++ b/skbio/alignment/tests/test_tabular_msa.py
+@@ -1137,7 +1137,7 @@ class SharedIndexTests:
+ def test_bad_fancy_index(self):
+ msa = TabularMSA([DNA("ABCD"), DNA("GHKM"), DNA("NRST")])
+
+- with self.assertRaises((KeyError, TypeError)):
++ with self.assertRaises((KeyError, TypeError, ValueError)):
+ self.get(msa, [0, "foo"])
+
+ with self.assertRaises(IndexError):
+@@ -2506,6 +2506,19 @@ class TestExtend(unittest.TestCase):
+
+
+ class TestJoin(unittest.TestCase):
++ def assertEqualJoinedMSA(self, msa1, msa2):
++ # `TabularMSA.join` doesn't guarantee index order in the joined MSA.
++ # The order differs across pandas versions, so sort each MSA before
++ # comparing for equality.
++
++ # copy because `TabularMSA.sort` is in-place.
++ msa1 = copy.copy(msa1)
++ msa2 = copy.copy(msa2)
++ msa1.sort()
++ msa2.sort()
++
++ self.assertEqual(msa1, msa2)
++
+ def test_invalid_how(self):
+ with self.assertRaisesRegex(ValueError, '`how`'):
+ TabularMSA([]).join(TabularMSA([]), how='really')
+@@ -2543,7 +2556,7 @@ class TestJoin(unittest.TestCase):
+
+ joined = msa1.join(msa2)
+
+- self.assertEqual(
++ self.assertEqualJoinedMSA(
+ joined,
+ TabularMSA([DNA('AC-C'),
+ DNA('G..G')]))
+@@ -2560,7 +2573,7 @@ class TestJoin(unittest.TestCase):
+
+ joined = msa1.join(msa2)
+
+- self.assertEqual(
++ self.assertEqualJoinedMSA(
+ joined,
+ TabularMSA([DNA('ACCA'),
+ DNA('G..G'),
+@@ -2576,7 +2589,7 @@ class TestJoin(unittest.TestCase):
+
+ joined = msa1.join(msa2)
+
+- self.assertEqual(
++ self.assertEqualJoinedMSA(
+ joined,
+ TabularMSA([
+ DNA('ACCA',
+@@ -2594,7 +2607,7 @@ class TestJoin(unittest.TestCase):
+
+ joined = msa1.join(msa2)
+
+- self.assertEqual(joined, TabularMSA([]))
++ self.assertEqualJoinedMSA(joined, TabularMSA([]))
+
+ def test_no_positions(self):
+ msa1 = TabularMSA([DNA('', positional_metadata={'1': []}),
+@@ -2606,7 +2619,7 @@ class TestJoin(unittest.TestCase):
+
+ joined = msa1.join(msa2)
+
+- self.assertEqual(
++ self.assertEqualJoinedMSA(
+ joined,
+ TabularMSA([DNA('', positional_metadata={'1': [], '3': []}),
+ DNA('', positional_metadata={'2': [], '4': []})],
+@@ -2622,7 +2635,7 @@ class TestJoin(unittest.TestCase):
+
+ joined = msa1.join(msa2)
+
+- self.assertEqual(
++ self.assertEqualJoinedMSA(
+ joined,
+ TabularMSA([DNA('A', positional_metadata={'1': ['a'],
+ '3': [np.nan]}),
+@@ -2644,7 +2657,7 @@ class TestJoin(unittest.TestCase):
+
+ joined = msa1.join(msa2)
+
+- self.assertEqual(
++ self.assertEqualJoinedMSA(
+ joined,
+ TabularMSA([DNA('ACCA'),
+ DNA('G..G'),
+@@ -2693,7 +2706,7 @@ class TestJoin(unittest.TestCase):
+
+ joined = msa1.join(msa2, how='inner')
+
+- self.assertEqual(
++ self.assertEqualJoinedMSA(
+ joined,
+ TabularMSA([DNA('C--C'),
+ DNA('G..G'),
+@@ -2710,7 +2723,7 @@ class TestJoin(unittest.TestCase):
+
+ joined = msa1.join(msa2, how='inner')
+
+- self.assertEqual(
++ self.assertEqualJoinedMSA(
+ joined,
+ TabularMSA([DNA('G.-C'),
+ DNA('AC.G')], index=['a', 'b']))
+@@ -2725,7 +2738,7 @@ class TestJoin(unittest.TestCase):
+
+ joined = msa1.join(msa2, how='inner')
+
+- self.assertEqual(joined, TabularMSA([]))
++ self.assertEqualJoinedMSA(joined, TabularMSA([]))
+
+ def test_how_outer(self):
+ msa1 = TabularMSA([DNA('AC'),
+@@ -2743,7 +2756,7 @@ class TestJoin(unittest.TestCase):
+
+ joined = msa1.join(msa2, how='outer')
+
+- self.assertEqual(
++ self.assertEqualJoinedMSA(
+ joined,
+ TabularMSA([DNA('--...'),
+ DNA('ACCAA'),
+@@ -2771,7 +2784,7 @@ class TestJoin(unittest.TestCase):
+
+ joined = msa1.join(msa2, how='left')
+
+- self.assertEqual(
++ self.assertEqualJoinedMSA(
+ joined,
+ TabularMSA([DNA('ACCAA'),
+ DNA('G..GG'),
+@@ -2797,7 +2810,7 @@ class TestJoin(unittest.TestCase):
+
+ joined = msa1.join(msa2, how='right')
+
+- self.assertEqual(
++ self.assertEqualJoinedMSA(
+ joined,
+ TabularMSA([DNA('C--CC'),
+ DNA('G..GG'),
+--- a/skbio/diversity/_block.py
++++ b/skbio/diversity/_block.py
+@@ -185,8 +185,8 @@ def _block_compute(**kwargs):
+ def _map(func, kw_gen):
+ """Map a function over arguments
+
+- Note
+- ----
++ Notes
++ -----
+ builtin map does not allow for mapping with kwargs.
+
+ Parallel uses of block decomposition will likely replace this method with
+@@ -276,8 +276,8 @@ def block_beta_diversity(metric, counts,
+ A distance matrix relating all samples represented by counts to each
+ other.
+
+- Note
+- ----
++ Notes
++ -----
+ This method is designed to facilitate computing beta diversity in parallel.
+ In general, if you are processing a few hundred samples or less, then it is
+ likely the case that `skbio.diversity.beta_diversity` will be faster. The
+--- a/skbio/metadata/_mixin.py
++++ b/skbio/metadata/_mixin.py
+@@ -254,7 +254,7 @@ class PositionalMetadataMixin(metaclass=
+ Set positional metadata:
+
+ >>> seq.positional_metadata = {'degenerates': seq.degenerates()}
+- >>> seq.positional_metadata
++ >>> seq.positional_metadata # doctest: +NORMALIZE_WHITESPACE
+ degenerates
+ 0 False
+ 1 False
+@@ -285,7 +285,10 @@ class PositionalMetadataMixin(metaclass=
+ try:
+ # Pass copy=True to copy underlying data buffer.
+ positional_metadata = pd.DataFrame(positional_metadata, copy=True)
+- except pd.core.common.PandasError as e:
++ # Different versions of pandas will raise different error types. We
++ # don't really care what the type of the error is, just its message, so
++ # a blanket Exception will do.
++ except Exception as e:
+ raise TypeError(
+ "Invalid positional metadata. Must be consumable by "
+ "`pd.DataFrame` constructor. Original pandas error message: "
+@@ -368,7 +371,15 @@ class PositionalMetadataMixin(metaclass=
+
+ def _deepcopy_(self, memo):
+ if self.has_positional_metadata():
+- return copy.deepcopy(self.positional_metadata, memo)
++ # `copy.deepcopy` no longer recursively copies contents of the
++ # DataFrame, so we must handle the deep copy ourselves.
++ # Reference: https://github.com/pandas-dev/pandas/issues/17406
++ df = self.positional_metadata
++ data_cp = copy.deepcopy(df.values.tolist(), memo)
++ return pd.DataFrame(data_cp,
++ index=df.index.copy(deep=True),
++ columns=df.columns.copy(deep=True),
++ copy=False)
+ else:
+ return None
+
+--- a/skbio/stats/gradient.py
++++ b/skbio/stats/gradient.py
+@@ -163,8 +163,10 @@ def _weight_by_vector(trajectories, w_ve
+ for i, idx in enumerate(trajectories.index):
+ # Skipping the first element is it doesn't need to be weighted
+ if i != 0:
+- trajectories.ix[idx] = (trajectories.ix[idx] * optimal_gradient /
+- (np.abs((w_vector[i] - w_vector[i-1]))))
++ trajectories.loc[idx] = (
++ trajectories.loc[idx] * optimal_gradient /
++ np.abs((w_vector[i] - w_vector[i-1]))
++ )
+
+ return trajectories
+
+@@ -428,7 +430,7 @@ class GradientANOVA:
+ % (len(prop_expl), axes))
+
+ # Restrict coordinates to those axes that we actually need to compute
+- self._coords = coords.ix[:, :axes-1]
++ self._coords = coords.loc[:, :axes-1]
+ self._prop_expl = prop_expl[:axes]
+ self._metadata_map = metadata_map
+ self._weighted = weighted
+@@ -501,10 +503,10 @@ class GradientANOVA:
+
+ # Need to take a subset of coords
+ if coords_sample_ids != sample_ids:
+- self._coords = self._coords.ix[sample_ids]
++ self._coords = self._coords.loc[sample_ids]
+ # Need to take a subset of metadata_map
+ if mm_sample_ids != sample_ids:
+- self._metadata_map = self._metadata_map.ix[sample_ids]
++ self._metadata_map = self._metadata_map.loc[sample_ids]
+
+ def _make_groups(self, trajectory_categories, sort_category):
+ r"""Groups the sample ids in `self._metadata_map` by the values in
+@@ -566,7 +568,7 @@ class GradientANOVA:
+ If sids is an empty list
+ """
+ # We multiply the coord values with the prop_expl
+- trajectories = self._coords.ix[sids] * self._prop_expl
++ trajectories = self._coords.loc[sids] * self._prop_expl
+
+ if trajectories.empty:
+ # Raising a RuntimeError since in a usual execution this should
+@@ -590,7 +592,7 @@ class GradientANOVA:
+ trajectories = trajectories_copy
+
+ return self._compute_trajectories_results(group_name,
+- trajectories.ix[sids])
++ trajectories.loc[sids])
+
+ def _compute_trajectories_results(self, group_name, trajectories):
+ r"""Do the actual trajectories computation over trajectories
+@@ -695,8 +697,8 @@ class TrajectoryGradientANOVA(GradientAN
+ # Loop through all the rows in trajectories and create '2-norm'
+ # by taking the norm of the 2nd row - 1st row, 3rd row - 2nd row...
+ trajectory = \
+- np.array([np.linalg.norm(trajectories.ix[i+1].get_values() -
+- trajectories.ix[i].get_values())
++ np.array([np.linalg.norm(trajectories.iloc[i+1].get_values() -
++ trajectories.iloc[i].get_values())
+ for i in range(len(trajectories) - 1)])
+ calc = {'2-norm': np.linalg.norm(trajectory)}
+
+@@ -745,8 +747,8 @@ class FirstDifferenceGradientANOVA(Gradi
+ calc = {'mean': trajectory[0], 'std': 0}
+ else:
+ vec_norm = \
+- np.array([np.linalg.norm(trajectories.ix[i+1].get_values() -
+- trajectories.ix[i].get_values())
++ np.array([np.linalg.norm(trajectories.iloc[i+1].get_values() -
++ trajectories.iloc[i].get_values())
+ for i in range(len(trajectories) - 1)])
+ trajectory = np.diff(vec_norm)
+ calc = {'mean': np.mean(trajectory), 'std': np.std(trajectory)}
+@@ -830,8 +832,8 @@ class WindowDifferenceGradientANOVA(Grad
+ calc = {'mean': trajectory, 'std': 0}
+ else:
+ vec_norm = \
+- np.array([np.linalg.norm(trajectories.ix[i+1].get_values() -
+- trajectories.ix[i].get_values())
++ np.array([np.linalg.norm(trajectories.iloc[i+1].get_values() -
++ trajectories.iloc[i].get_values())
+ for i in range(len(trajectories) - 1)])
+ # windowed first differences won't be able on every group,
+ # specially given the variation of size that a trajectory tends
+--- a/skbio/stats/ordination/_ordination_results.py
++++ b/skbio/stats/ordination/_ordination_results.py
+@@ -93,8 +93,6 @@ class OrdinationResults(SkbioObject):
+ str
+ String representation of the ordination results.
+
+- .. shownumpydoc
+-
+ """
+ lines = ['Ordination results:']
+ method = '%s (%s)' % (self.long_method_name, self.short_method_name)
+--- a/skbio/stats/tests/test_composition.py
++++ b/skbio/stats/tests/test_composition.py
+@@ -6,6 +6,7 @@
+ # The full license is in the file COPYING.txt, distributed with this software.
+ # ----------------------------------------------------------------------------
+
++import functools
+ from unittest import TestCase, main
+ import numpy as np
+ import numpy.testing as npt
+@@ -882,10 +883,12 @@ class AncomTests(TestCase):
+ assert_data_frame_almost_equal(result[0], exp)
+
+ def test_ancom_multiple_comparisons(self):
++ significance_test = functools.partial(scipy.stats.mannwhitneyu,
++ alternative='two-sided')
+ result = ancom(self.table1,
+ self.cats1,
+ multiple_comparisons_correction='holm-bonferroni',
+- significance_test=scipy.stats.mannwhitneyu)
++ significance_test=significance_test)
+ exp = pd.DataFrame(
+ {'W': np.array([0]*7),
+ 'Reject null hypothesis': np.array([False]*7, dtype=bool)})
+--- a/skbio/stats/tests/test_gradient.py
++++ b/skbio/stats/tests/test_gradient.py
+@@ -335,7 +335,7 @@ class GradientTests(BaseTests):
+ -0.44931561,
+ 0.74490965])
+ }, orient='index')
+- obs = _weight_by_vector(trajectory.ix[sample_ids],
++ obs = _weight_by_vector(trajectory.loc[sample_ids],
+ w_vector[sample_ids])
+ assert_data_frame_almost_equal(obs.sort_index(), exp.sort_index())
+
+@@ -779,7 +779,7 @@ class GradientANOVATests(BaseTests):
+ """Should raise a RuntimeError if the user call _get_group_trajectories
+ with erroneous inputs"""
+ bv = GradientANOVA(self.coords, self.prop_expl, self.metadata_map)
+- with self.assertRaises(RuntimeError):
++ with self.assertRaises(KeyError):
+ bv._get_group_trajectories("foo", ['foo'])
+ with self.assertRaises(RuntimeError):
+ bv._get_group_trajectories("bar", [])
diff --git a/debian/patches/series b/debian/patches/series
index 9614d0f..18e965d 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -1,3 +1,4 @@
mathjax-path
0002-use-libsww-as-library-not-embedded-src.patch
0003-Cherry-pick-upstream-fix-for-numpy-transition.patch
+0004-MAINT-compat-with-new-pandas-and-numpydoc-fix-deprec.patch
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/python-skbio.git
More information about the debian-med-commit
mailing list