Repository navigation
Register namespaces used only inside extensions before serializing - #297
Closed
afonsojanu wants to merge 1 commit into
Closed
afonsojanu wants to merge 1 commit into
afonsojanu wants to merge 1 commit into
Conversation
An extension element can declare its own default namespace that is
never mentioned anywhere else in the document, the classic example
being a Garmin gpx_style block:
<extensions>
<line xmlns="http://www.topografix.com/GPX/gpx_style/0/2">
<color>000000</color>
</line>
</extensions>
to_xml() only knows how to write prefixes for namespaces already
present in nsmap, and nothing ever adds this one there, since it only
shows up nested inside an extensions element rather than on the gpx
root or one of the other namespaced attributes the parser scans for.
When _resolve_prefix in gpxfield.py hits a Clark-notation tag whose
namespace it cannot find, it falls back to gluing the raw URI onto
the local name as if it were a prefix, producing something like
<http://www.topografix.com/GPX/gpx_style/0/2:line>
which is not well formed XML and cannot be parsed back at all. Three
separate users ran into this over the years on issue #234, each with
real files produced by different GPS hardware or software, and the
maintainer confirmed it and even committed a failing test for it back
in 2021 without getting the chance to fix it.
This adds GPX._register_extension_namespaces, called from to_xml
before the root element is written, which walks every extensions list
in the document (root, metadata, waypoints, routes and their points,
tracks and their segments and points), including nested children such
as the line element above, and assigns a fresh nsN prefix to any
namespace it finds that isn't already in nsmap. That prefix then gets
declared normally on the gpx root alongside the others, and
_resolve_prefix can find it like any other namespace.
Added a regression test with the same shape as the maintainer's 2021
one (adapted since ElementTree.getchildren() no longer exists) that
checks the output stays parseable and the extension data survives a
round trip.
Author
|
Closing this one too, same reason: starting fresh and clearing out everything I had open. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #234.
An extension element can declare its own default namespace that never shows up anywhere else in the document. The classic real-world example, reported independently by three different users on this issue over the years, is a Garmin gpx_style block:
to_xml()can only write a prefix for a namespace that's already innsmap, and nothing currently puts this one there, since it only appears nested inside anextensionselement rather than on thegpxroot or one of the other attributes the parser's regex scans for. When_resolve_prefixingpxfield.pyhits a Clark-notation tag whose namespace it can't find, it falls back to gluing the raw URI onto the local name as if it were a prefix:which isn't well formed XML and can't be parsed back at all. I confirmed this against current
devwith the file from the issue: the round trip throughto_xml()and back raisesParseError: not well-formed (invalid token).@tkrajina had already dug into the root cause and committed a failing test for this back in 2021 (
test_extension_with_xmlns, referenced in the issue thread) but never got to a fix.This adds
GPX._register_extension_namespaces, called fromto_xmlright before the root element is written. It walks every extensions list in the document, including the root, metadata, waypoints, routes and their points, tracks and their segments and points, and recurses into nested children such as thelineelement above. Any namespace it finds that isn't already innsmapgets a freshnsNprefix assigned to it, which is then declared normally on thegpxroot alongside the rest, so_resolve_prefixcan resolve it like any other namespace instead of falling back to the broken path.I've added a regression test with the same shape as the maintainer's 2021 one (adapted since
ElementTree.getchildren()no longer exists on current Python), checking that the serialized output stays parseable and the extension data survives the round trip. Full test suite (146 tests) andmypy --strictboth pass with no new findings compared todev.