crefutil.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. # -*- test-case-name: twisted.test.test_persisted -*-
  2. # Copyright (c) Twisted Matrix Laboratories.
  3. # See LICENSE for details.
  4. """
  5. Utility classes for dealing with circular references.
  6. """
  7. from __future__ import division, absolute_import
  8. from twisted.python import log, reflect
  9. from twisted.python.compat import range, _constructMethod
  10. class NotKnown:
  11. def __init__(self):
  12. self.dependants = []
  13. self.resolved = 0
  14. def addDependant(self, mutableObject, key):
  15. assert not self.resolved
  16. self.dependants.append( (mutableObject, key) )
  17. resolvedObject = None
  18. def resolveDependants(self, newObject):
  19. self.resolved = 1
  20. self.resolvedObject = newObject
  21. for mut, key in self.dependants:
  22. mut[key] = newObject
  23. def __hash__(self):
  24. assert 0, "I am not to be used as a dictionary key."
  25. class _Container(NotKnown):
  26. """
  27. Helper class to resolve circular references on container objects.
  28. """
  29. def __init__(self, l, containerType):
  30. """
  31. @param l: The list of object which may contain some not yet referenced
  32. objects.
  33. @param containerType: A type of container objects (e.g., C{tuple} or
  34. C{set}).
  35. """
  36. NotKnown.__init__(self)
  37. self.containerType = containerType
  38. self.l = l
  39. self.locs = list(range(len(l)))
  40. for idx in range(len(l)):
  41. if not isinstance(l[idx], NotKnown):
  42. self.locs.remove(idx)
  43. else:
  44. l[idx].addDependant(self, idx)
  45. if not self.locs:
  46. self.resolveDependants(self.containerType(self.l))
  47. def __setitem__(self, n, obj):
  48. """
  49. Change the value of one contained objects, and resolve references if
  50. all objects have been referenced.
  51. """
  52. self.l[n] = obj
  53. if not isinstance(obj, NotKnown):
  54. self.locs.remove(n)
  55. if not self.locs:
  56. self.resolveDependants(self.containerType(self.l))
  57. class _Tuple(_Container):
  58. """
  59. Manage tuple containing circular references. Deprecated: use C{_Container}
  60. instead.
  61. """
  62. def __init__(self, l):
  63. """
  64. @param l: The list of object which may contain some not yet referenced
  65. objects.
  66. """
  67. _Container.__init__(self, l, tuple)
  68. class _InstanceMethod(NotKnown):
  69. def __init__(self, im_name, im_self, im_class):
  70. NotKnown.__init__(self)
  71. self.my_class = im_class
  72. self.name = im_name
  73. # im_self _must_ be a NotKnown
  74. im_self.addDependant(self, 0)
  75. def __call__(self, *args, **kw):
  76. import traceback
  77. log.msg('instance method %s.%s' % (reflect.qual(self.my_class), self.name))
  78. log.msg('being called with %r %r' % (args, kw))
  79. traceback.print_stack(file=log.logfile)
  80. assert 0
  81. def __setitem__(self, n, obj):
  82. assert n == 0, "only zero index allowed"
  83. if not isinstance(obj, NotKnown):
  84. method = _constructMethod(self.my_class, self.name, obj)
  85. self.resolveDependants(method)
  86. class _DictKeyAndValue:
  87. def __init__(self, dict):
  88. self.dict = dict
  89. def __setitem__(self, n, obj):
  90. if n not in (1, 0):
  91. raise RuntimeError("DictKeyAndValue should only ever be called with 0 or 1")
  92. if n: # value
  93. self.value = obj
  94. else:
  95. self.key = obj
  96. if hasattr(self, "key") and hasattr(self, "value"):
  97. self.dict[self.key] = self.value
  98. class _Dereference(NotKnown):
  99. def __init__(self, id):
  100. NotKnown.__init__(self)
  101. self.id = id
  102. from twisted.internet.defer import Deferred
  103. class _Defer(Deferred, NotKnown):
  104. def __init__(self):
  105. Deferred.__init__(self)
  106. NotKnown.__init__(self)
  107. self.pause()
  108. wasset = 0
  109. def __setitem__(self, n, obj):
  110. if self.wasset:
  111. raise RuntimeError('setitem should only be called once, setting %r to %r' % (n, obj))
  112. else:
  113. self.wasset = 1
  114. self.callback(obj)
  115. def addDependant(self, dep, key):
  116. # by the time I'm adding a dependant, I'm *not* adding any more
  117. # callbacks
  118. NotKnown.addDependant(self, dep, key)
  119. self.unpause()
  120. resovd = self.result
  121. self.resolveDependants(resovd)