Guest User

Untitled

a guest
Oct 15th, 2018
71
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 5.62 KB | None | 0 0
  1. #!/usr/bin/env python
  2. import functools
  3.  
  4. class Dependent:
  5. """A class to help manage dependencies within my filters.
  6. Classes that inherit from it are automatically created using the
  7. decorator :func:`make_dependent`. No instances of this class are
  8. usually created. Instead, calling it is equivalent to calling it's
  9. object.
  10. """
  11. _object = None
  12. _dependencies = []
  13.  
  14. def __new__(cls, *args, **kwargs):
  15. return cls._object(*args, **kwargs)
  16.  
  17. def __init__(self, _object=None, _dependencies=None):
  18. if _dependencies is not None:
  19. self._dependencies = check_type_strictly(_dependencies, list)
  20.  
  21. if _object is not None:
  22. self._object = _object
  23.  
  24. @classmethod
  25. def resolve_dependencies(cls, resolved=None, seen=None):
  26. """Calles the function :func:`resolve_dependencies` and returns
  27. its return value.
  28.  
  29. :param resolved: list of :class:`Dependent` who's dependencies have
  30. alredy been resolved.
  31. :type resolved: `list` | `None`
  32. :param seen: list or :class:`Dependent` that already appeared while
  33. resolving its dependencies. Helps avoid circular
  34. dependencies.
  35. :type seen: `list` | `None`
  36. """
  37. return resolve_dependencies(cls, resolved, seen)
  38.  
  39. @classmethod
  40. def __repr__(cls):
  41. if hasattr(cls._object, '__name__'):
  42. return cls._object.__name__
  43. else:
  44. return repr(cls._object)
  45.  
  46. @classmethod
  47. def add_dependency(cls, dependency):
  48. cls._dependencies.append(dependency)
  49.  
  50. @classmethod
  51. def add_dependencies(cls, *dependencies):
  52. for dependency in dependencies:
  53. cls.add_dependency(dependency)
  54.  
  55. @classmethod
  56. def to_function(cls):
  57. actions, _ = cls.resolve_dependencies()
  58.  
  59. def run_functions(*args):
  60. for a in actions:
  61. a._object(*args)
  62.  
  63. return run_functions
  64.  
  65.  
  66. def resolve_dependencies(dependent, resolved=None, seen=None):
  67. """Resolve all the dependencies of a Class or Instance of a Class that
  68. inherited from :class:`Dependent`
  69.  
  70. :param resolved: list of :class:`Dependent` who's dependencies have
  71. alredy been resolved.
  72. :type resolved: `list` | `None`
  73. :param seen: list or :class:`Dependent` that already appeared while
  74. resolving its dependencies. Helps avoid circular
  75. dependencies.
  76. :type seen: `list` | `None`
  77. :returns: a `tuple` of containing the list of resolved and seen
  78. :class:`Dependent` objects.
  79. """
  80. if resolved is None:
  81. resolved = []
  82.  
  83. if seen is None:
  84. seen = []
  85.  
  86. if isinstance(dependent, list):
  87. for d in dependent:
  88. resolve_dependencies(d, resolved, seen)
  89.  
  90. else:
  91. if dependent not in resolved:
  92. seen.append(dependent)
  93.  
  94. for dependency in dependent._dependencies:
  95. if dependency not in resolved:
  96. if dependency in seen:
  97. raise Exception('Circular dependency')
  98. resolve_dependencies(dependency, resolved, seen)
  99. resolved.append(dependent)
  100.  
  101. return resolved, seen
  102.  
  103.  
  104. def make_dependent(*dependencies):
  105. """A decorator to make a function into a Class that inherits from
  106. :class:`Dependent`.
  107.  
  108. :param *dependencies: What other functions this one is dependent on.
  109. :type dependencies: :class:`Dependent` sequence
  110.  
  111. .. code-block:: python
  112.  
  113. @make_dependent()
  114. def a_func(name):
  115. print(name)
  116.  
  117. @make_dependent(a_func)
  118. def b_func(name):
  119. print(name)
  120.  
  121. Now `b_func` is dependent on `a_func`. A list of dependent functions can
  122. then be created doing this:
  123.  
  124. .. code-block:: python
  125.  
  126. b_func.resolve_dependencies()
  127. """
  128. def function_wrapper(func):
  129. inner_dependencies = dependencies
  130.  
  131. class Wrapped(Dependent):
  132. _object = func
  133. _dependencies = inner_dependencies
  134.  
  135. # Make the wrapped class have the original functions attributed
  136. for attr in functools.WRAPPER_ASSIGNMENTS:
  137. setattr(Wrapped, attr, getattr(func, attr))
  138.  
  139. doc = getattr(func, '__doc__', '')
  140. doc = '' if doc is None else doc
  141.  
  142. signature = str(inspect.signature(func))
  143. name = getattr(func, '__name__')
  144.  
  145. doc = f'{name}{signature}\n' + doc
  146. setattr(Wrapped, '__doc__', doc)
  147.  
  148. return Wrapped
  149.  
  150. return function_wrapper
  151.  
  152.  
  153. def reduce_dependencies(*list_):
  154. """Tries to resolve all the dependencies passed as parameters.
  155.  
  156. :param *list_: The dependencies that sould be resolved
  157. :type *list: `tuple` of `Dependent`
  158. :returns: A list of functions, hopefully in the order of their
  159. depencency.
  160. """
  161. return [i._object
  162. for i in functools.reduce(
  163. reduce_dependencies_helper,
  164. [None] + list(list_),
  165. )[0]]
  166.  
  167.  
  168. def reduce_dependencies_helper(returned_value, dependent):
  169. """Just a helper function for :func:`reduce_dependencies`.
  170.  
  171. :param returned_value: The tuple returned by
  172. :func:`Dependent.resolve_dependencies`
  173. :type returned_value: `tuple` of two `list`
  174. :param dependent:
  175. """
  176. if returned_value is None:
  177. returned_value = ([], [])
  178.  
  179. return_value = dependent.resolve_dependencies(*returned_value)
  180. return return_value
  181.  
  182.  
  183. def check_type_strictly(input, type_):
  184. if isinstance(input, type_):
  185. return input
  186. else:
  187. raise TypeError('Expected input to be of type {}, was {}.'.format(
  188. type_,
  189. type(input),
  190. ))
Add Comment
Please, Sign In to add comment