mirror of
https://github.com/ansible-collections/community.general.git
synced 2026-06-14 03:55:37 +00:00
[PR #12148/5004c9f7 backport][stable-13] xml: preserve DOCTYPE declaration when writing XML files (#12249)
xml: preserve DOCTYPE declaration when writing XML files (#12148)
* fix(xml): preserve DOCTYPE declaration when writing XML files
Pass `doctype=tree.docinfo.doctype` to all `ElementTree.write()` calls
so lxml does not silently drop the DOCTYPE on serialization. Also replace
`etree.tostring()` with BytesIO+write() in the diff and xmlstring paths
for consistency.
Fixes #2762
* test(xml): add integration test for DOCTYPE preservation
* feat(changelog): add fragment for xml DOCTYPE fix (#12148)
---------
(cherry picked from commit 5004c9f70f)
Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
58d8bbf164
commit
3bc92d03c4
8 changed files with 99 additions and 8 deletions
2
changelogs/fragments/12148-xml-preserve-doctype.yml
Normal file
2
changelogs/fragments/12148-xml-preserve-doctype.yml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
bugfixes:
|
||||
- "xml - preserve DOCTYPE declaration when writing modified XML files (https://github.com/ansible-collections/community.general/issues/2762, https://github.com/ansible-collections/community.general/pull/12148)."
|
||||
|
|
@ -813,9 +813,15 @@ def children_to_nodes(module=None, children=None, type="yaml"):
|
|||
|
||||
|
||||
def make_pretty(module, tree):
|
||||
xml_string = etree.tostring(
|
||||
tree, xml_declaration=True, encoding="UTF-8", pretty_print=module.params["pretty_print"]
|
||||
buf = BytesIO()
|
||||
tree.write(
|
||||
buf,
|
||||
xml_declaration=True,
|
||||
encoding="UTF-8",
|
||||
pretty_print=module.params["pretty_print"],
|
||||
doctype=tree.docinfo.doctype or None,
|
||||
)
|
||||
xml_string = buf.getvalue()
|
||||
|
||||
result = dict(
|
||||
changed=False,
|
||||
|
|
@ -830,7 +836,11 @@ def make_pretty(module, tree):
|
|||
if module.params["backup"]:
|
||||
result["backup_file"] = module.backup_local(module.params["path"])
|
||||
tree.write(
|
||||
xml_file, xml_declaration=True, encoding="UTF-8", pretty_print=module.params["pretty_print"]
|
||||
xml_file,
|
||||
xml_declaration=True,
|
||||
encoding="UTF-8",
|
||||
pretty_print=module.params["pretty_print"],
|
||||
doctype=tree.docinfo.doctype or None,
|
||||
)
|
||||
|
||||
elif module.params["xmlstring"]:
|
||||
|
|
@ -859,10 +869,23 @@ def finish(module, tree, xpath, namespaces, changed=False, msg="", hitcount=0, m
|
|||
|
||||
if result["changed"]:
|
||||
if module._diff:
|
||||
result["diff"] = dict(
|
||||
before=etree.tostring(orig_doc, xml_declaration=True, encoding="UTF-8", pretty_print=True),
|
||||
after=etree.tostring(tree, xml_declaration=True, encoding="UTF-8", pretty_print=True),
|
||||
before_buf = BytesIO()
|
||||
orig_doc.write(
|
||||
before_buf,
|
||||
xml_declaration=True,
|
||||
encoding="UTF-8",
|
||||
pretty_print=True,
|
||||
doctype=orig_doc.docinfo.doctype or None,
|
||||
)
|
||||
after_buf = BytesIO()
|
||||
tree.write(
|
||||
after_buf,
|
||||
xml_declaration=True,
|
||||
encoding="UTF-8",
|
||||
pretty_print=True,
|
||||
doctype=tree.docinfo.doctype or None,
|
||||
)
|
||||
result["diff"] = dict(before=before_buf.getvalue(), after=after_buf.getvalue())
|
||||
|
||||
if module.params["path"] and not module.check_mode:
|
||||
if module.params["backup"]:
|
||||
|
|
@ -872,12 +895,19 @@ def finish(module, tree, xpath, namespaces, changed=False, msg="", hitcount=0, m
|
|||
xml_declaration=True,
|
||||
encoding="UTF-8",
|
||||
pretty_print=module.params["pretty_print"],
|
||||
doctype=tree.docinfo.doctype or None,
|
||||
)
|
||||
|
||||
if module.params["xmlstring"]:
|
||||
result["xmlstring"] = etree.tostring(
|
||||
tree, xml_declaration=True, encoding="UTF-8", pretty_print=module.params["pretty_print"]
|
||||
xmlstring_buf = BytesIO()
|
||||
tree.write(
|
||||
xmlstring_buf,
|
||||
xml_declaration=True,
|
||||
encoding="UTF-8",
|
||||
pretty_print=module.params["pretty_print"],
|
||||
doctype=tree.docinfo.doctype or None,
|
||||
)
|
||||
result["xmlstring"] = xmlstring_buf.getvalue()
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<!DOCTYPE foo SYSTEM "foo.dtd">
|
||||
<foo value="0"/>
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
SPDX-FileCopyrightText: Ansible Project
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<!DOCTYPE foo SYSTEM "foo.dtd">
|
||||
<foo value="1"/>
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
SPDX-FileCopyrightText: Ansible Project
|
||||
|
|
@ -79,6 +79,7 @@
|
|||
- include_tasks: test-print-match.yml
|
||||
- include_tasks: test-xmlstring.yml
|
||||
- include_tasks: test-children-elements-xml.yml
|
||||
- include_tasks: test-preserve-doctype.yml
|
||||
|
||||
# Unicode tests
|
||||
- include_tasks: test-add-children-elements-unicode.yml
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
# Copyright (c) Ansible Project
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
- name: Setup test fixture
|
||||
copy:
|
||||
src: fixtures/ansible-xml-doctype.xml
|
||||
dest: /tmp/ansible-xml-doctype.xml
|
||||
|
||||
- name: Set '/foo/@value' to '1' (file has DOCTYPE)
|
||||
xml:
|
||||
path: /tmp/ansible-xml-doctype.xml
|
||||
xpath: /foo
|
||||
attribute: value
|
||||
value: "1"
|
||||
register: set_attribute_doctype
|
||||
|
||||
- name: Add trailing newline
|
||||
shell: echo "" >> /tmp/ansible-xml-doctype.xml
|
||||
|
||||
- name: Compare to expected result
|
||||
copy:
|
||||
src: results/test-preserve-doctype.xml
|
||||
dest: /tmp/ansible-xml-doctype.xml
|
||||
check_mode: true
|
||||
diff: true
|
||||
register: comparison
|
||||
|
||||
- name: Test expected result (DOCTYPE is preserved)
|
||||
assert:
|
||||
that:
|
||||
- set_attribute_doctype is changed
|
||||
- comparison is not changed
|
||||
|
||||
- name: Set '/foo/@value' to '1' again (idempotency)
|
||||
xml:
|
||||
path: /tmp/ansible-xml-doctype.xml
|
||||
xpath: /foo
|
||||
attribute: value
|
||||
value: "1"
|
||||
register: set_attribute_doctype_idempotent
|
||||
|
||||
- name: Test idempotency
|
||||
assert:
|
||||
that:
|
||||
- set_attribute_doctype_idempotent is not changed
|
||||
Loading…
Add table
Add a link
Reference in a new issue