From 93797da50a605e1822f361bc56429463737fe65a Mon Sep 17 00:00:00 2001 From: Kurt Steinkraus Date: Fri, 2 Aug 2013 08:38:45 -0700 Subject: [PATCH] Addes default __eq__() to BindingSpec, so that DAG binding spec dependencies can have equal but not identical dependencies. --- README.rst | 25 +++++++++++++++++++++++++ pinject/bindings.py | 6 ++++++ pinject/bindings_test.py | 27 +++++++++++++++++++++++++++ pinject/object_graph_test.py | 5 ++--- 4 files changed, 60 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index ed4db45..0dbe85b 100644 --- a/README.rst +++ b/README.rst @@ -370,6 +370,30 @@ binding spec A can be a dependency of B and of C, and binding spec D can have dependencies on B and C. Even though there are multiple dependency paths from D to A, the bindings in binding spec A will only be evaluated once. +The binding spec instance of A that is a dependency of B is considered the +same as the instance that is a dependency of C if the two instances are equal +(via ``__eq__()``). The default implementation of ``__eq__()`` in +``BindingSpec`` says that two binding specs are equal if they are of exactly +the same python type. You will need to override ``__eq__()`` (as well as +``__hash__()``) if your binding spec is parameterized, i.e., if it takes one +or more initializer args so that two instances of the binding spec may behave +differently. + +.. code-block:: python + + >>> class SomeBindingSpec(pinject.BindingSpec): + ... def __init__(self, the_instance): + ... self._the_instance = the_instance + ... def configure(self, bind): + ... bind('foo', to_instance=self._the_instance) + ... def __eq__(self, other): + ... return (type(self) == type(other) and + ... self._the_instance == other._the_instance) + ... def __hash__(self): + ... return hash(type(self)) ^ hash(self._the_instance) + ... + >>> + Provider methods ---------------- @@ -1218,6 +1242,7 @@ Changelog v0.10: +* Added default ``__eq__()`` to ``BindingSpec``, so that DAG binding spec dependencies can have equal but not identical dependencies. * Allowed customizing ``configure()`` and ``dependencies()`` binding spec method names. * Deprecated ``@injectable`` in favor of ``@inject``. * Added partial injection. diff --git a/pinject/bindings.py b/pinject/bindings.py index 0bc9bd5..f0949f3 100644 --- a/pinject/bindings.py +++ b/pinject/bindings.py @@ -293,6 +293,12 @@ def configure(self, bind): def dependencies(self): return [] + def __eq__(self, other): + return type(self) == type(other) + + def __hash__(self): + return hash(type(self)) + def get_provider_fn_bindings(provider_fn, default_arg_names): provider_decorations = decorators.get_provider_fn_decorations( diff --git a/pinject/bindings_test.py b/pinject/bindings_test.py index 85b08b4..684ecbc 100644 --- a/pinject/bindings_test.py +++ b/pinject/bindings_test.py @@ -408,6 +408,33 @@ def test_binding_to_non_class_raises_error(self): to_class='not-a-class') +class BindingSpecTest(unittest.TestCase): + + def test_equal_if_same_type(self): + class SomeBindingSpec(bindings_lib.BindingSpec): + pass + self.assertEqual(SomeBindingSpec(), SomeBindingSpec()) + + def test_not_equal_if_not_same_type(self): + class BindingSpecOne(bindings_lib.BindingSpec): + pass + class BindingSpecTwo(bindings_lib.BindingSpec): + pass + self.assertNotEqual(BindingSpecOne(), BindingSpecTwo()) + + def test_hash_equal_if_same_type(self): + class SomeBindingSpec(bindings_lib.BindingSpec): + pass + self.assertEqual(hash(SomeBindingSpec()), hash(SomeBindingSpec())) + + def test_hash_not_equal_if_not_same_type(self): + class BindingSpecOne(bindings_lib.BindingSpec): + pass + class BindingSpecTwo(bindings_lib.BindingSpec): + pass + self.assertNotEqual(hash(BindingSpecOne()), hash(BindingSpecTwo())) + + class GetProviderFnBindingsTest(unittest.TestCase): def test_proviser_calls_provider_fn(self): diff --git a/pinject/object_graph_test.py b/pinject/object_graph_test.py index 72114cb..cea4f67 100644 --- a/pinject/object_graph_test.py +++ b/pinject/object_graph_test.py @@ -78,13 +78,12 @@ def test_allows_dag_binding_spec_dependencies(self): class CommonBindingSpec(bindings.BindingSpec): def configure(self, bind): bind('foo', to_instance='a-foo') - a_common_binding_spec = CommonBindingSpec() class BindingSpecOne(bindings.BindingSpec): def dependencies(self): - return [a_common_binding_spec] + return [CommonBindingSpec()] class BindingSpecTwo(bindings.BindingSpec): def dependencies(self): - return [a_common_binding_spec] + return [CommonBindingSpec()] class RootBindingSpec(bindings.BindingSpec): def dependencies(self): return [BindingSpecOne(), BindingSpecTwo()]