From 2ea596cb8e9fa5c96fecbf440e8d05b14c670ae3 Mon Sep 17 00:00:00 2001 From: "David W.H. Swenson" Date: Mon, 22 Jan 2018 21:56:29 +0100 Subject: [PATCH 1/7] Bump 0.3.1.dev0 --- ci/conda-recipe/meta.yaml | 2 +- setup.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ci/conda-recipe/meta.yaml b/ci/conda-recipe/meta.yaml index d56c755..2f64970 100644 --- a/ci/conda-recipe/meta.yaml +++ b/ci/conda-recipe/meta.yaml @@ -1,7 +1,7 @@ package: name: contact_map # add ".dev0" for unreleased versions - version: "0.3.0" + version: "0.3.1.dev0" source: path: ../../ diff --git a/setup.py b/setup.py index a149a9e..f93c1f4 100644 --- a/setup.py +++ b/setup.py @@ -9,8 +9,8 @@ ####################### USER SETUP AREA ################################# # * VERSION: base version (do not include .dev0, etc -- that's automatic) # * IS_RELEASE: whether this is a release -VERSION = "0.3.0" -IS_RELEASE = True +VERSION = "0.3.1" +IS_RELEASE = False DEV_NUM = 0 # always 0: we don't do public (pypi) .dev releases PRE_TYPE = "" # a, b, or rc (although we rarely release such versions) From 93472bd197fe395e55c624fc89059314a7bfb5ba Mon Sep 17 00:00:00 2001 From: "David W.H. Swenson" Date: Tue, 23 Jan 2018 10:44:18 +0100 Subject: [PATCH 2/7] [pylint] Misc code clean-up --- contact_map/contact_map.py | 3 +-- contact_map/tests/test_contact_map.py | 2 +- contact_map/tests/test_dask_runner.py | 2 +- contact_map/tests/test_frequency_task.py | 5 +++-- contact_map/tests/utils.py | 4 +++- 5 files changed, 9 insertions(+), 7 deletions(-) diff --git a/contact_map/contact_map.py b/contact_map/contact_map.py index 005d938..0119f6e 100644 --- a/contact_map/contact_map.py +++ b/contact_map/contact_map.py @@ -12,7 +12,6 @@ import mdtraj as md from .contact_count import ContactCount -from .plot_utils import ranged_colorbar from .py_2_3 import inspect_method_arguments # TODO: @@ -126,7 +125,7 @@ def from_dict(cls, dct): -------- to_dict """ - deserialize_set = lambda k: set(k) + deserialize_set = set deserialize_atom_to_residue_dct = lambda d: {int(k): d[k] for k in d} deserialization_helpers = { 'topology': cls._deserialize_topology, diff --git a/contact_map/tests/test_contact_map.py b/contact_map/tests/test_contact_map.py index 99af6cd..9a16cf4 100644 --- a/contact_map/tests/test_contact_map.py +++ b/contact_map/tests/test_contact_map.py @@ -133,7 +133,7 @@ def test_counters(self, idx): def test_to_dict(self, idx): m = self.maps[idx] - json_topol = json.dumps(pdb_topology_dict()) + #json_topol = json.dumps(pdb_topology_dict()) dct = m.to_dict() # NOTE: topology only tested in a cycle; JSON order not guaranteed assert dct['cutoff'] == 0.075 diff --git a/contact_map/tests/test_dask_runner.py b/contact_map/tests/test_dask_runner.py index 7f804be..d72cb18 100644 --- a/contact_map/tests/test_dask_runner.py +++ b/contact_map/tests/test_dask_runner.py @@ -9,7 +9,7 @@ class TestDaskContactFrequency(object): def test_dask_integration(self): # this is an integration test to check that dask works - dask = pytest.importorskip('dask') + dask = pytest.importorskip('dask') # pylint: disable=W0612 distributed = pytest.importorskip('dask.distributed') client = distributed.Client() diff --git a/contact_map/tests/test_frequency_task.py b/contact_map/tests/test_frequency_task.py index 6a79038..57eab69 100644 --- a/contact_map/tests/test_frequency_task.py +++ b/contact_map/tests/test_frequency_task.py @@ -1,5 +1,6 @@ -import os -import collections +# pylint: disable=wildcard-import, missing-docstring, protected-access +# pylint: disable=attribute-defined-outside-init, invalid-name, no-self-use +# pylint: disable=wrong-import-order, unused-wildcard-import from .utils import * from .test_contact_map import traj diff --git a/contact_map/tests/utils.py b/contact_map/tests/utils.py index b600d4e..926d9bf 100644 --- a/contact_map/tests/utils.py +++ b/contact_map/tests/utils.py @@ -1,7 +1,9 @@ import os -import numpy as np from pkg_resources import resource_filename +# pylint: disable=unused-import + +import numpy as np from numpy.testing import assert_array_equal, assert_allclose import pytest From 56dfe444945eb2d8f52d39c47f15e72e04a47a6d Mon Sep 17 00:00:00 2001 From: "David W.H. Swenson" Date: Tue, 23 Jan 2018 11:38:17 +0100 Subject: [PATCH 3/7] Non-assert-based _check_compatibility (draft) --- contact_map/contact_map.py | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/contact_map/contact_map.py b/contact_map/contact_map.py index 0119f6e..b09272a 100644 --- a/contact_map/contact_map.py +++ b/contact_map/contact_map.py @@ -210,12 +210,21 @@ def from_json(cls, json_string): dct = json.loads(json_string) return cls.from_dict(dct) - def _check_compatibility(self, other): - assert self.cutoff == other.cutoff - assert self.topology == other.topology - assert self.query == other.query - assert self.haystack == other.haystack - assert self.n_neighbors_ignored == other.n_neighbors_ignored + def _check_compatibility(self, other, err=AssertionError): + compatibility_attrs = ['cutoff', 'topology', 'query', 'haystack', + 'n_neighbors_ignored'] + failed_attr = {} + for attr in compatibility_attrs: + self_val = getattr(self, attr) + other_val = getattr(other, attr) + if self_val != other_val: + failed_attr.update({attr: (self_val, other_val)}) + msg = "Incompatible ContactObjects:\n" + for (attr, vals) in failed_attr.items(): + msg += " %s: %s != %s".format(attr, str(vals[0]), + str(vals[1])) + if failed_attr: + raise err(msg) def save_to_file(self, filename, mode="w"): """Save this object to the given file. From 484d53c0160283b0c654ed4cad367efe2c957c00 Mon Sep 17 00:00:00 2001 From: "David W.H. Swenson" Date: Tue, 23 Jan 2018 13:35:12 +0100 Subject: [PATCH 4/7] Tests for _check_compatibility --- contact_map/contact_map.py | 6 ++++-- contact_map/dask_runner.py | 2 ++ contact_map/tests/test_contact_map.py | 31 +++++++++++++++++++++++++-- 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/contact_map/contact_map.py b/contact_map/contact_map.py index b09272a..c0b66a1 100644 --- a/contact_map/contact_map.py +++ b/contact_map/contact_map.py @@ -221,10 +221,12 @@ def _check_compatibility(self, other, err=AssertionError): failed_attr.update({attr: (self_val, other_val)}) msg = "Incompatible ContactObjects:\n" for (attr, vals) in failed_attr.items(): - msg += " %s: %s != %s".format(attr, str(vals[0]), - str(vals[1])) + msg += " {attr}: {self} != {other}\n".format( + attr=attr, self=str(vals[0]), other=str(vals[1]) + ) if failed_attr: raise err(msg) + return True def save_to_file(self, filename, mode="w"): """Save this object to the given file. diff --git a/contact_map/dask_runner.py b/contact_map/dask_runner.py index 5521a1c..ceb0b43 100644 --- a/contact_map/dask_runner.py +++ b/contact_map/dask_runner.py @@ -87,6 +87,8 @@ def __init__(self, client, filename, query=None, haystack=None, self.frames = range(len(trajectory)) self.kwargs = kwargs + # TODO: this can be fixed (use proper super) by overriding this + # class's _build_contact_map method to run the dask_run function ContactObject.__init__(self, trajectory.topology, query, haystack, cutoff, n_neighbors_ignored) diff --git a/contact_map/tests/test_contact_map.py b/contact_map/tests/test_contact_map.py index 9a16cf4..3a3eece 100644 --- a/contact_map/tests/test_contact_map.py +++ b/contact_map/tests/test_contact_map.py @@ -1,6 +1,5 @@ import os import collections -import json import mdtraj as md # pylint: disable=wildcard-import, missing-docstring, protected-access @@ -133,7 +132,6 @@ def test_counters(self, idx): def test_to_dict(self, idx): m = self.maps[idx] - #json_topol = json.dumps(pdb_topology_dict()) dct = m.to_dict() # NOTE: topology only tested in a cycle; JSON order not guaranteed assert dct['cutoff'] == 0.075 @@ -279,6 +277,35 @@ def test_counters(self): } assert residue_contacts.counter == expected_residue_contacts + def test_check_compatibility_true(self): + map2 = ContactFrequency(trajectory=traj[0:2], + cutoff=0.075, + n_neighbors_ignored=0) + assert self.map._check_compatibility(map2) == True + + @pytest.mark.parametrize("diff", [ + {'trajectory': traj.atom_slice([0, 1, 2, 3])}, + {'cutoff': 0.45}, + {'n_neighbors_ignored': 2}, + {'query': [1, 2, 3, 4]}, + {'haystack': [1, 2, 3, 4]} + ]) + def test_check_compatibility_assertion_error(self, diff): + params = {'trajectory': traj[0:2], + 'cutoff': 0.075, + 'n_neighbors_ignored': 0} + params.update(diff) + map2 = ContactFrequency(**params) + with pytest.raises(AssertionError): + self.map._check_compatibility(map2) + + def test_check_compatibility_runtime_error(self): + map2 = ContactFrequency(trajectory=traj, + cutoff=0.45, + n_neighbors_ignored=2) + with pytest.raises(RuntimeError): + self.map._check_compatibility(map2, err=RuntimeError) + @pytest.mark.parametrize("intermediate", ["dict", "json"]) def test_serialization_cycle(self, intermediate): serializer, deserializer = { From 3a0bdedd151200b274a3223ef201c2d9834040c4 Mon Sep 17 00:00:00 2001 From: "David W.H. Swenson" Date: Tue, 23 Jan 2018 13:41:35 +0100 Subject: [PATCH 5/7] Fix DaskContactFreq to inherit properly --- contact_map/dask_runner.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/contact_map/dask_runner.py b/contact_map/dask_runner.py index ceb0b43..8fbd777 100644 --- a/contact_map/dask_runner.py +++ b/contact_map/dask_runner.py @@ -84,18 +84,16 @@ def __init__(self, client, filename, query=None, haystack=None, self.filename = filename trajectory = md.load(filename, **kwargs) - self.frames = range(len(trajectory)) self.kwargs = kwargs - # TODO: this can be fixed (use proper super) by overriding this - # class's _build_contact_map method to run the dask_run function - ContactObject.__init__(self, trajectory.topology, query, haystack, - cutoff, n_neighbors_ignored) + super(DaskContactFrequency, self).__init__( + trajectory, query, haystack, cutoff, n_neighbors_ignored + ) - freq = dask_run(trajectory, client, self.run_info) - self._n_frames = freq.n_frames - self._atom_contacts = freq._atom_contacts - self._residue_contacts = freq._residue_contacts + def _build_contact_map(self, trajectory): + freq = dask_run(trajectory, self.client, self.run_info) + self._frames = freq.n_frames + return (freq._atom_contacts, freq._residue_contacts) @property def parameters(self): From 97edcfbe3f1e8ec8dc21204f07f93c0100556878 Mon Sep 17 00:00:00 2001 From: "David W.H. Swenson" Date: Tue, 1 May 2018 16:13:09 +0200 Subject: [PATCH 6/7] Drop Python 3.4 support [travis] pandas no longer has wheels for Py-3.4, so we can't pip-install. We require pandas for serialization/deserialization, which is an essential (not optional) feature. Therefore we can no longer support 3.4. Also, MDTraj doesn't test against 3.4 anyway. --- .travis.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 90bc771..03cc830 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,6 @@ language: python python: - '3.6' - '2.7' - - '3.4' - '3.5' branches: @@ -27,8 +26,6 @@ env: matrix: exclude: - - env: MDTRAJ="dev" - python: "3.4" - env: MDTRAJ="dev" python: "3.5" From efb3640a3baad88fc89f20cf876f1713eeea7b64 Mon Sep 17 00:00:00 2001 From: "David W.H. Swenson" Date: Tue, 1 May 2018 16:55:39 +0200 Subject: [PATCH 7/7] Release 0.3.1 --- ci/conda-recipe/meta.yaml | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ci/conda-recipe/meta.yaml b/ci/conda-recipe/meta.yaml index 2f64970..4d7323e 100644 --- a/ci/conda-recipe/meta.yaml +++ b/ci/conda-recipe/meta.yaml @@ -1,7 +1,7 @@ package: name: contact_map # add ".dev0" for unreleased versions - version: "0.3.1.dev0" + version: "0.3.1" source: path: ../../ diff --git a/setup.py b/setup.py index f93c1f4..cafee03 100644 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ # * VERSION: base version (do not include .dev0, etc -- that's automatic) # * IS_RELEASE: whether this is a release VERSION = "0.3.1" -IS_RELEASE = False +IS_RELEASE = True DEV_NUM = 0 # always 0: we don't do public (pypi) .dev releases PRE_TYPE = "" # a, b, or rc (although we rarely release such versions)