Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 33 additions & 10 deletions dev_tools/docs/xsd_units.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,19 @@ def generate_xsd_units_doc(
if node_name is None:
continue
if "nxdl:" + node_name in members:
words = node.xpath("xs:annotation/xs:documentation", namespaces=ns)[0]
examples = []
for example in words.iterchildren():
nm = example.attrib.get("name")
if nm is not None and nm == "example":
examples.append("``" + example.text + "``")
a = words.text
if len(examples) > 0:
a = " ".join(a.split()) + ",\n\texample(s): " + " | ".join(examples)
db[node_name] = a
doc = _extract_xsd_doc(node, ns)
examples = _extract_xsd_examples(node, ns)

# Replacing other whitespace characters to remove Sphinx warnings.
content = " ".join(doc.split())

if len(examples) == 1:
content += f",\n\texample: {examples[0]}"
elif examples:
examples_str = " | ".join(examples)
content += f",\n\texamples: {examples_str}"

db[node_name] = content

# for item in node.xpath("xs:restriction//xs:enumeration", namespaces=ns):
# key = "%s" % item.get("value")
Expand All @@ -69,3 +72,23 @@ def generate_xsd_units_doc(
rst_lines.append(f" {line}\n")
rst_lines.append("\n")
return rst_lines


def _extract_xsd_doc(node, ns):
"""
Extracts documentation text from an XSD annotation.
"""
doc_nodes = node.xpath("xs:annotation/xs:documentation", namespaces=ns)
if not doc_nodes:
return ""

return " ".join(doc_nodes[0].itertext()).strip()


def _extract_xsd_examples(node, ns):
"""
Extracts examples from an XSD annotation.
"""
examples = node.xpath("xs:annotation/xs:appinfo/example/text()", namespaces=ns)

return [f"``{ex}``" for ex in examples if ex]
1 change: 1 addition & 0 deletions dev_tools/nxdl/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@
from .discover import iter_definitions # noqa F401
from .syntax import nxdl_schema # noqa F401
from .syntax import validate_definition # noqa F401
from .syntax import validate_nxdl # noqa F401
25 changes: 18 additions & 7 deletions dev_tools/nxdl/syntax.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from typing import Optional

import lxml.etree
import xmlschema

from ..globals import errors
from ..globals.directories import get_xsd_file
Expand All @@ -12,24 +13,34 @@ def nxdl_schema() -> lxml.etree.XMLSchema:
return lxml.etree.XMLSchema(lxml.etree.parse(get_xsd_file()))


def validate_nxdl():
"""Validate the NXDL schema itself.

:raises XMLSchemaParseError:
"""
xsd_path = get_xsd_file()
_ = xmlschema.XMLSchema(xsd_path)


def validate_definition(
xml_file_name: PathLike,
xml_path: PathLike,
xml_schema: Optional[lxml.etree.XMLSchema] = None,
):
xml_file_name = str(xml_file_name)
with _handle_xml_error(xml_file_name, lxml.etree.XMLSyntaxError):
xml_tree = lxml.etree.parse(xml_file_name)
"""Validate an NXDL instance (NeXus definition)."""
xml_path = str(xml_path)
with _handle_xml_error(xml_path, lxml.etree.XMLSyntaxError):
xml_tree = lxml.etree.parse(xml_path)
if xml_schema is None:
xml_schema = nxdl_schema()
with _handle_xml_error(xml_file_name, lxml.etree.DocumentInvalid):
with _handle_xml_error(xml_path, lxml.etree.DocumentInvalid):
xml_schema.assertValid(xml_tree)


@contextmanager
def _handle_xml_error(xml_file_name: str, *exception_types):
def _handle_xml_error(xml_path: str, *exception_types):
try:
yield
except exception_types as e:
raise errors.XMLSyntaxError(
"\n " + "\n ".join([xml_file_name] + str(e).rsplit(":", 1))
"\n " + "\n ".join([xml_path] + str(e).rsplit(":", 1))
) from e
7 changes: 6 additions & 1 deletion dev_tools/tests/test_nxdl.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from ..nxdl import iter_definitions
from ..nxdl import nxdl_schema
from ..nxdl import validate_definition
from ..nxdl import validate_nxdl
from .utils import pytest_path_param_id


Expand All @@ -26,8 +27,12 @@ def xml_schema():
return nxdl_schema()


def test_nxdl_syntax():
validate_nxdl()


@pytest.mark.parametrize(
"nxdl_file", list(iter_definitions()), ids=pytest_path_param_id
)
def test_nxdl_syntax(nxdl_file, xml_schema):
def test_nexus_syntax(nxdl_file, xml_schema):
validate_definition(nxdl_file, xml_schema)
2 changes: 1 addition & 1 deletion manual/source/nxdl-types.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ with any of these data types. The default type ``NX_CHAR`` is applied in cases
where a field or attribute is defined in an NXDL specification without explicit assignment of a ``type``.

.. Generated from ../nxdlTypes.xsd via a custom Python tool
../../utils/types2rst.py ../../nxdlTypes.xsd > types.table
dev_tools.docs.xsd_units.generate_xsd_units_doc

.. index::
seealso: binary data; NX_BINARY
Expand Down
Loading
Loading