diff --git a/graphi/operators/interface.py b/graphi/operators/interface.py deleted file mode 100644 index c938a50..0000000 --- a/graphi/operators/interface.py +++ /dev/null @@ -1,75 +0,0 @@ -import functools - - -DEFAULT_OPERATOR_PREFIX = __name__.split('.')[0] - - -def graph_operator(prefix=DEFAULT_OPERATOR_PREFIX): - """ - Implement a callable as a graph operator - - :param prefix: identifier to prepend to special method names - - Adds the operator lookup and fallback procedure to allow - :py:class:`~graphi.abc.Graph` subclasses to implement optimized algorithms. - For example, a graph storing edges in a sorted data structure may prematurely - end a search for neighbours given a maximum distance. - - A graph can influence the evaluation of a graph operator by providing an attribute - named ``_____``, e.g. ``__graphi_neighbours__`` for an operator ``neighbours``. - If this is the case, the attribute is called with the provided arguments as a replacement - for the operator implementation. - - .. py:function:: operator(graph, *args, **kwargs) - - The generic implementation of a graph operator. - - .. py:method:: graph.__graphi_operator__(*args, **kwargs) - - The optimized implementation of a graph operator. - - There are three special conditions to this procedure: - - attribute is :py:const:`None` - The graph does not support the operation. - - Attempting the operation on the graph raises :py:exc:`TypeError`. - - attribute is :py:const:`NotImplemented` - The graph does not overwrite the operation. - The operator implementation is always used. - - calling the attribute returns :py:const:`NotImplemented` - The graph does not overwrite the operation for the specific parameters. - The operator implementation is used. - - The name of an operator is taken from ``operator.__name__`` or ``operator.__class__.__name__``. - """ - def wrap_operator(operator): - try: - operator_name = operator.__name__ - except AttributeError: - operator_name = operator.__class__.__name__ - override_name = '__%s_%s__' % (prefix, operator_name) - - @functools.wraps(operator) - def wrapped_operator(graph, *args, **kwargs): - graph_op = getattr(graph, override_name, NotImplemented) - if graph_op is None: - raise TypeError( - 'object of type %r does not support %r graph operator' % type(graph).__name__, operator.__name__ - ) - if graph_op is not NotImplemented: - return_value = graph_op(*args, **kwargs) - else: - return_value = NotImplemented - if return_value is not NotImplemented: - return return_value - return operator(graph, *args, **kwargs) - wrapped_operator.override_name = override_name - return wrapped_operator - if not isinstance(prefix, str): - _operator = prefix - prefix = DEFAULT_OPERATOR_PREFIX - return wrap_operator(_operator) - return wrap_operator diff --git a/graphi_unittests/operators_unittests/test_interface.py b/graphi_unittests/operators_unittests/test_interface.py deleted file mode 100644 index d3fc9fd..0000000 --- a/graphi_unittests/operators_unittests/test_interface.py +++ /dev/null @@ -1,83 +0,0 @@ -import unittest - -from graphi.types import adjacency_graph -from graphi.operators import interface - - -class UnsupportedGraph(adjacency_graph.AdjacencyGraph): - __graphi_operator__ = None - - -class UnoptimizedGraph(adjacency_graph.AdjacencyGraph): - __graphi_operator__ = NotImplemented - - -class MaybeGraph(adjacency_graph.AdjacencyGraph): - @staticmethod - def __graphi_operator__(optimize=True): - if optimize is False: - return NotImplemented - return 'maybe' - - -class OptimizedGraph(adjacency_graph.AdjacencyGraph): - @staticmethod - def __graphi_operator__(): - return 'optimized' - - -class TestInterface(unittest.TestCase): - def test_decorator(self): - """Bare operator protocol decorator""" - @interface.graph_operator() - def decorate_brackets(graph, *args, **kwargs): - return 42 - - @interface.graph_operator - def decorate_bare(graph, *args, **kwargs): - return 42 - - for test_case in (decorate_brackets, decorate_bare): - for test_subject in (object(), adjacency_graph.AdjacencyGraph()): - with self.subTest(operator=test_case, subject=test_subject): - self.assertEqual(decorate_brackets(test_subject), 42) - self.assertEqual(decorate_brackets(test_subject, 73), 42) - self.assertEqual(decorate_brackets(test_subject, foo="bar"), 42) - self.assertEqual(decorate_brackets(test_subject, 21, foo="baz"), 42) - - def test_unsupported(self): - """An operation is not supported by a graph""" - @interface.graph_operator - def operator(graph): - return 0 - - with self.assertRaises(TypeError): - operator(UnsupportedGraph()) - - def test_unoptimized(self): - """An operation is not optimized by a graph""" - @interface.graph_operator - def operator(graph): - return 0 - - self.assertEqual(operator(UnoptimizedGraph()), 0) - - def test_maybe(self): - """An operation is partially optimized by a graph""" - @interface.graph_operator - def operator(graph, optimize=True): - return 0 - - self.assertEqual(operator(MaybeGraph()), 'maybe') - self.assertEqual(operator(MaybeGraph(), True), 'maybe') - self.assertEqual(operator(MaybeGraph(), optimize=True), 'maybe') - self.assertEqual(operator(MaybeGraph(), False), 0) - self.assertEqual(operator(MaybeGraph(), optimize=False), 0) - - def test_optimized(self): - """An operation is optimized by a graph""" - @interface.graph_operator - def operator(graph): - return 0 - - self.assertEqual(operator(OptimizedGraph()), 'optimized')