Re: [xsl] Running a stylesheet with Python

Subject: Re: [xsl] Running a stylesheet with Python
From: "Michael Kay mike@xxxxxxxxxxxx" <xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx>
Date: Tue, 26 Aug 2025 22:44:52 -0000
The href attribute of xsl:result-document is resolved relative to the "base
output URI" which is where the main output is sent, or would be sent if there
was any main output. So you need to provide a destination for the main output,
even if the stylesheet doesn't produce any.

Michael Kay
Saxonica

> On 26 Aug 2025, at 23:34, dvint@xxxxxxxxx
<xsl-list-service@xxxxxxxxxxxxxxxxxxxxxx> wrote:
>
> I'm trying to run a couple of stylesheets directly from Python. In one case
the stylesheet is meant to send the output to std out and the other has all
the useful output being created/redirected with result-documents. I'm only
interested in those result files and nothing going to std out.
>
> It seems that when called from Python it is detecting there is output and
errors out saying I didn't provide an output_file value. When I provide an
output_file it works and the content is empty.
>
> Is there an easy setting in the stylesheet that I could say - ignore all
output? Seems like it could be logically added to the <xsl:output> statement.
Knowing that doesn't work, I dug into the stylesheet a little more and I'm not
sure where the leak is.
>
> Here is my Python function
>
> from saxonche import *
>
> def run_saxon_py(input_file, xslt_file, output_file=None, TRYREQUEST=None,
DEBUG=None):
>
>    time_to_wait = 10
>    time_counter = 0
>    while not os.path.exists(input_file):
>        time.sleep(2)
>        time_counter += 2
>        print(f"WARNING: Waiting for \"{input_file}\" to exist to apply
\"{xslt_file}\".")
>        if time_counter > time_to_wait:break
>    try:
>        with PySaxonProcessor(license=False) as proc:
>
>            xsltproc = proc.new_xslt30_processor()
>            executable =
xsltproc.compile_stylesheet(stylesheet_file=xslt_file)
>
>
>            if DEBUG == 'yes':
>                executable.set_parameter('DEBUG',
proc.make_string_value('yes'))
>
>            if TRYREQUEST == 'yes':
>                executable.set_parameter('P1_CONTENT',
proc.make_string_value('yes'))
>
>            if output_file == None:
>
>                print(f"NOW")
>                messages =
executable.transform_to_value(source_file=input_file)
>                print(messages)
>            else:
>                # Perform the transformation and write the output directly to
a file
>                print(f"HERE")
>                result_file = xsltproc.transform_to_file(
>                    source_file=input_file,
>                    stylesheet_file=xslt_file,
>                    output_file=output_file
>                )
>                # print(f"Transformation complete. Output saved to:
{result_file}")
>
>    except Exception as e:
>        print(f"ERROR: Unable to apply \"{xslt_file}\" to \"{input_file}\":
\n{e}\n")
>
> It gets called like this for the one that fails
>
>   run_saxon_py(collection_build_xml, os.path.join(SCRIPT_DIR, navadoc_xsl),
TRYREQUEST=TRYREQUEST)
>
> In the navdoc_xsl I start with this:
>
> 	<xsl:template match="/" priority="10">
>
> 		<xsl:variable name="COLLECTION-TITLE"
> 			select="normalize-space(/build-map/item[@type='startpage']/title)"/>
> 		<xsl:variable name="START-PAGE"
>
select="ping:modularize-path(/build-map/item[@type='startpage']/filepath/rela
tive)"/>
>
>
> 		<xsl:call-template name="build-antora-yml">
> 			<xsl:with-param name="COLLECTION-TITLE" select="$COLLECTION-TITLE"/>
> 			<xsl:with-param name="START-PAGE" select="$START-PAGE"/>
> 		</xsl:call-template>
>
> 		<xsl:call-template name="build-playbook-yml">
> 			<xsl:with-param name="START-PAGE" select="$START-PAGE"/>
> 			<xsl:with-param name="COLLECTION-TITLE" select="$COLLECTION-TITLE"/>
> 		</xsl:call-template>
>
> 		<xsl:result-document method="text" href="{concat($srcPath, $MODULES_LOC,
'/ROOT/nav.adoc')}">
> 			<xsl:apply-templates />
> 		</xsl:result-document>
>
> 			<!-- Create the Postman partial files -->
> 		<xsl:for-each select="//item/partial">
>
> 			<xsl:apply-templates  select="." mode="partials">
> 				<xsl:with-param name="topicid" select="parent::item/@id"/>
> 			</xsl:apply-templates>
> 		</xsl:for-each>
>
> 	</xsl:template>
>
> The csll-templates do some processing but each of them writes output to
different result-documents. If I comment out the for-each in this template
there is no error. So looking at this template that is called I have this:
>
> 	<xsl:template match="partial" mode="partials">
> 		<xsl:param name="topicid" />
>
> 		<xsl:result-document href="{concat($PARTIALS, $topicid, '_EP.adoc')}">
> 			<xsl:call-template name="endpoint">
> 			<xsl:with-param name="METHOD"
select="preceding-sibling::request-type"></xsl:with-param>
> 			</xsl:call-template>
> 		</xsl:result-document>
>
> 	</xsl:template>
>
> And the endpoint template
>
> 	<xsl:template name="endpoint">
> 		<xsl:param name="METHOD"/>
>
> 		<xsl:text>[.api_endpoint]</xsl:text><xsl:value-of select="$RETURN"/>
> 		<xsl:text>[source,subs="+attributes,+macros"]</xsl:text><xsl:value-of
select="$RETURN"/>
> 		<xsl:text>--</xsl:text><xsl:value-of select="$RETURN"/>
> 		<xsl:value-of select="$METHOD"/><xsl:text> </xsl:text>
> 		<xsl:value-of select="endpoint"/><xsl:value-of select="$RETURN"/>
> 		<xsl:text>--</xsl:text><xsl:value-of select="$RETURN"/>
> 		<xsl:value-of select="$RETURN"/>
> 	</xsl:template>
>
> I'm not seeing where the output is escaping. So I tried getting rid of the
call template like this:
>
> 		<xsl:result-document href="{concat($PARTIALS, $topicid, '_EP.adoc')}">
> <!--			<xsl:call-template name="endpoint">
> 				<xsl:with-param name="METHOD"
select="preceding-sibling::request-type"></xsl:with-param>
> 			</xsl:call-template>-->
>
> 			<xsl:variable name="METHOD"
> 				select="preceding-sibling::request-type"/>
>
> 			<xsl:text>[.api_endpoint]</xsl:text><xsl:value-of select="$RETURN"/>
> 			<xsl:text>[source,subs="+attributes,+macros"]</xsl:text><xsl:value-of
select="$RETURN"/>
> 			<xsl:text>--</xsl:text><xsl:value-of select="$RETURN"/>
> 			<xsl:value-of select="$METHOD"/><xsl:text> </xsl:text>
> 			<xsl:value-of select="endpoint"/><xsl:value-of select="$RETURN"/>
> 			<xsl:text>--</xsl:text><xsl:value-of select="$RETURN"/>
> 			<xsl:value-of select="$RETURN"/>
>
> 		</xsl:result-document>
>
> I still get this error that points to the start of the result-document
element:
>
> NOW
> Error at xsl:result-document on line 68 column 73 of
build-xmlTOpartials.xsl:
>  SXRD0002  The system identifier of the principal output file is unknown
>  In template rule with match="partial" on line 38 of
build-xmlTOpartials.xsl
>     invoked by xsl:apply-templates at
file:///Users/danvint/pubsrc/_src-data-files/API-markdown-adoc/_build-pipelin
e/_scripts/conversion/build-xmlTOnavadoc.xsl#59
> ERROR: Unable to apply
"/Users/danvint/pubsrc/_src-data-files/API-markdown-adoc/_build-pipeline/_scr
ipts/conversion/build-xmlTOnavadoc.xsl" to
"pingone-for-developers-getting-started-build-map.xml":
> The system identifier of the principal output file is unknown. Line number:
-1
>
> What am I missing?
>
> ..dan

Current Thread