Skip to content

Commit 18a6350

Browse files
authored
Merge pull request #402 from sartography/bugfix/clean-up-parser-namespaces
ensure parser knows about all namepsaces
2 parents c1150c6 + 2b54e70 commit 18a6350

File tree

5 files changed

+29
-39
lines changed

5 files changed

+29
-39
lines changed

SpiffWorkflow/bpmn/parser/node_parser.py

+6-11
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,12 @@ def bpmn_attributes(self):
5656
def get_description(self):
5757
return self.process_parser.parser.spec_descriptions.get(self.node.tag)
5858

59-
def xpath(self, xpath, extra_ns=None):
60-
return self._xpath(self.node, xpath, extra_ns)
59+
def xpath(self, xpath):
60+
return self._xpath(self.node, xpath)
6161

62-
def doc_xpath(self, xpath, extra_ns=None):
62+
def doc_xpath(self, xpath):
6363
root = self.node.getroottree().getroot()
64-
return self._xpath(root, xpath, extra_ns)
64+
return self._xpath(root, xpath)
6565

6666
def attribute(self, attribute, namespace=None, node=None):
6767
if node is None:
@@ -156,13 +156,8 @@ def _get_lane(self):
156156
if noderef is not None:
157157
return noderef.getparent().get('name')
158158

159-
def _xpath(self, node, xpath, extra_ns=None):
160-
if extra_ns is not None:
161-
nsmap = self.nsmap.copy()
162-
nsmap.update(extra_ns)
163-
else:
164-
nsmap = self.nsmap
165-
return node.xpath(xpath, namespaces=nsmap)
159+
def _xpath(self, node, xpath):
160+
return node.xpath(xpath, namespaces=self.nsmap)
166161

167162
def raise_validation_exception(self, message):
168163
raise ValidationException(message, self.node, self.filename)

SpiffWorkflow/dmn/parser/BpmnDmnParser.py

+7-4
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
from ...bpmn.parser.ValidationException import ValidationException
2727

2828
from ...bpmn.parser.BpmnParser import BpmnParser, BpmnValidator
29-
from ...dmn.parser.DMNParser import DMNParser, get_dmn_ns
29+
from ...dmn.parser.DMNParser import DMNParser
3030
from ..engine.DMNEngine import DMNEngine
3131

3232
XSD_DIR = os.path.join(os.path.dirname(__file__), 'schema')
@@ -62,16 +62,19 @@ def add_dmn_xml(self, node, filename=None):
6262
"""
6363
Add the given lxml representation of the DMN file to the parser's set.
6464
"""
65-
nsmap = get_dmn_ns(node)
65+
namespaces = self.namespaces.copy()
66+
namespaces.update(node.nsmap)
67+
if None in namespaces:
68+
namespaces['dmn'] = namespaces.pop(None)
6669
# We have to create a dmn validator on the fly, because we support multiple versions
6770
# If we have a bpmn validator, assume DMN validation should be done as well.
6871
# I don't like this, but I don't see a better solution.
69-
schema = self.dmn_schemas.get(nsmap.get('dmn'))
72+
schema = self.dmn_schemas.get(namespaces.get('dmn'))
7073
if self.validator and schema is not None:
7174
validator = BpmnValidator(schema)
7275
validator.validate(node, filename)
7376

74-
dmn_parser = DMNParser(self, node, nsmap, filename=filename)
77+
dmn_parser = DMNParser(self, node, namespaces, filename=filename)
7578
self.dmn_parsers[dmn_parser.bpmn_id] = dmn_parser
7679
self.dmn_parsers_by_name[dmn_parser.get_name()] = dmn_parser
7780

SpiffWorkflow/dmn/parser/DMNParser.py

+7-22
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
import ast
2121

22-
from SpiffWorkflow.bpmn.parser.node_parser import NodeParser, DEFAULT_NSMAP
22+
from SpiffWorkflow.bpmn.parser.node_parser import NodeParser
2323
from SpiffWorkflow.bpmn.parser.ValidationException import ValidationException
2424

2525
from SpiffWorkflow.bpmn.parser.util import xpath_eval
@@ -34,20 +34,6 @@
3434
Rule,
3535
)
3636

37-
def get_dmn_ns(node):
38-
"""
39-
Returns the namespace definition for the current DMN
40-
41-
:param node: the XML node for the DMN document
42-
"""
43-
nsmap = DEFAULT_NSMAP.copy()
44-
if 'http://www.omg.org/spec/DMN/20151101/dmn.xsd' in node.nsmap.values():
45-
nsmap['dmn'] = 'http://www.omg.org/spec/DMN/20151101/dmn.xsd'
46-
elif 'http://www.omg.org/spec/DMN/20180521/DI/' in node.nsmap.values():
47-
nsmap['dmn'] = 'http://www.omg.org/spec/DMN/20180521/DI/'
48-
elif 'https://www.omg.org/spec/DMN/20191111/MODEL/' in node.nsmap.values():
49-
nsmap['dmn'] = 'https://www.omg.org/spec/DMN/20191111/MODEL/'
50-
return nsmap
5137

5238
class DMNParser(NodeParser):
5339
"""
@@ -79,20 +65,20 @@ def __init__(self, p, node, nsmap, svg=None, filename=None):
7965
self.filename = filename
8066

8167
def parse(self):
82-
self.decision = self._parse_decision(self.node.findall('{*}decision'))
68+
self.decision = self._parse_decision(self.xpath('.//dmn:decision'))
8369

8470
@property
8571
def bpmn_id(self):
8672
"""
8773
Returns the process ID
8874
"""
89-
return self.node.findall('{*}decision[1]')[0].get('id')
75+
return self.xpath('dmn:decision[1]')[0].get('id')
9076

9177
def get_name(self):
9278
"""
9379
Returns the process name (or ID, if no name is included in the file)
9480
"""
95-
return self.node.findall('{*}decision[1]')[0].get('name')
81+
return self.xpath('dmn:decision[1]')[0].get('name')
9682

9783
def _parse_decision(self, root):
9884
decision_elements = list(root)
@@ -115,7 +101,7 @@ def _parse_decision(self, root):
115101
return decision
116102

117103
def _parse_decision_tables(self, decision, decisionElement):
118-
for decision_table_element in decisionElement.findall('{*}decisionTable'):
104+
for decision_table_element in decisionElement.findall('dmn:decisionTable', namespaces=self.nsmap):
119105
name = decision_table_element.attrib.get('name', '')
120106
hitPolicy = decision_table_element.attrib.get('hitPolicy', 'UNIQUE').upper()
121107
decision_table = DecisionTable(decision_table_element.attrib['id'],
@@ -146,12 +132,11 @@ def _parse_inputs_outputs(self, decisionTable,
146132

147133
def _parse_input(self, input_element):
148134
type_ref = None
149-
prefix = self.nsmap['dmn']
150-
xpath = xpath_eval(input_element, {'dmn': prefix})
135+
xpath = xpath_eval(input_element, self.nsmap)
151136
expression = None
152137
for input_expression in xpath('dmn:inputExpression'):
153138
type_ref = input_expression.attrib.get('typeRef', '')
154-
expression_node = input_expression.find('{' + prefix + '}text')
139+
expression_node = input_expression.find('dmn:text', namespaces=self.nsmap)
155140
if expression_node is not None:
156141
expression = expression_node.text
157142

tests/SpiffWorkflow/dmn/DecisionRunner.py

+8-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
from lxml import etree
44

55
from SpiffWorkflow.dmn.engine.DMNEngine import DMNEngine
6-
from SpiffWorkflow.dmn.parser.DMNParser import DMNParser, get_dmn_ns
6+
from SpiffWorkflow.dmn.parser.DMNParser import DMNParser
7+
from SpiffWorkflow.bpmn.parser.node_parser import DEFAULT_NSMAP
78

89
class WorkflowSpec:
910
def __init__(self):
@@ -38,7 +39,12 @@ def __init__(self, script_engine, filename, path=''):
3839
with open(fn) as fh:
3940
node = etree.parse(fh)
4041

41-
self.dmnParser = DMNParser(None, node.getroot(), get_dmn_ns(node.getroot()))
42+
nsmap = DEFAULT_NSMAP.copy()
43+
nsmap.update(node.getroot().nsmap)
44+
if None in nsmap:
45+
nsmap['dmn'] = nsmap.pop(None)
46+
47+
self.dmnParser = DMNParser(None, node.getroot(), nsmap)
4248
self.dmnParser.parse()
4349

4450
decision = self.dmnParser.decision

tests/SpiffWorkflow/dmn/VersionTest.py

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ class DmnVersionTest(unittest.TestCase):
99

1010
def setUp(self):
1111
self.parser = BpmnDmnParser()
12+
self.parser.namespaces.update({'dmn': 'https://www.omg.org/spec/DMN/20191111/MODEL/'})
1213

1314
def test_load_v1_0(self):
1415
filename = os.path.join(data_dir, 'dmn_version_20151101_test.dmn')

0 commit comments

Comments
 (0)