Project

Profile

Help

Feature #5848

closed

XQuery that doesn't compile sets exception_occurred but in some cases doesn't set the error_message, it is None

Added by Martin Honnen about 1 year ago. Updated 5 months ago.

Status:
Closed
Priority:
Low
Category:
Python API
Start date:
2023-01-23
Due date:
% Done:

100%

Estimated time:
Found in version:
12.0
Fixed in version:
12.1
Platforms:

Description

The following Python code might seem weird as it passes XSLT code to the XPath and XQuery processor but in the end the bug is about the sequence of running the code first through the XPath processor and then through the XQuery processor somehow SaxonC HE 12.0 doesn't manage to set the error_message of the XQuery processor, it is None.

from saxonche import *

def xpath(saxon_proc, xpath_code, input_xml):
    xpath_processor = saxon_proc.new_xpath_processor()

    xdm_input = saxon_proc.parse_xml(xml_text=input_xml)
    xpath_processor.set_context(xdm_item=xdm_input)

    xdm_result = xpath_processor.evaluate(xpath_code)

    if not(xpath_processor.exception_occurred):
        result_items = []
        for xdm_item in xdm_result:
            result_items.append(str(xdm_item))
        result_python_map = { 'results': result_items }
    else:
        result_python_map = { 'messages': xpath_processor.error_message, 'results': None }

    return result_python_map

def xquery(saxon_proc, xquery_code, input_xml):

    xquery_processor = saxon_proc.new_xquery_processor()

    xquery_processor.set_query_content(xquery_code)

    if xquery_processor.exception_occurred:
        return { 'messages' : xquery_processor.error_message }

    xdm_input = saxon_proc.parse_xml(xml_text=input_xml)
    xquery_processor.set_context(xdm_item=xdm_input)

    serialized_result = xquery_processor.run_query_to_string()

    if not(xquery_processor.exception_occurred):
        result_python_map = { 'results': [serialized_result] }
    else:
        result_python_map = { 'messages': xquery_processor.error_message, 'results': None }

    return result_python_map

with PySaxonProcessor(license=True) as proc:
    print(proc.version)

    xml = '''<?xml version="1.0" encoding="UTF-8"?>
    <root>
      <foo>test</foo>
    </root>'''

    xslt = '''<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="3.0"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  exclude-result-prefixes="#all"
  expand-text="yes">

  <xsl:output method="html" indent="yes" html-version="5"/>

  <xsl:mode on-no-match="shallow-copy"/>

  <xsl:template match="/" name="xsl:initial-template">
    <xsl:next-match/>
    <xsl:comment>Run with {system-property('xsl:product-name')} {system-property('xsl:product-version')} at {current-dateTime()}</xsl:comment>
  </xsl:template>

</xsl:stylesheet>'''

    result = xpath(proc, xslt, xml)

    print(result['messages'])

    result = xquery(proc, xslt, xml)

    print(result['messages'])

Output for me is

SaxonC-HE 12.0 from Saxonica
Unexpected token "<" at start of expression
None

so the code doing the XQuery execution notices that the code passed in is not XQuery and gives exception_occurred but somehow doesn't manage to populate the error_message.

This happens with the above sequence, if I comment out or take out the lines doing XPath (e.g.

    #result = xpath(proc, xslt, xml)

    #print(result['messages'])

then I get (from the XQuery evaluation only) the proper error message:

SaxonC-HE 12.0 from Saxonica
A processing instruction must not be named 'xml' in any combination of upper and lower case
Static error near {...rsion="1.0" encoding="utf-8...} 
  XPST0003  A processing instruction must not be named 'xml' in any combination of upper and
  lower case

So something in terms of storing/reporting the error message goes wrong if the XPath evaluation code is run before the XQuery evaluation code.

Again the use of XSLT code as the input to XPath or XQuery is weird but after switching to a "production server" for my Python/Flask based SaxonC powered XML workbench finally freed me from all the threading related crashes I am now having a working app but I am occasionally running into some weirdness with error messages being None, the above is an attempt to isolate that problem.

Actions #1

Updated by O'Neil Delpratt about 1 year ago

Thanks for reporting your issue. This does look like a bug. Exceptions are handled in a central way, so it looks like one of the processors is clearing the exception before the other needs to use the information. I will investigate this further and report back.

Actions #2

Updated by Martin Honnen about 1 year ago

Here is another reduced test case of something going in with exception_occurred/error_message in a sequence of several uses of (this time only) XPath processors from the Python API.

When I first run some none XPath code through the XPath API the error is reported fine but the subsequent new use of XPath (calling fn:transform) (that alone works fine with the used sample data) for some reasons I cannot figure out has exception_occurred as True (with error_message being None) and that way throws my code off as obviously I check for exception occurred and then try to output the error message and don't exepct to have received an XPath evaluation result:

from saxonche import *

def xpath(saxon_proc, xpath_code, input_xml):
    xpath_processor = saxon_proc.new_xpath_processor()

    xdm_input = saxon_proc.parse_xml(xml_text=input_xml)
    xpath_processor.set_context(xdm_item=xdm_input)

    xdm_result = xpath_processor.evaluate(xpath_code)

    if not(xpath_processor.exception_occurred):
        result_items = []
        for xdm_item in xdm_result:
            result_items.append(str(xdm_item))
        result_python_map = { 'results': result_items }
    else:
        result_python_map = { 'messages': xpath_processor.error_message, 'results': None }

    return result_python_map

def transform(saxon_proc, xslt_code, input_code):

    xpath_processor = saxon_proc.new_xpath_processor()
    xpath_processor.set_parameter('stylesheet-text', saxon_proc.make_string_value(xslt_code))
    xpath_processor.set_parameter('source-text', saxon_proc.make_string_value(input_code))
    fn_transform_call = "transform(map{'stylesheet-text':$stylesheet-text,'delivery-format':'serialized','source-node':parse-xml($source-text)})"

    xdm_item = xpath_processor.evaluate_single(fn_transform_call)

    if not(xpath_processor.exception_occurred):
        xdm_map = xdm_item.get_map_value()
        result_docs = []
        for uri in xdm_map.keys():
            result_docs.append({ 'url' : uri.string_value, 'content' : xdm_map.get(uri).head.string_value})
        result_python_map = { 'results': result_docs}
    else:
        result_python_map = { 'messages': xpath_processor.error_message, 'results': None }

    return result_python_map

with PySaxonProcessor(license=True) as proc:
    print(proc.version)

    xml = '''<?xml version="1.0" encoding="utf-8"?>
<html lang="en">
  <head>
    <title>SaxonC XML Workbench: XSLT 3.0, XQuery 3.1, XPath 3.1</title>
  </head>
  <body>
    <section>
      <h1>Release 0.1 alpha of SaxonC XML Workbench</h1>
      <section>
        <h2>Features using SaxonC HE 12.0</h2>
        <ul>
          <li>XSLT 3.0 against XML or JSON input or no input starting with <code>xsl:template name="xsl:initial-template"</code>, all with support for creating and displaying various <code>xsl:result-document</code>s</li>
          <li>XQuery 3.1 against XML or JSON input or no input</li>
          <li>XPath 3.1 against XML or JSON input or no input</li>
        </ul>
      </section>
    </section>
  </body>
</html>'''

    xslt = '''<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="3.0"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  exclude-result-prefixes="#all"
  expand-text="yes">

  <xsl:output method="html" indent="yes" html-version="5"/>

  <xsl:mode on-no-match="shallow-copy"/>

  <xsl:template match="/" name="xsl:initial-template">
    <xsl:next-match/>
    <xsl:comment>Run with {system-property('xsl:product-name')} {system-property('xsl:product-version')} at {current-dateTime()}</xsl:comment>
  </xsl:template>

  <xsl:template match="section">
    <xsl:next-match/>
    <xsl:variable name="section-number" as="xs:string">
      <xsl:number level="any"/>
    </xsl:variable>
    <xsl:result-document href="section-{$section-number}.html">
      <xsl:sequence select="."/>
    </xsl:result-document>
  </xsl:template>

</xsl:stylesheet>'''

    result = xpath(proc, xslt, xml)

    print(result['messages'])

    result = transform(proc, xslt, xml)

    print(result)

This outputs

SaxonC-HE 12.0 from Saxonica
Unexpected token "<" at start of expression
{'messages': None, 'results': None}

consistently for me and if you comment out the first XPath evaluation reporting the error on the first XPath evaluation e.g.

    #result = xpath(proc, xslt, xml)

    #print(result['messages'])

the transform call doesn't run into exception_occurred and returns the result of calling fn:transform just fine.

So this is another case where the use of a chain of evaluations, where the first one causes an error that is correctly supported, but then wrongly messes up the second evaluation where exception_occurred is (still?) True although the evaluation seems to have worked fine and no error_message is reported.

Actions #3

Updated by O'Neil Delpratt about 1 year ago

What works for me is to call saxon_proc.exception_clear() at the start of transform method. Each Processor class has this method available to clear any exception that would have been thrown from a previous run.

Actions #4

Updated by Martin Honnen about 1 year ago

Thanks, O'Neil, that seems to fix most (or perhaps all) of the spurious reported errors I got.

Am I supposed to call that method explicitly for proper API use or is that a workaround you suggest?

Actions #5

Updated by O'Neil Delpratt about 1 year ago

Yes this is recommended. We are still using this callback mechanism from the (Excelsior Jet) JNI use days, but we have started to just throw the exception in some cases in SaxonC 12. This might be the pattern in future releases.

Actions #6

Updated by O'Neil Delpratt about 1 year ago

  • Tracker changed from Bug to Feature
  • Priority changed from Normal to Low

I am actually thinking this is not a bug, therefore I am converting this to a feature request.

For a future release we will look at changing how exceptions are reported.

Actions #7

Updated by Martin Honnen about 1 year ago

Understood. Although I wonder whether a server-side use of single PySaxonProcessor handling multiple requests doing XPath and/or XSLT and/or XQuery at the "same time" will not currently nevertheless at some stage clear the central error_message or exception_occurred raised by some request while another requests would kind of depend on it being present.

Actions #8

Updated by O'Neil Delpratt about 1 year ago

Yes, I am inclined to just throw the exceptions. We will consider this for SaxonC 12.1

BTW, the other Processors have the exception handling methods too: PyXslt30Processor, PyXsltExecutable, PyXPathProcessor and PySchemaValidator.

Actions #9

Updated by O'Neil Delpratt 5 months ago

  • Status changed from New to Closed
  • % Done changed from 0 to 100
  • Fixed in version set to 12.1

As mentioned in comment #8 exceptions are now thrown since SaxonC 12.1. Marking this bug issue as resolved

Please register to edit this issue

Also available in: Atom PDF