Recently, I have been working on automating Jenkin’s config XML files using Python and the lxml package.
I have several articles on using lxml and its handy objectify functionality:
But I’ve never really covered what to do when you need to create or parse an XML tag that contains periods.
For example, Jenkin’s config files are notorious for having tags named like this: <hudson.triggers.TimerTrigger>
The way to get the value of a tag that contains periods is to use lxml’s objectify’s __getattr__() method and pass in the strangely named tag name:
For example:
from lxml import objectify with open("config_no_build_trigger.xml") as xml_file: config = xml_file.read() root = objectify.fromstring(config.encode()) timer_trigger = root.triggers.__getattr__("hudson.triggers.TimerTrigger")
But what do you do if you need to create a tag that contains periods in its name?
I wanted to enable the Build Periodically function in Jenkins programmatically. There are several tags and sub-tags that contain periods that I would need to create if I wanted to turn this feature on in a Jenkins job.
To do it successfully, you need to use __setattr__()
!
Here’s an example:
from lxml import etree, objectify with open("config_no_build_trigger.xml") as xml_file: config = xml_file.read() root = objectify.fromstring(config.encode()) try: _ = root.properties.__getattr__( "org.jenkinsci.plugins.workflow.job.properties.PipelineTriggersJobProperty" ) except AttributeError: # Add org.jenkinsci.plugins.workflow.job.properties.PipelineTriggersJobProperty # XML element root.properties.__setattr__("org.jenkinsci.plugins.workflow.job.properties.PipelineTriggersJobProperty", "") try: trigger_prop = root.properties.__getattr__( "org.jenkinsci.plugins.workflow.job.properties.PipelineTriggersJobProperty" ) _ = trigger_prop.triggers except AttributeError: # Add trigger XML element trigger_prop = root.properties.__getattr__( "org.jenkinsci.plugins.workflow.job.properties.PipelineTriggersJobProperty" ) _ = trigger_prop.triggers = objectify.Element("triggers") try: trigger_prop = root.properties.__getattr__( "org.jenkinsci.plugins.workflow.job.properties.PipelineTriggersJobProperty" ) _ = trigger_prop.triggers.__getattr__("hudson.triggers.TimerTrigger") except AttributeError: # Add hudson.triggers.TimerTrigger XML element trigger_prop = root.properties.__getattr__( "org.jenkinsci.plugins.workflow.job.properties.PipelineTriggersJobProperty" ) _ = trigger_prop.triggers.__setattr__("hudson.triggers.TimerTrigger", "") try: trigger = root.properties.__getattr__( "org.jenkinsci.plugins.workflow.job.properties.PipelineTriggersJobProperty" ) trigger.triggers.__getattr__( "hudson.triggers.TimerTrigger" ).spec except AttributeError: # Add spec XML element trigger = root.properties.__getattr__( "org.jenkinsci.plugins.workflow.job.properties.PipelineTriggersJobProperty" ) trigger.triggers.__getattr__( "hudson.triggers.TimerTrigger" ).spec = objectify.Element("spec") # Modify the schedule try: trigger = root.properties.__getattr__( "org.jenkinsci.plugins.workflow.job.properties.PipelineTriggersJobProperty" ) trigger.triggers.__getattr__( "hudson.triggers.TimerTrigger" ).spec = "H 18 * * *" except AttributeError: print(f"ERROR: Unable to add schedule!") objectify.deannotate(root) etree.cleanup_namespaces(root) config_xml = etree.tostring(root, pretty_print=True, encoding=str) with open("new_config.xml", "w") as xml_file: xml_file.write(config_xml)
This example demonstrates how to use Python exception handling to create all the XML tags and sub-tags effectively and how to set the new tag to a value.
Hopefully, you have found this mini-tutorial helpful and will be able to use it yourself someday!