Description
Bug report
_Raise_Serialization_Error thrown when SubElement.Text attribute is never over-written from type(None) to type(Str)
#######################################################
Python version - Python v3.10
IDE - PyCharm 2021.2.4 Community Edition
Operating System - Windows 11 Pro version 10.0.22000 Build 22000
#######################################################
User Relevant Code Implementation:
from xml.etree.ElementTree import Element, SubElement, Comment
def __compile_to_xml__(self):
set_status_bar("Compiling XML File From Calculator")
root = Element('Root') # root of XML tree
root.set('version', '1.0')
comment = Comment('Indicon Corporation Compiled Power Distribution Panel XML Generated File') # root comment
root.append(comment)
body = SubElement(root, 'body')
# system name
system_name = SubElement(body, 'SystemName', {'SystemName': self._system_name})
# panel name
panel_name = SubElement(body, "PanelName", {'PanelName': self._panel_name})
# max panel amps
max_amps = SubElement(body, "MaxAmps", {'MaxAmps': self._max_amps.__str__()})
# current amps
current_amps = SubElement(body, "CurrentAmps", {'CurrentAmps': self._current_amps.__str__()})
# 480 breakers
c480_breakers = SubElement(body, "c480Breakers")
for breaker in self._vac480_breakers:
new_breaker = SubElement(c480_breakers, "c480Breaker", {'Name': breaker.name, 'Size': breaker.breaker_size.__str__(), 'Amps': breaker.current_amps.__str__()})
for child in breaker.children_devices:
new_child_device = SubElement(new_breaker, "Child_Device", {'Name': child.name, 'AmpDraw': child.amp_draw.__str__()})
# 120 transformers
c120_xformers = SubElement(body, "c120Transformers")
for xformer in self._vac120_xfmrs:
new_xformer = SubElement(c120_xformers, "c120Transformer", {'Name': xformer.name, 'Amps': xformer.current_amps.__str__()})
for child in xformer.children_devices:
new_child_device = SubElement(new_xformer, "c120Breaker", {'Name': child.name, 'Amps': child.current_amps.__str__()})
for device in child.children_devices:
new_device = SubElement(new_child_device, "Child_Device", {'Name': child.name, 'AmpDraw': child.amp_draw.__str__()})
self._xml_stream = prettify(root) <--- THIS IS THE FAILURE POINT ##########################
#######################################################
EDIT: Including "prettyiffy(root) function call for transparency
NOTE: standard library function "tostring" also throws the same error on inspect of "text" attribute
from xml.etree import ElementTree
from xml.dom import minidom
def prettify(elem):
"""Return a pretty-printed XML string for the Element.
"""
rough_string = ElementTree.tostring(elem, 'utf-8')
reparsed = minidom.parseString(rough_string)
return reparsed.toprettyxml(indent=" ")
#######################################################
ElementTree.py code:
def _escape_attrib(text):
# escape attribute value
if text is None: # irox addition, crashing if "text" field is never assigned to SubElement <--- This Line Added
return <--- This Line Added ##########################
try:
if "&" in text:
text = text.replace("&", "&")
if "<" in text:
text = text.replace("<", "<")
if ">" in text:
text = text.replace(">", ">")
if """ in text:
text = text.replace(""", """)
# Although section 2.11 of the XML specification states that CR or
# CR LN should be replaced with just LN, it applies only to EOLNs
# which take part of organizing file into lines. Within attributes,
# we are replacing these with entity numbers, so they do not count.
# http://www.w3.org/TR/REC-xml/#sec-line-ends
# The current solution, contained in following six lines, was
# discussed in issue 17582 and 39011.
if "\r" in text:
text = text.replace("\r", "
")
if "\n" in text:
text = text.replace("\n", "
")
if "\t" in text:
text = text.replace("\t", " ")
return text
except (TypeError, AttributeError):
_raise_serialization_error(text)
#######################################################
Root Cause (from my assumption, while only viewing as an include)
class Element:
"""An XML element.
This class is the reference implementation of the Element interface.
An element's length is its number of subelements. That means if you
want to check if an element is truly empty, you should check BOTH
its length AND its text attribute.
The element tag, attribute names, and attribute values can be either
bytes or strings.
*tag* is the element name. *attrib* is an optional dictionary containing
element attributes. *extra* are additional element attributes given as
keyword arguments.
Example form:
<tag attrib>text<child/>...</tag>tail
"""
tag = None
"""The element's name."""
attrib = None
"""Dictionary of the element's attributes."""
text = None <--- Default as "None", fails typecheck on type(str).replace() ##########################
"""
Text before first subelement. This is either a string or the value None.
Note that if there is no text, this attribute may be either
None or the empty string, depending on the parser.
"""
tail = None
"""
Text after this element's end tag, but before the next sibling element's
start tag. This is either a string or the value None. Note that if there
was no text, this attribute may be either None or an empty string,
depending on the parser.
"""
#######################################################
As seen above, the code i'm working on will crash natively unless the "Text" attribute is assinged per Element/SubElement.
The most simple fix i could provide for myself was a type check in the standard library on this function call.
I figured i could put this "bug" as a report on this page in hopes that either A) i'm terrible and seemingly missed something else i was supposed to do with the library or B) as a simple fix to help others who may run into the same issue.