sequence.py 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. from __future__ import absolute_import
  2. # Copyright (c) 2010-2019 openpyxl
  3. from openpyxl.compat import safe_string
  4. from openpyxl.xml.functions import Element
  5. from openpyxl.utils.indexed_list import IndexedList
  6. from .base import Descriptor, Alias, _convert
  7. from .namespace import namespaced
  8. class Sequence(Descriptor):
  9. """
  10. A sequence (list or tuple) that may only contain objects of the declared
  11. type
  12. """
  13. expected_type = type(None)
  14. seq_types = (list, tuple)
  15. idx_base = 0
  16. unique = False
  17. def __set__(self, instance, seq):
  18. if not isinstance(seq, self.seq_types):
  19. raise TypeError("Value must be a sequence")
  20. seq = [_convert(self.expected_type, value) for value in seq]
  21. if self.unique:
  22. seq = IndexedList(seq)
  23. super(Sequence, self).__set__(instance, seq)
  24. def to_tree(self, tagname, obj, namespace=None):
  25. """
  26. Convert the sequence represented by the descriptor to an XML element
  27. """
  28. for idx, v in enumerate(obj, self.idx_base):
  29. if hasattr(v, "to_tree"):
  30. el = v.to_tree(tagname, idx)
  31. else:
  32. tagname = namespaced(obj, tagname, namespace)
  33. el = Element(tagname)
  34. el.text = safe_string(v)
  35. yield el
  36. class ValueSequence(Sequence):
  37. """
  38. A sequence of primitive types that are stored as a single attribute.
  39. "val" is the default attribute
  40. """
  41. attribute = "val"
  42. def to_tree(self, tagname, obj, namespace=None):
  43. tagname = namespaced(self, tagname, namespace)
  44. for v in obj:
  45. yield Element(tagname, {self.attribute:safe_string(v)})
  46. def from_tree(self, node):
  47. return node.get(self.attribute)
  48. class NestedSequence(Sequence):
  49. """
  50. Wrap a sequence in an containing object
  51. """
  52. count = False
  53. def to_tree(self, tagname, obj, namespace=None):
  54. tagname = namespaced(self, tagname, namespace)
  55. container = Element(tagname)
  56. if self.count:
  57. container.set('count', str(len(obj)))
  58. for v in obj:
  59. container.append(v.to_tree())
  60. return container
  61. def from_tree(self, node):
  62. return [self.expected_type.from_tree(el) for el in node]
  63. class MultiSequence(Sequence):
  64. """
  65. Sequences can contain objects with different tags
  66. """
  67. def __set__(self, instance, seq):
  68. if not isinstance(seq, (tuple, list)):
  69. raise ValueError("Value must be a sequence")
  70. seq = list(seq)
  71. Descriptor.__set__(self, instance, seq)
  72. def to_tree(self, tagname, obj, namespace=None):
  73. """
  74. Convert the sequence represented by the descriptor to an XML element
  75. """
  76. for v in obj:
  77. el = v.to_tree(namespace=namespace)
  78. yield el
  79. class MultiSequencePart(Alias):
  80. """
  81. Allow a multisequence to be built up from parts
  82. Excluded from the instance __elements__ or __attrs__ as is effectively an Alias
  83. """
  84. def __init__(self, expected_type, store):
  85. self.expected_type = expected_type
  86. self.store = store
  87. def __set__(self, instance, value):
  88. value = _convert(self.expected_type, value)
  89. instance.__dict__[self.store].append(value)
  90. def __get__(self, instance, cls):
  91. return self