mirror of
https://github.com/python/cpython.git
synced 2025-08-04 08:59:19 +00:00
bpo-28238: Implement "{*}tag" and "{ns}*" wildcard tag selection support for ElementPath, and extend the surrounding tests and docs. (GH-12997)
This commit is contained in:
parent
cf48e55f7f
commit
47541689cc
6 changed files with 171 additions and 15 deletions
|
@ -99,13 +99,70 @@ def get_parent_map(context):
|
|||
parent_map[e] = p
|
||||
return parent_map
|
||||
|
||||
|
||||
|
||||
def _is_wildcard_tag(tag):
|
||||
return tag[:3] == '{*}' or tag[-2:] == '}*'
|
||||
|
||||
|
||||
def _prepare_tag(tag):
|
||||
_isinstance, _str = isinstance, str
|
||||
if tag == '{*}*':
|
||||
# Same as '*', but no comments or processing instructions.
|
||||
# It can be a surprise that '*' includes those, but there is no
|
||||
# justification for '{*}*' doing the same.
|
||||
def select(context, result):
|
||||
for elem in result:
|
||||
if _isinstance(elem.tag, _str):
|
||||
yield elem
|
||||
elif tag == '{}*':
|
||||
# Any tag that is not in a namespace.
|
||||
def select(context, result):
|
||||
for elem in result:
|
||||
el_tag = elem.tag
|
||||
if _isinstance(el_tag, _str) and el_tag[0] != '{':
|
||||
yield elem
|
||||
elif tag[:3] == '{*}':
|
||||
# The tag in any (or no) namespace.
|
||||
suffix = tag[2:] # '}name'
|
||||
no_ns = slice(-len(suffix), None)
|
||||
tag = tag[3:]
|
||||
def select(context, result):
|
||||
for elem in result:
|
||||
el_tag = elem.tag
|
||||
if el_tag == tag or _isinstance(el_tag, _str) and el_tag[no_ns] == suffix:
|
||||
yield elem
|
||||
elif tag[-2:] == '}*':
|
||||
# Any tag in the given namespace.
|
||||
ns = tag[:-1]
|
||||
ns_only = slice(None, len(ns))
|
||||
def select(context, result):
|
||||
for elem in result:
|
||||
el_tag = elem.tag
|
||||
if _isinstance(el_tag, _str) and el_tag[ns_only] == ns:
|
||||
yield elem
|
||||
else:
|
||||
raise RuntimeError(f"internal parser error, got {tag}")
|
||||
return select
|
||||
|
||||
|
||||
def prepare_child(next, token):
|
||||
tag = token[1]
|
||||
def select(context, result):
|
||||
for elem in result:
|
||||
for e in elem:
|
||||
if e.tag == tag:
|
||||
yield e
|
||||
if _is_wildcard_tag(tag):
|
||||
select_tag = _prepare_tag(tag)
|
||||
def select(context, result):
|
||||
def select_child(result):
|
||||
for elem in result:
|
||||
yield from elem
|
||||
return select_tag(context, select_child(result))
|
||||
else:
|
||||
if tag[:2] == '{}':
|
||||
tag = tag[2:] # '{}tag' == 'tag'
|
||||
def select(context, result):
|
||||
for elem in result:
|
||||
for e in elem:
|
||||
if e.tag == tag:
|
||||
yield e
|
||||
return select
|
||||
|
||||
def prepare_star(next, token):
|
||||
|
@ -130,11 +187,24 @@ def prepare_descendant(next, token):
|
|||
tag = token[1]
|
||||
else:
|
||||
raise SyntaxError("invalid descendant")
|
||||
def select(context, result):
|
||||
for elem in result:
|
||||
for e in elem.iter(tag):
|
||||
if e is not elem:
|
||||
yield e
|
||||
|
||||
if _is_wildcard_tag(tag):
|
||||
select_tag = _prepare_tag(tag)
|
||||
def select(context, result):
|
||||
def select_child(result):
|
||||
for elem in result:
|
||||
for e in elem.iter():
|
||||
if e is not elem:
|
||||
yield e
|
||||
return select_tag(context, select_child(result))
|
||||
else:
|
||||
if tag[:2] == '{}':
|
||||
tag = tag[2:] # '{}tag' == 'tag'
|
||||
def select(context, result):
|
||||
for elem in result:
|
||||
for e in elem.iter(tag):
|
||||
if e is not elem:
|
||||
yield e
|
||||
return select
|
||||
|
||||
def prepare_parent(next, token):
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue