Funktionierender Prototyp des Serious Games zur Vermittlung von Wissen zu Software-Engineering-Arbeitsmodellen.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

roots.py 7.0KB

1 year ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. # -*- test-case-name: twisted.test.test_roots -*-
  2. # Copyright (c) Twisted Matrix Laboratories.
  3. # See LICENSE for details.
  4. """
  5. Twisted Python Roots: an abstract hierarchy representation for Twisted.
  6. Maintainer: Glyph Lefkowitz
  7. """
  8. from twisted.python import reflect
  9. class NotSupportedError(NotImplementedError):
  10. """
  11. An exception meaning that the tree-manipulation operation
  12. you're attempting to perform is not supported.
  13. """
  14. class Request:
  15. """I am an abstract representation of a request for an entity.
  16. I also function as the response. The request is responded to by calling
  17. self.write(data) until there is no data left and then calling
  18. self.finish().
  19. """
  20. # This attribute should be set to the string name of the protocol being
  21. # responded to (e.g. HTTP or FTP)
  22. wireProtocol = None
  23. def write(self, data):
  24. """Add some data to the response to this request."""
  25. raise NotImplementedError("%s.write" % reflect.qual(self.__class__))
  26. def finish(self):
  27. """The response to this request is finished; flush all data to the network stream."""
  28. raise NotImplementedError("%s.finish" % reflect.qual(self.__class__))
  29. class Entity:
  30. """I am a terminal object in a hierarchy, with no children.
  31. I represent a null interface; certain non-instance objects (strings and
  32. integers, notably) are Entities.
  33. Methods on this class are suggested to be implemented, but are not
  34. required, and will be emulated on a per-protocol basis for types which do
  35. not handle them.
  36. """
  37. def render(self, request):
  38. """
  39. I produce a stream of bytes for the request, by calling request.write()
  40. and request.finish().
  41. """
  42. raise NotImplementedError("%s.render" % reflect.qual(self.__class__))
  43. class Collection:
  44. """I represent a static collection of entities.
  45. I contain methods designed to represent collections that can be dynamically
  46. created.
  47. """
  48. def __init__(self, entities=None):
  49. """Initialize me."""
  50. if entities is not None:
  51. self.entities = entities
  52. else:
  53. self.entities = {}
  54. def getStaticEntity(self, name):
  55. """Get an entity that was added to me using putEntity.
  56. This method will return 'None' if it fails.
  57. """
  58. return self.entities.get(name)
  59. def getDynamicEntity(self, name, request):
  60. """Subclass this to generate an entity on demand.
  61. This method should return 'None' if it fails.
  62. """
  63. def getEntity(self, name, request):
  64. """Retrieve an entity from me.
  65. I will first attempt to retrieve an entity statically; static entities
  66. will obscure dynamic ones. If that fails, I will retrieve the entity
  67. dynamically.
  68. If I cannot retrieve an entity, I will return 'None'.
  69. """
  70. ent = self.getStaticEntity(name)
  71. if ent is not None:
  72. return ent
  73. ent = self.getDynamicEntity(name, request)
  74. if ent is not None:
  75. return ent
  76. return None
  77. def putEntity(self, name, entity):
  78. """Store a static reference on 'name' for 'entity'.
  79. Raises a KeyError if the operation fails.
  80. """
  81. self.entities[name] = entity
  82. def delEntity(self, name):
  83. """Remove a static reference for 'name'.
  84. Raises a KeyError if the operation fails.
  85. """
  86. del self.entities[name]
  87. def storeEntity(self, name, request):
  88. """Store an entity for 'name', based on the content of 'request'."""
  89. raise NotSupportedError("%s.storeEntity" % reflect.qual(self.__class__))
  90. def removeEntity(self, name, request):
  91. """Remove an entity for 'name', based on the content of 'request'."""
  92. raise NotSupportedError("%s.removeEntity" % reflect.qual(self.__class__))
  93. def listStaticEntities(self):
  94. """Retrieve a list of all name, entity pairs that I store references to.
  95. See getStaticEntity.
  96. """
  97. return self.entities.items()
  98. def listDynamicEntities(self, request):
  99. """A list of all name, entity that I can generate on demand.
  100. See getDynamicEntity.
  101. """
  102. return []
  103. def listEntities(self, request):
  104. """Retrieve a list of all name, entity pairs I contain.
  105. See getEntity.
  106. """
  107. return self.listStaticEntities() + self.listDynamicEntities(request)
  108. def listStaticNames(self):
  109. """Retrieve a list of the names of entities that I store references to.
  110. See getStaticEntity.
  111. """
  112. return self.entities.keys()
  113. def listDynamicNames(self):
  114. """Retrieve a list of the names of entities that I store references to.
  115. See getDynamicEntity.
  116. """
  117. return []
  118. def listNames(self, request):
  119. """Retrieve a list of all names for entities that I contain.
  120. See getEntity.
  121. """
  122. return self.listStaticNames()
  123. class ConstraintViolation(Exception):
  124. """An exception raised when a constraint is violated."""
  125. class Constrained(Collection):
  126. """A collection that has constraints on its names and/or entities."""
  127. def nameConstraint(self, name):
  128. """A method that determines whether an entity may be added to me with a given name.
  129. If the constraint is satisfied, return 1; if the constraint is not
  130. satisfied, either return 0 or raise a descriptive ConstraintViolation.
  131. """
  132. return 1
  133. def entityConstraint(self, entity):
  134. """A method that determines whether an entity may be added to me.
  135. If the constraint is satisfied, return 1; if the constraint is not
  136. satisfied, either return 0 or raise a descriptive ConstraintViolation.
  137. """
  138. return 1
  139. def reallyPutEntity(self, name, entity):
  140. Collection.putEntity(self, name, entity)
  141. def putEntity(self, name, entity):
  142. """Store an entity if it meets both constraints.
  143. Otherwise raise a ConstraintViolation.
  144. """
  145. if self.nameConstraint(name):
  146. if self.entityConstraint(entity):
  147. self.reallyPutEntity(name, entity)
  148. else:
  149. raise ConstraintViolation("Entity constraint violated.")
  150. else:
  151. raise ConstraintViolation("Name constraint violated.")
  152. class Locked(Constrained):
  153. """A collection that can be locked from adding entities."""
  154. locked = 0
  155. def lock(self):
  156. self.locked = 1
  157. def entityConstraint(self, entity):
  158. return not self.locked
  159. class Homogenous(Constrained):
  160. """A homogenous collection of entities.
  161. I will only contain entities that are an instance of the class or type
  162. specified by my 'entityType' attribute.
  163. """
  164. entityType = object
  165. def entityConstraint(self, entity):
  166. if isinstance(entity, self.entityType):
  167. return 1
  168. else:
  169. raise ConstraintViolation(f"{entity} of incorrect type ({self.entityType})")
  170. def getNameType(self):
  171. return "Name"
  172. def getEntityType(self):
  173. return self.entityType.__name__