Guest User

Untitled

a guest
Nov 22nd, 2017
149
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 4.08 KB | None | 0 0
  1. """
  2. This module implements a record facility in Python. The aim is to simplify the
  3. case where you want a simple data class, which is used mainly to store data,
  4. and you don't want to implement the regular python boilerplate of:
  5.  
  6. * Assigning fields in the constructors
  7. * Sanity checking number of arguments, types, optional or not, etc
  8. * Implementing repr
  9. * Implementing hash and structural equality (not yet implemented)
  10.  
  11. Here is how you declare a basic record
  12.  
  13. >>> class Point(Record):
  14. ... x = Field(type=int)
  15. ... y = Field(type=int)
  16.  
  17. And here is how you use it
  18.  
  19. >>> p = Point(12, 15)
  20. >>> p
  21. <Point x=12, y=15>
  22.  
  23. You can use keyword associations
  24.  
  25. >>> Point(y=15, x=12)
  26. <Point x=12, y=15>
  27.  
  28. If you pass too much or too few arguments, or you pass an association twice,
  29. you will get errors.
  30.  
  31. >>> Point(1, 2, 3)
  32. Traceback (most recent call last):
  33. ...
  34. RecordError: Too many args to init
  35.  
  36. >>> Point(1, x=2)
  37. Traceback (most recent call last):
  38. ...
  39. RecordError: Field already set: x
  40.  
  41. >>> Point(x=2)
  42. Traceback (most recent call last):
  43. ...
  44. RecordError: Missing field: y
  45.  
  46. Fields are inherited so you can create hierarchies of records. The order of
  47. fields, relevant for initialization, is from the base class to the most
  48. specific derivation.
  49.  
  50. >>> class Node(Record):
  51. ... name = Field(type=str, optional=True)
  52.  
  53. >>> class Add(Node):
  54. ... left = Field(type=Node)
  55. ... right = Field(type=Node)
  56.  
  57. >>> class Int(Node):
  58. ... val = Field(type=int)
  59.  
  60. >>> Add("add_1", Int(val=12), Int(val=15))
  61. <Add name=add_1, left=<Int name=None, val=12>, right=<Int name=None, val=15>>
  62. """
  63.  
  64. from itertools import count
  65.  
  66.  
  67. class Field(object):
  68. _counter = iter(count(0))
  69.  
  70. def __init__(self, type=None, optional=False):
  71. self._index = next(self._counter)
  72. self._name = None
  73. self.optional = optional
  74. self.type = type
  75.  
  76. def set_val(self, record, val):
  77. setattr(record, self._name, val)
  78.  
  79. def get_val(self, record):
  80. return getattr(record, self._name, None)
  81.  
  82.  
  83. class RecordError(Exception):
  84. pass
  85.  
  86.  
  87. class RecordMC(type):
  88. def __new__(mcs, name, bases, dct):
  89. cls = type.__new__(mcs, name, bases, dct)
  90.  
  91. fields = []
  92. for k, v in dct.items():
  93. if isinstance(v, Field):
  94. v._name = k
  95. fields.append(v)
  96.  
  97. fields = sorted(fields, key=lambda f: f._index)
  98. fields_dict = {f._name: f for f in fields}
  99.  
  100. cls._own_fields = fields
  101. cls._own_fields_dict = fields_dict
  102.  
  103. return cls
  104.  
  105.  
  106. class Record(object):
  107. __metaclass__ = RecordMC
  108.  
  109. def __init__(self, *args, **kwargs):
  110. set_fields = set()
  111. for i, arg in enumerate(args):
  112. try:
  113. f = self._fields()[i]
  114. f.set_val(self, arg)
  115. set_fields.add(f)
  116. except IndexError:
  117. raise RecordError("Too many args to init")
  118.  
  119. for k, v in kwargs.items():
  120. try:
  121. f = self._fields_dict()[k]
  122. if f in set_fields:
  123. raise RecordError("Field already set: {}".format(f._name))
  124. f.set_val(self, v)
  125. set_fields.add(f)
  126. except KeyError:
  127. raise RecordError("Wrong field name: {}".format(k))
  128.  
  129. rem = set(self._fields()) - set_fields
  130.  
  131. if rem:
  132. for f in rem:
  133. if f.optional:
  134. f.set_val(self, None)
  135. else:
  136. raise RecordError("Missing field: {}".format(f._name))
  137.  
  138. def __repr__(self):
  139. return "<{} {}>".format(self.__class__.__name__, ", ".join(
  140. "{}={}".format(f._name, f.get_val(self))
  141. for f in self._fields()
  142. ))
  143.  
  144. @classmethod
  145. def _fields(cls):
  146. b = cls.__bases__[0]
  147. if b is object:
  148. return []
  149. return b._fields() + cls._own_fields
  150.  
  151. @classmethod
  152. def _fields_dict(cls):
  153. b = cls.__bases__[0]
  154. if b is object:
  155. return {}
  156. return dict(b._fields_dict(), **cls._own_fields_dict)
  157.  
  158. def is_a(self, cls):
  159. return isinstance(self, cls)
  160.  
  161.  
  162. if __name__ == "__main__":
  163. import doctest
  164. doctest.testmod()
Add Comment
Please, Sign In to add comment