sequence.py 3.4 KB

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