Skip to content

Commit 1c9f686

Browse files
committed
b132637: Fix positional xpath predicates when default namespace provided
1 parent 11284d0 commit 1c9f686

File tree

1 file changed

+18
-3
lines changed

1 file changed

+18
-3
lines changed

Lib/xml/etree/ElementPath.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
xpath_tokenizer_re = re.compile(
6262
r"("
6363
r"'[^']*'|\"[^\"]*\"|"
64+
r"last\(\)|" # Pick out the only xpath function currently supported
6465
r"::|"
6566
r"//?|"
6667
r"\.\.|"
@@ -71,6 +72,9 @@
7172
r"\s+"
7273
)
7374

75+
# Find integers, possibly preceded by - or +
76+
int_re = re.compile(r"[\+\-]?\d+$")
77+
7478
def xpath_tokenizer(pattern, namespaces=None):
7579
default_namespace = namespaces.get('') if namespaces else None
7680
parsing_attribute = False
@@ -85,11 +89,22 @@ def xpath_tokenizer(pattern, namespaces=None):
8589
yield ttype, "{%s}%s" % (namespaces[prefix], uri)
8690
except KeyError:
8791
raise SyntaxError("prefix %r not found in prefix map" % prefix) from None
88-
elif default_namespace and not parsing_attribute:
92+
# We don't preprend the default_namespace when:
93+
# - the tag is an attribute as the xml spec says default namespaces
94+
# don't apply to attributes
95+
# - when the tag is a number, possibly preceded by - or +, as these
96+
# are not valid characters to start a tag with and are probably
97+
# used as positional predicates.
98+
elif default_namespace and not (parsing_attribute or int_re.match(tag)):
8999
yield ttype, "{%s}%s" % (default_namespace, tag)
90100
else:
91101
yield token
92102
parsing_attribute = False
103+
elif ttype == 'last()':
104+
# Break the found 'last()' part into the separate 'tag' and 'ttype'
105+
# separate returned values expected from this generator
106+
yield ('', 'last')
107+
yield ('()', '')
93108
else:
94109
yield token
95110
parsing_attribute = ttype == '@'
@@ -266,7 +281,7 @@ def select_negated(context, result):
266281
if (attr_value := elem.get(key)) is not None and attr_value != value:
267282
yield elem
268283
return select_negated if '!=' in signature else select
269-
if signature == "-" and not re.match(r"\-?\d+$", predicate[0]):
284+
if signature == "-" and not int_re.match(predicate[0]):
270285
# [tag]
271286
tag = predicate[0]
272287
def select(context, result):
@@ -276,7 +291,7 @@ def select(context, result):
276291
return select
277292
if signature == ".='" or signature == ".!='" or (
278293
(signature == "-='" or signature == "-!='")
279-
and not re.match(r"\-?\d+$", predicate[0])):
294+
and not int_re.match(predicate[0])):
280295
# [.='value'] or [tag='value'] or [.!='value'] or [tag!='value']
281296
tag = predicate[0]
282297
value = predicate[-1]

0 commit comments

Comments
 (0)