Repository: dwavesystems/dimod

Issue:
Fix multiple inheritance in `ComposedSampler` and `ComposedPolySampler`
Currently, `Sampler` attributes override `Composite` attributes in `ComposedSampler` (and similarly in `ComposedPolySampler`):

https://github.com/dwavesystems/dimod/blob/7c27eb5655b5aaec7dd2da220431ecfed034742f/dimod/core/composite.py#L110-L111

Up until we implemented `Scoped` interface (`.close()`) on both `Sampler` and `Composite`, this was not an issue, as their methods didn't overlap.

However, now we want `Composite.close()` to override `Sampler.close()`, as it implements a meaningful behavior unlike the no-op close on sampler. Generally as well I think it makes sense for composite attributes to take precedence over sampler attrs on a composed sampler.

Discussion:

Solution patch:
diff --git a/dimod/core/composite.py b/dimod/core/composite.py
index 8f967bf5a..5dd721e00 100644
--- a/dimod/core/composite.py
+++ b/dimod/core/composite.py
@@ -107,7 +107,7 @@ def close(self):
         super().close()
 
 
-class ComposedSampler(Sampler, Composite):
+class ComposedSampler(Composite, Sampler):
     """Abstract base class for dimod composed samplers.
 
     Inherits from :class:`.Sampler` and :class:`.Composite`.
diff --git a/dimod/core/polysampler.py b/dimod/core/polysampler.py
index 5802c1a84..f898307c8 100644
--- a/dimod/core/polysampler.py
+++ b/dimod/core/polysampler.py
@@ -117,7 +117,7 @@ def close(self):
         pass
 
 
-class ComposedPolySampler(PolySampler, Composite):
+class ComposedPolySampler(Composite, PolySampler):
     """Abstract base class for dimod composed polynomial samplers.
 
     Inherits from :class:`.PolySampler` and :class:`.Composite`."""
diff --git a/releasenotes/notes/fix-composedsampler-inheritance-92966dac7eb1128c.yaml b/releasenotes/notes/fix-composedsampler-inheritance-92966dac7eb1128c.yaml
new file mode 100644
index 000000000..2b72a63db
--- /dev/null
+++ b/releasenotes/notes/fix-composedsampler-inheritance-92966dac7eb1128c.yaml
@@ -0,0 +1,7 @@
+---
+fixes:
+  - |
+    Fix inheritance in ``ComposedSampler`` and ``ComposedPolySampler`` so that
+    composite attributes override sampler attributes.
+
+    See `#1402 <https://github.com/dwavesystems/dimod/issues/1402>`_.


Test patch:
diff --git a/tests/test_composite.py b/tests/test_composite.py
index 20b0738c1..7ffadbc99 100644
--- a/tests/test_composite.py
+++ b/tests/test_composite.py
@@ -100,3 +100,45 @@ def close(self):
         inner_2.resource.close.assert_called_once()
         # closed by subclass
         outer.resource.close.assert_called_once()
+
+
+class TestComposedSamplerInheritance(unittest.TestCase):
+    def test_instantiation_base_class(self):
+        with self.assertRaises(TypeError):
+            dimod.ComposedSampler()
+
+    def test_inheritance(self):
+        class Dummy(dimod.ComposedSampler):
+            def sample(self, bqm):
+                return self.child.sample(bqm)
+
+            @property
+            def parameters(self):
+                return {}
+
+            @property
+            def properties(self):
+                return {}
+
+            @property
+            def children(self):
+                return [self._sampler]
+
+            def __init__(self, sampler):
+                self._sampler = sampler
+
+        sampler = mock.MagicMock()
+        composite = Dummy(sampler)
+
+        with self.subTest("sampler interface"):
+            h = {}
+            J = {'ab': 1}
+            composite.sample_ising(h, J)
+            sampler.sample.assert_called_with(dimod.BQM.from_ising(h, J))
+
+        with self.subTest("composite interface"):
+            self.assertEqual(composite.child, sampler)
+
+        with self.subTest("close inheritance"):
+            composite.close()
+            sampler.close.assert_called_once()