Skip to content

Commit

Permalink
fix: handle namespaced attributes
Browse files Browse the repository at this point in the history
Similar to namespaced tags, we also convert namespaced attributes into
their corresponding ComponentTypes.

Otherwise, we get errors of this form:

```
Parsing error: not well-formed (invalid token)
at: onent id="rdf_Description" type="rdf_Description" {http://www.w3.org/1999/02/22-rdf-syntax-ns}about=
at: --------------------------------------------------^
Traceback (most recent call last):
  File "/home/asinha/Documents/02_Code/00_mine/models/Human-L2-3-Cortical-Microcircuit/NeuroML2/pyneuroml_20250205172314_LMX8TG/pyneuroml_20250205172314_LMX8TG_generated/../../nml_v2spikes.py", line 22, in <module>
    print(model.export_to_dom().toprettyxml())
          ^^^^^^^^^^^^^^^^^^^^^
  File "/home/asinha/.local/share/virtualenvs/hl23/lib/python3.11/site-packages/lems/model/model.py", line 369, in export_to_dom
    xmldom = minidom.parseString(xmlstr)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib64/python3.11/xml/dom/minidom.py", line 2000, in parseString
    return expatbuilder.parseString(string)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib64/python3.11/xml/dom/expatbuilder.py", line 925, in parseString
    return builder.parseString(string)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib64/python3.11/xml/dom/expatbuilder.py", line 223, in parseString
    parser.Parse(string, True)
xml.parsers.expat.ExpatError: not well-formed (invalid token): line 1, column 10180222

```

A note here is that we lose namespace information, and if a loaded model
is thus exported, it will be different from the imported text. Once the
model has been loaded here, it has been "LEMSified", in the sense that
it has been converted to LEMS's XML language, which does not support
namespaces.

Thus, there should not be an expectation that a model loaded into PyLEMS
will be exported in the same form. It will not.

I do not know if we want that to be the case. If we do, we will need to
update our parsing to not strip out namespaces and so on.

I also have some queries regarding this code:

- I do not know why we first convert the XML to a DOM to then export it
  to XML: is the DOM used anywhere else?
- We seem to be mixing xml.ElementTree and xml.minidom here too: the
  `parse` method in LEMS.py uses ElementTree to load the XML text, but
  then creates the `LEMSXMLNode` of it to process it.
  • Loading branch information
sanjayankur31 committed Feb 6, 2025
1 parent ea31aa7 commit 0612b1d
Showing 1 changed file with 52 additions and 2 deletions.
54 changes: 52 additions & 2 deletions lems/parser/LEMS.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,50 @@
from lems.model.structure import *


def get_nons_attribute(attribute):
"""Get attributes without namespace prefixes.
The parsed attributes will include their namespaces in {}:
eg: `{some_namespace_url}attribute=value`
Similar to tags, we replace these with their corresponding LEMS component
types.
:param attribute: attribute to strip namespace from
:type attribute: str
:returns: tweaked attribute
"""
bits = attribute.split("}")
if len(bits) == 1:
return attribute
elif "/lems/" in bits[0]:
return bits[1]
elif "neuroml2" in bits[0]:
return bits[1]
elif "rdf" in bits[0]:
return "rdf_" + bits[1]
elif "model-qualifiers" in bits[0]:
return "bqmodel_" + bits[1]
elif "biology-qualifiers" in bits[0]:
return "bqbiol_" + bits[1]
else:
return "%s:%s" % (bits[0], bits[1])

def get_nons_tag_from_node(node):
"""Get tags without namespace prefixes.
The parsed tags will include their namespaces in {}:
eg: `<{some_namespace_url}mytag ..>`
Similar to attributes, we replace these with their corresponding LEMS
component types.
:param tag: tag to strip namespace from
:type tag: xml.ElementTree.Element
:returns: tweaked tag
"""
tag = node.tag
bits = tag.split("}")
if len(bits) == 1:
Expand All @@ -37,6 +80,12 @@ def get_nons_tag_from_node(node):


class LEMSXMLNode:
"""LEMS XML Node container class.
This does not include any namespace declarations in tags and their
attributes with their corresponding Component definitions (because LEMS
does not know what XML namespaces are).
"""
def __init__(self, pyxmlnode):
self.tag = get_nons_tag_from_node(pyxmlnode)
self.ltag = self.tag.lower()
Expand All @@ -45,8 +94,9 @@ def __init__(self, pyxmlnode):
self.lattrib = dict()

for k in pyxmlnode.attrib:
self.attrib[k] = pyxmlnode.attrib[k]
self.lattrib[k.lower()] = pyxmlnode.attrib[k]
k_nons = get_nons_attribute(k)
self.attrib[k_nons] = pyxmlnode.attrib[k]
self.lattrib[k_nons.lower()] = pyxmlnode.attrib[k]

self.children = list()
for pyxmlchild in pyxmlnode:
Expand Down

0 comments on commit 0612b1d

Please sign in to comment.