Skip to content

Commit a63abbe

Browse files
Merge pull request #13 from secondlife/log/deep_map
SL-18330: In XML formatter, avoid adding call stack depth.
2 parents 46fdce0 + 674f8aa commit a63abbe

File tree

2 files changed

+30
-17
lines changed

2 files changed

+30
-17
lines changed

llsd/serde_xml.py

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -44,17 +44,13 @@ def _elt(self, name, contents=None):
4444
If 'contents' is omitted, write <name/>.
4545
If 'contents' is bytes, write <name>contents</name>.
4646
If 'contents' is str, write <name>contents.encode('utf8')</name>.
47-
If 'contents' is callable, write <name>, call contents(), write </name>.
4847
"""
4948
if not contents:
5049
self.stream.writelines([b"<", name, b" />"])
5150
else:
52-
self.stream.writelines([b"<", name, b">"])
53-
if callable(contents):
54-
contents()
55-
else:
56-
self.stream.write(_str_to_bytes(contents))
57-
self.stream.writelines([b"</", name, b">"])
51+
self.stream.writelines([b"<", name, b">",
52+
_str_to_bytes(contents),
53+
b"</", name, b">"])
5854

5955
def xml_esc(self, v):
6056
"Escape string or unicode object v for xml output"
@@ -100,15 +96,16 @@ def _URI(self, v):
10096
def _DATE(self, v):
10197
return self._elt(b'date', _format_datestr(v))
10298
def _ARRAY(self, v):
103-
return self._elt(
104-
b'array',
105-
lambda: [self._generate(item) for item in v])
99+
self.stream.write(b'<array>')
100+
for item in v:
101+
self._generate(item)
102+
self.stream.write(b'</array>')
106103
def _MAP(self, v):
107-
return self._elt(
108-
b'map',
109-
lambda: [(self._elt(b'key', self.xml_esc(UnicodeType(key))),
110-
self._generate(value))
111-
for key, value in v.items()])
104+
self.stream.write(b'<map>')
105+
for key, value in v.items():
106+
self._elt(b'key', self.xml_esc(UnicodeType(key)))
107+
self._generate(value)
108+
self.stream.write(b'</map>')
112109

113110
def _generate(self, something):
114111
"Generate xml from a single python object."
@@ -127,8 +124,10 @@ def _write(self, something):
127124
128125
:param something: A python object (typically a dict) to be serialized.
129126
"""
130-
self.stream.write(b'<?xml version="1.0" ?>')
131-
self._elt(b"llsd", lambda: self._generate(something))
127+
self.stream.write(b'<?xml version="1.0" ?>'
128+
b'<llsd>')
129+
self._generate(something)
130+
self.stream.write(b'</llsd>')
132131

133132

134133
class LLSDXMLPrettyFormatter(LLSDXMLFormatter):

tests/llsd_test.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1345,6 +1345,20 @@ def testMap(self):
13451345
map_within_map_xml)
13461346
self.assertXMLRoundtrip({}, blank_map_xml)
13471347

1348+
def testDeepMap(self):
1349+
"""
1350+
Test that formatting a deeply nested map does not cause a RecursionError
1351+
"""
1352+
1353+
test_map = {"foo":"bar", "depth":0, "next":None}
1354+
max_depth = 200
1355+
for depth in range(max_depth):
1356+
test_map = {"foo":"bar", "depth":depth, "next":test_map}
1357+
1358+
# this should not throw an exception.
1359+
test_xml = self.llsd.as_xml(test_map)
1360+
1361+
13481362
def testBinary(self):
13491363
"""
13501364
Test the parse and serialization of input type : binary.

0 commit comments

Comments
 (0)