1# Licensed under a 3-clause BSD style license - see LICENSE.rst
2
3"""
4Functions to do XML schema and DTD validation.  At the moment, this
5makes a subprocess call to xmllint.  This could use a Python-based
6library at some point in the future, if something appropriate could be
7found.
8"""
9
10
11import os
12import subprocess
13
14
15def validate_schema(filename, schema_file):
16    """
17    Validates an XML file against a schema or DTD.
18
19    Parameters
20    ----------
21    filename : str
22        The path to the XML file to validate
23
24    schema_file : str
25        The path to the XML schema or DTD
26
27    Returns
28    -------
29    returncode, stdout, stderr : int, str, str
30        Returns the returncode from xmllint and the stdout and stderr
31        as strings
32    """
33
34    base, ext = os.path.splitext(schema_file)
35    if ext == '.xsd':
36        schema_part = '--schema ' + schema_file
37    elif ext == '.dtd':
38        schema_part = '--dtdvalid ' + schema_file
39    else:
40        raise TypeError("schema_file must be a path to an XML Schema or DTD")
41
42    p = subprocess.Popen(
43        f"xmllint --noout --nonet {schema_part} {filename}",
44        shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
45    stdout, stderr = p.communicate()
46
47    if p.returncode == 127:
48        raise OSError(
49            "xmllint not found, so can not validate schema")
50    elif p.returncode < 0:
51        from astropy.utils.misc import signal_number_to_name
52        raise OSError(
53            "xmllint was terminated by signal '{}'".format(
54                signal_number_to_name(-p.returncode)))
55
56    return p.returncode, stdout, stderr
57