]> git.ipfire.org Git - thirdparty/python-drafthorse.git/commitdiff
Declare internal attributes as internal and prevent adding new attributes
authorRaphael Michel <michel@rami.io>
Wed, 3 Aug 2022 10:30:53 +0000 (12:30 +0200)
committerRaphael Michel <michel@rami.io>
Wed, 3 Aug 2022 10:30:53 +0000 (12:30 +0200)
drafthorse/models/container.py
drafthorse/models/elements.py
drafthorse/models/fields.py

index 857da8d9ee095d6e218f23f5af40db70fe679e32..8219378a1187d4423fd9f28d91d407afe9e2e309 100644 (file)
@@ -57,8 +57,8 @@ class CurrencyContainer(SimpleContainer):
         return CurrencyElement(namespace=self.namespace, tag=self.tag)
 
     def set_element(self, el, child):
-        el.amount = child[0]
-        el.currency = child[1]
+        el._amount = child[0]
+        el._currency = child[1]
 
     def add_from_etree(self, root):
         if root.attrib.get("currencyID"):
@@ -74,8 +74,8 @@ class IDContainer(SimpleContainer):
         return IDElement(namespace=self.namespace, tag=self.tag)
 
     def set_element(self, el, child):
-        el.text = child[1]
-        el.scheme_id = child[0]
+        el._text = child[1]
+        el._scheme_id = child[0]
 
     def add_from_etree(self, root):
         self.add((root.attrib["schemeID"], root.text))
@@ -88,7 +88,7 @@ class StringContainer(SimpleContainer):
         return StringElement(namespace=self.namespace, tag=self.tag)
 
     def set_element(self, el, child):
-        el.text = child
+        el._text = child
 
     def add_from_etree(self, root):
         self.add(root.text)
index f6c0c47bcd702daf53262fdcd6a46358aa8c7e3e..3d0062cab51bf06c8d2d2c1d9f329c350fffe72d 100644 (file)
@@ -70,6 +70,11 @@ class Element(metaclass=BaseElementMeta):
             self.to_etree(), "utf-8"
         )
         return validate_xml(xmlout=xml, schema=schema)
+    
+    def __setattr__(self, key, value):
+        if not hasattr(self, key) and not key.startswith("_") and not key in ("required",):
+            raise AttributeError(f"Element {type(self)} has no attribute '{key}'. If you set it, it would not be included in the output.")
+        return super().__setattr__(key, value)
 
     def from_etree(self, root):
         if (
@@ -110,10 +115,10 @@ class Element(metaclass=BaseElementMeta):
 class StringElement(Element):
     def __init__(self, namespace, tag, text=""):
         super().__init__()
-        self.namespace = namespace
-        self.tag = tag
-        self.text = text
-        self.set_on_input = False
+        self._namespace = namespace
+        self._tag = tag
+        self._text = text
+        self._set_on_input = False
 
     def __repr__(self):
         return "<{}: {}>".format(type(self).__name__, str(self))
@@ -122,82 +127,82 @@ class StringElement(Element):
         return str(self.text)
 
     def is_empty(self, el):
-        return super().is_empty(el) and not self.set_on_input
+        return super().is_empty(el) and not self._set_on_input
 
     def get_tag(self):
-        return "{%s}%s" % (self.namespace, self.tag)
+        return "{%s}%s" % (self._namespace, self._tag)
 
     def to_etree(self):
         node = self._etree_node()
-        node.text = self.text
+        node.text = self._text
         return node
 
     def from_etree(self, root):
-        self.text = root.text
-        self.set_on_input = True
+        self._text = root.text
+        self._set_on_input = True
         return self
 
 
 class DecimalElement(StringElement):
     def __init__(self, namespace, tag, value=None):
         super().__init__(namespace, tag)
-        self.value = value
+        self._value = value
 
     def to_etree(self):
         node = self._etree_node()
-        node.text = str(self.value) if self.value is not None else ""
+        node.text = str(self._value) if self._value is not None else ""
         return node
 
     def __str__(self):
         return self.value
 
     def from_etree(self, root):
-        self.value = Decimal(root.text)
-        self.set_on_input = True
+        self._value = Decimal(root.text)
+        self._set_on_input = True
         return self
 
 
 class QuantityElement(StringElement):
     def __init__(self, namespace, tag, amount="", unit_code=""):
         super().__init__(namespace, tag)
-        self.amount = amount
-        self.unit_code = unit_code
+        self._amount = amount
+        self._unit_code = unit_code
 
     def to_etree(self):
         node = self._etree_node()
-        node.text = str(self.amount)
-        node.attrib["unitCode"] = self.unit_code
+        node.text = str(self._amount)
+        node.attrib["unitCode"] = self._unit_code
         return node
 
     def __str__(self):
-        return "{} {}".format(self.amount, self.unit_code)
+        return "{} {}".format(self._amount, self._unit_code)
 
     def from_etree(self, root):
-        self.amount = Decimal(root.text)
-        self.unit_code = root.attrib["unitCode"]
-        self.set_on_input = True
+        self._amount = Decimal(root.text)
+        self._unit_code = root.attrib["unitCode"]
+        self._set_on_input = True
         return self
 
 
 class CurrencyElement(StringElement):
     def __init__(self, namespace, tag, amount="", currency=None):
         super().__init__(namespace, tag)
-        self.amount = amount
-        self.currency = currency
+        self._amount = amount
+        self._currency = currency
 
     def to_etree(self):
         node = self._etree_node()
-        node.text = str(self.amount)
-        if self.currency is not None:
-            node.attrib["currencyID"] = self.currency
+        node.text = str(self._amount)
+        if self._currency is not None:
+            node.attrib["currencyID"] = self._currency
         elif "currencyID" in node.attrib:
             del node.attrib["currencyID"]
         return node
 
     def from_etree(self, root):
-        self.amount = Decimal(root.text)
-        self.currency = root.attrib.get("currencyID") or None
-        self.set_on_input = True
+        self._amount = Decimal(root.text)
+        self._currency = root.attrib.get("currencyID") or None
+        self._set_on_input = True
         return self
 
     def __str__(self):
@@ -207,121 +212,121 @@ class CurrencyElement(StringElement):
 class ClassificationElement(StringElement):
     def __init__(self, namespace, tag, text="", list_id="", list_version_id=""):
         super().__init__(namespace, tag)
-        self.text = text
-        self.list_id = list_id
-        self.list_version_id = list_version_id
+        self._text = text
+        self._list_id = list_id
+        self._list_version_id = list_version_id
 
     def to_etree(self):
         node = self._etree_node()
         node.text = self.text
-        node.attrib["listID"] = self.list_id
-        node.attrib["listVersionID"] = self.list_version_id
+        node.attrib["listID"] = self._list_id
+        node.attrib["listVersionID"] = self._list_version_id
         return node
 
     def from_etree(self, root):
-        self.text = Decimal(root.text)
-        self.list_id = root.attrib["listID"]
-        self.list_version_id = root.attrib["listVersionID"]
-        self.set_on_input = True
+        self._text = Decimal(root.text)
+        self._list_id = root.attrib["listID"]
+        self._list_version_id = root.attrib["listVersionID"]
+        self._set_on_input = True
         return self
 
     def __str__(self):
-        return "{} ({} {})".format(self.text, self.list_id, self.list_version_id)
+        return "{} ({} {})".format(self._text, self._list_id, self._list_version_id)
 
 
 class BinaryObjectElement(StringElement):
     def __init__(self, namespace, tag, text="", filename="", mime_code=""):
         super().__init__(namespace, tag)
-        self.mime_code = mime_code
-        self.filename = filename
-        self.text = text
+        self._mime_code = mime_code
+        self._filename = filename
+        self._text = text
 
     def to_etree(self):
         node = self._etree_node()
-        node.attrib["mimeCode"] = self.mime_code
-        node.attrib["filename"] = self.filename
-        node.text = self.text
+        node.attrib["mimeCode"] = self._mime_code
+        node.attrib["filename"] = self._filename
+        node.text = self._text
         return node
 
     def from_etree(self, root):
-        self.mime_code = root.attrib["mimeCode"]
-        self.filename = root.attrib["filename"]
-        self.text = root.text
-        self.set_on_input = True
+        self._mime_code = root.attrib["mimeCode"]
+        self._filename = root.attrib["filename"]
+        self._text = root.text
+        self._set_on_input = True
         return self
 
     def __str__(self):
-        return "{} ({} {})".format(self.text, self.mime_code)
+        return "{} ({} {})".format(self._text, self._mime_code)
 
 
 class AgencyIDElement(StringElement):
     def __init__(self, namespace, tag, text="", scheme_id=""):
         super().__init__(namespace, tag)
-        self.text = text
-        self.scheme_id = scheme_id
+        self._text = text
+        self._scheme_id = scheme_id
 
     def to_etree(self):
         node = self._etree_node()
-        node.text = self.text
-        node.attrib["schemeAgencyID"] = self.scheme_id
+        node.text = self._text
+        node.attrib["schemeAgencyID"] = self._scheme_id
         return node
 
     def from_etree(self, root):
-        self.text = root.text
-        self.scheme_id = root.attrib["schemeAgencyID"]
-        self.set_on_input = True
+        self._text = root.text
+        self._scheme_id = root.attrib["schemeAgencyID"]
+        self._set_on_input = True
         return self
 
     def __str__(self):
-        return "{} ({})".format(self.text, self.scheme_id)
+        return "{} ({})".format(self._text, self._scheme_id)
 
 
 class IDElement(StringElement):
     def __init__(self, namespace, tag, text="", scheme_id=""):
         super().__init__(namespace, tag)
-        self.text = text
-        self.scheme_id = scheme_id
+        self._text = text
+        self._scheme_id = scheme_id
 
     def to_etree(self):
         node = self._etree_node()
-        node.text = self.text
-        node.attrib["schemeID"] = self.scheme_id
+        node.text = self._text
+        node.attrib["schemeID"] = self._scheme_id
         return node
 
     def from_etree(self, root):
-        self.text = root.text
+        self._text = root.text
         try:
-            self.scheme_id = root.attrib["schemeID"]
+            self._scheme_id = root.attrib["schemeID"]
         except:
             root.attrib["schemeID"] = ""
-            self.scheme_id = root.attrib["schemeID"]
-        self.set_on_input = True
+            self._scheme_id = root.attrib["schemeID"]
+        self._set_on_input = True
         return self
 
     def __str__(self):
-        return "{} ({})".format(self.text, self.scheme_id)
+        return "{} ({})".format(self._text, self._scheme_id)
 
 
 class DateTimeElement(StringElement):
     def __init__(self, namespace, tag, value=None, format="102"):
         super().__init__(namespace, tag)
-        self.value = value
-        self.format = format
+        self._value = value
+        self._format = format
 
     def to_etree(self):
         t = self._etree_node()
         node = ET.Element("{%s}%s" % (NS_UDT, "DateTimeString"))
-        if self.value:
-            if self.format == "102":
-                node.text = self.value.strftime("%Y%m%d")
-            elif self.format == "616":
+        if self._value:
+            if self._format == "102":
+                node.text = self._value.strftime("%Y%m%d")
+            elif self._format == "616":
                 if sys.version_info < (3, 6):
                     node.text = "{}{}".format(
-                        self.value.isocalendar()[0], self.value.isocalendar()[1]
+                        self._value.isocalendar()[0], self._value.isocalendar()[1]
                     )
                 else:
-                    node.text = self.value.strftime("%G%V")
-            node.attrib["format"] = self.format
+                    node.text = self._value.strftime("%G%V")
+            node.attrib["format"] = self._format
             t.append(node)
         return t
 
@@ -330,77 +335,76 @@ class DateTimeElement(StringElement):
             raise TypeError("Date containers should have one child")
         if root[0].tag != "{%s}%s" % (NS_UDT, "DateTimeString"):
             raise TypeError("Tag %s not recognized" % root[0].tag)
-        self.format = root[0].attrib["format"]
-        if self.format == "102":
-            self.value = datetime.strptime(root[0].text, "%Y%m%d").date()
-        elif self.format == "616":
+        self._format = root[0].attrib["format"]
+        if self._format == "102":
+            self._value = datetime.strptime(root[0].text, "%Y%m%d").date()
+        elif self._format == "616":
             if sys.version_info < (3, 6):
                 from isoweek import Week
 
                 w = Week(int(root[0].text[:4]), int(root[0].text[4:]))
-                self.value = w.monday()
+                self._value = w.monday()
             else:
-                self.value = datetime.strptime(root[0].text + "1", "%G%V%u").date()
+                self._value = datetime.strptime(root[0].text + "1", "%G%V%u").date()
         else:
             raise TypeError(
                 "Date format %s cannot be parsed" % root[0].attrib["format"]
             )
-        self.set_on_input = True
+        self._set_on_input = True
         return self
 
     def __str__(self):
-        return "{}".format(self.value)
+        return "{}".format(self._value)
 
 
 class DirectDateTimeElement(StringElement):
     def __init__(self, namespace, tag, value=None):
         super().__init__(namespace, tag)
-        self.value = value
-        self.format = format
+        self._value = value
 
     def to_etree(self):
         t = self._etree_node()
-        if self.value:
-            t.text = self.value.strftime("%Y-%m-%dT%H:%M:%S")
+        if self._value:
+            t.text = self._value.strftime("%Y-%m-%dT%H:%M:%S")
         return t
 
     def from_etree(self, root):
         try:
-            self.value = datetime.strptime(root.text, "%Y-%m-%dT%H:%M:%S").date()
+            self._value = datetime.strptime(root.text, "%Y-%m-%dT%H:%M:%S").date()
         except:
-            self.value = ""
-        self.set_on_input = True
+            self._value = ""
+        self._set_on_input = True
         return self
 
     def __str__(self):
-        return "{}".format(self.value)
+        return "{}".format(self._value)
 
 
 class IndicatorElement(StringElement):
     def __init__(self, namespace, tag, value=None):
         super().__init__(namespace, tag)
-        self.value = value
+        self._value = value
 
     def get_tag(self):
-        return "{%s}%s" % (self.namespace, self.tag)
+        return "{%s}%s" % (self._namespace, self._tag)
 
     def to_etree(self):
         t = self._etree_node()
-        if self.value is None:
+        if self._value is None:
             return t
         node = ET.Element("{%s}%s" % (NS_UDT, "Indicator"))
-        node.text = str(self.value).lower()
+        node.text = str(self._value).lower()
         t.append(node)
         return t
 
     def __str__(self):
-        return "{}".format(self.value)
+        return "{}".format(self._value)
 
     def from_etree(self, root):
         if len(root) != 1:
             raise TypeError("Indicator containers should have one child")
         if root[0].tag != "{%s}%s" % (NS_UDT, "Indicator"):
             raise TypeError("Tag %s not recognized" % root[0].tag)
-        self.value = root[0].text == "true"
-        self.set_on_input = True
+        self._value = root[0].text == "true"
+        self._set_on_input = True
         return self
index e4b49809ab3a9c28ec6fb5377aa7ee571ac9c8ef..bf238d7fc026314f6d1d836088e72fb5f6aa6571 100644 (file)
@@ -47,7 +47,7 @@ class StringField(Field):
     def __set__(self, instance, value):
         if instance._data.get(self.name, None) is None:
             instance._data[self.name] = self.initialize()
-        instance._data[self.name].text = value
+        instance._data[self.name]._text = value
 
 
 class AgencyIDField(Field):
@@ -69,8 +69,8 @@ class AgencyIDField(Field):
 
         if not isinstance(value, (tuple, list)):
             raise TypeError("Please pass a 2-tuple of scheme agency ID and ID.")
-        instance._data[self.name].text = value[1]
-        instance._data[self.name].scheme_id = value[0]
+        instance._data[self.name]._text = value[1]
+        instance._data[self.name]._scheme_id = value[0]
 
 
 class ClassificationField(Field):
@@ -92,9 +92,9 @@ class ClassificationField(Field):
 
         if not isinstance(value, (tuple, list)):
             raise TypeError("Please pass a 3-tuple of list ID, list version and ID.")
-        instance._data[self.name].text = value[2]
-        instance._data[self.name].list_id = value[0]
-        instance._data[self.name].list_version_id = value[1]
+        instance._data[self.name]._text = value[2]
+        instance._data[self.name]._list_id = value[0]
+        instance._data[self.name]._list_version_id = value[1]
 
 
 class IDField(Field):
@@ -116,8 +116,8 @@ class IDField(Field):
 
         if not isinstance(value, (tuple, list)):
             raise TypeError("Please pass a 2-tuple of including scheme ID and ID.")
-        instance._data[self.name].text = value[1]
-        instance._data[self.name].scheme_id = value[0]
+        instance._data[self.name]._text = value[1]
+        instance._data[self.name]._scheme_id = value[0]
 
 
 class CurrencyField(Field):
@@ -139,8 +139,8 @@ class CurrencyField(Field):
         if not isinstance(value, (tuple, list)):
             raise TypeError("Please pass a 2-tuple of including amount and currency.")
 
-        instance._data[self.name].amount = value[0]
-        instance._data[self.name].currency = value[1]
+        instance._data[self.name]._amount = value[0]
+        instance._data[self.name]._currency = value[1]
 
     def initialize(self):
         return self.cls(self.namespace, self.tag)
@@ -159,7 +159,7 @@ class DecimalField(Field):
     def __set__(self, instance, value):
         if instance._data.get(self.name, None) is None:
             instance._data[self.name] = self.initialize()
-        instance._data[self.name].value = value
+        instance._data[self.name]._value = value
 
     def initialize(self):
         return self.cls(self.namespace, self.tag)
@@ -181,8 +181,8 @@ class QuantityField(Field):
 
         if not isinstance(value, (tuple, list)):
             raise TypeError("Please pass a 2-tuple of including amount and unit code.")
-        instance._data[self.name].amount = value[0]
-        instance._data[self.name].unit_code = value[1]
+        instance._data[self.name]._amount = value[0]
+        instance._data[self.name]._unit_code = value[1]
 
     def initialize(self):
         return self.cls(self.namespace, self.tag)
@@ -204,9 +204,9 @@ class BinaryObjectField(Field):
 
         if not isinstance(value, (tuple, list)):
             raise TypeError("Please pass a 2-tuple of including amount and unit code.")
-        instance._data[self.name].text = value[2]
-        instance._data[self.name].mime_code = value[0]
-        instance._data[self.name].filename = value[1]
+        instance._data[self.name]._text = value[2]
+        instance._data[self.name]._mime_code = value[0]
+        instance._data[self.name]._filename = value[1]
 
     def initialize(self):
         return self.cls(self.namespace, self.tag)
@@ -225,7 +225,7 @@ class IndicatorField(Field):
     def __set__(self, instance, value):
         if instance._data.get(self.name, None) is None:
             instance._data[self.name] = self.initialize()
-        instance._data[self.name].value = value
+        instance._data[self.name]._value = value
 
     def initialize(self):
         return self.cls(self.namespace, self.tag)
@@ -244,7 +244,7 @@ class DateTimeField(Field):
     def __set__(self, instance, value):
         if instance._data.get(self.name, None) is None:
             instance._data[self.name] = self.initialize()
-        instance._data[self.name].value = value
+        instance._data[self.name]._value = value
 
     def initialize(self):
         return self.cls(self.namespace, self.tag)
@@ -263,7 +263,7 @@ class DirectDateTimeField(Field):
     def __set__(self, instance, value):
         if instance._data.get(self.name, None) is None:
             instance._data[self.name] = self.initialize()
-        instance._data[self.name].value = value
+        instance._data[self.name]._value = value
 
     def initialize(self):
         return self.cls(self.namespace, self.tag)