diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 538106e..8ad9b57 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -60,6 +60,8 @@ jobs: name: Publish to PyPI needs: build runs-on: [ubuntu-latest] + permissions: + id-token: write if: github.event_name != 'pull_request' steps: - uses: actions/download-artifact@v3 @@ -73,11 +75,8 @@ jobs: - name: Test Publish package uses: pypa/gh-action-pypi-publish@release/v1 with: - password: ${{ secrets.SHARED_PYPI_TEST_TOKEN }} - repository_url: https://test.pypi.org/legacy/ + repository-url: https://test.pypi.org/legacy/ - name: Publish package uses: pypa/gh-action-pypi-publish@release/v1 if: startsWith(github.event.ref, 'refs/tags/v') - with: - password: ${{ secrets.SHARED_PYPI_TOKEN }} diff --git a/llsd/serde_xml.py b/llsd/serde_xml.py index 99d39b8..7dfeaa2 100644 --- a/llsd/serde_xml.py +++ b/llsd/serde_xml.py @@ -44,17 +44,13 @@ def _elt(self, name, contents=None): If 'contents' is omitted, write . If 'contents' is bytes, write contents. If 'contents' is str, write contents.encode('utf8'). - If 'contents' is callable, write , call contents(), write . """ if not contents: self.stream.writelines([b"<", name, b" />"]) else: - self.stream.writelines([b"<", name, b">"]) - if callable(contents): - contents() - else: - self.stream.write(_str_to_bytes(contents)) - self.stream.writelines([b""]) + self.stream.writelines([b"<", name, b">", + _str_to_bytes(contents), + b""]) def xml_esc(self, v): "Escape string or unicode object v for xml output" @@ -100,15 +96,16 @@ def _URI(self, v): def _DATE(self, v): return self._elt(b'date', _format_datestr(v)) def _ARRAY(self, v): - return self._elt( - b'array', - lambda: [self._generate(item) for item in v]) + self.stream.write(b'') + for item in v: + self._generate(item) + self.stream.write(b'') def _MAP(self, v): - return self._elt( - b'map', - lambda: [(self._elt(b'key', self.xml_esc(UnicodeType(key))), - self._generate(value)) - for key, value in v.items()]) + self.stream.write(b'') + for key, value in v.items(): + self._elt(b'key', self.xml_esc(UnicodeType(key))) + self._generate(value) + self.stream.write(b'') def _generate(self, something): "Generate xml from a single python object." @@ -127,8 +124,10 @@ def _write(self, something): :param something: A python object (typically a dict) to be serialized. """ - self.stream.write(b'') - self._elt(b"llsd", lambda: self._generate(something)) + self.stream.write(b'' + b'') + self._generate(something) + self.stream.write(b'') class LLSDXMLPrettyFormatter(LLSDXMLFormatter): diff --git a/tests/llsd_test.py b/tests/llsd_test.py index 6691c1c..46f64fe 100644 --- a/tests/llsd_test.py +++ b/tests/llsd_test.py @@ -1345,6 +1345,20 @@ def testMap(self): map_within_map_xml) self.assertXMLRoundtrip({}, blank_map_xml) + def testDeepMap(self): + """ + Test that formatting a deeply nested map does not cause a RecursionError + """ + + test_map = {"foo":"bar", "depth":0, "next":None} + max_depth = 200 + for depth in range(max_depth): + test_map = {"foo":"bar", "depth":depth, "next":test_map} + + # this should not throw an exception. + test_xml = self.llsd.as_xml(test_map) + + def testBinary(self): """ Test the parse and serialization of input type : binary.