Skip to content

Commit b871dba

Browse files
authored
Merge pull request #24 from openSUSE/23-add-doctype.xpath
Fix #23: Add `.xpath` method to Doctype
2 parents 8275d18 + 92acd19 commit b871dba

3 files changed

Lines changed: 105 additions & 4 deletions

File tree

changelog.d/23.feature.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Support ``.xpath`` method in :class:`~docbuild.model.doctype.Doctype`

src/docbuild/models/doctype.py

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,15 @@
1515
class Doctype(BaseModel):
1616
"""A "doctype" that comprises of a product, docset, lifecycle, and language.
1717
18-
>>> Doctype.from_str("sles/15-SP6@supported/en-us,de-de")
19-
Doctype(product=<Product.SLES: 'sles'>, docset=['15-SP6'], \
20-
lifecycle=<LifecycleFlag.SUPPORTED: 'supported'>, \
21-
langs=[LanguageCode(language='en-us'), LanguageCode(language='de-de')])
18+
>>> doctype = Doctype.from_str("sles/15-SP6@supported/en-us,de-de")
19+
>>> doctype.product
20+
<Product.sles: 'sles'>
21+
>>> doctype.docset
22+
['15-SP6']
23+
>>> doctype.lifecycle.name
24+
'supported'
25+
>>> doctype.langs
26+
[LanguageCode(language='de-de'), LanguageCode(language='en-us')]
2227
"""
2328

2429
# __init__.__doc__ = """Create a new Doctype instance.
@@ -222,3 +227,46 @@ def from_str(cls, doctype_str: str) -> Self:
222227
lifecycle=lifecycle,
223228
langs=langs,
224229
)
230+
231+
def xpath(self) -> str:
232+
"""Return an XPath expression for this Doctype to find all deliverables.
233+
234+
>>> result = Doctype.from_str("sles/15-SP6@supported/en-us,de-de").xpath()
235+
>>> expected = (
236+
... "product[@productid='sles']/docset[@setid='15-SP6']"
237+
... "[@lifecycle='supported']"
238+
... "/builddocs/language[@lang='de-de' or @lang='en-us']"
239+
... )
240+
>>> result == expected
241+
True
242+
243+
:return: A relative XPath expression that can be used to find all
244+
deliverables that match this Doctype.
245+
"""
246+
# Example: /sles/15-SP6@supported/en-us,de-de
247+
product = f"product[@productid={self.product.value!r}]"
248+
setids = [f'@setid={d!r}' for d in self.docset if d != '*']
249+
250+
setids_str = ' or '.join(setids)
251+
if setids_str:
252+
docset = f"docset[{setids_str}]"
253+
else:
254+
docset = 'docset'
255+
256+
lifecycle = ' or '.join(
257+
[f'@lifecycle={lc.name!r}'
258+
for lc in self.lifecycle
259+
if lc != LifecycleFlag.UNKNOWN ]
260+
)
261+
if lifecycle:
262+
docset += f"[{lifecycle}]"
263+
264+
if '*' in self.langs:
265+
language = 'language'
266+
else:
267+
language = ' or '.join(
268+
[f"@lang={lang.language!r}" for lang in self.langs]
269+
)
270+
language = f"language[{language}]"
271+
272+
return f'{product}/{docset}/builddocs/{language}'

tests/models/test_doctype.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,3 +209,55 @@ def test_sorted_langs_in_doctype_instantiation():
209209
langs=langs,
210210
)
211211
assert dt1.langs == sorted([LanguageCode(lang) for lang in langs])
212+
213+
214+
@pytest.mark.parametrize(
215+
'string,xpath',
216+
[
217+
# 1: product + one docset + a single language
218+
(
219+
'sles/15-SP6/en-us',
220+
(
221+
"product[@productid='sles']"
222+
"/docset[@setid='15-SP6']"
223+
"/builddocs/language[@lang='en-us']"
224+
),
225+
),
226+
# 2: product + all docsets + a single language
227+
(
228+
'sles//en-us',
229+
("product[@productid='sles']/docset/builddocs/language[@lang='en-us']"),
230+
),
231+
# 3: product + one docset + one lifecycle + multiple languages
232+
(
233+
'sles/15-SP6@supported/en-us,de-de',
234+
(
235+
"product[@productid='sles']"
236+
"/docset[@setid='15-SP6'][@lifecycle='supported']"
237+
"/builddocs/language[@lang='de-de' or @lang='en-us']"
238+
),
239+
),
240+
# 4: product + one docset + multiple lifecycles + one language
241+
(
242+
'sles/15-SP7@supported,beta/de-de',
243+
(
244+
"product[@productid='sles']"
245+
"/docset[@setid='15-SP7'][@lifecycle='supported' or @lifecycle='beta']"
246+
"/builddocs/language[@lang='de-de']"
247+
),
248+
),
249+
# 5: product + one docset + multiple lifecycles + all languages
250+
(
251+
'sles/15-SP6@supported/*',
252+
(
253+
"product[@productid='sles']"
254+
"/docset[@setid='15-SP6'][@lifecycle='supported']"
255+
'/builddocs/language'
256+
),
257+
),
258+
],
259+
)
260+
def test_xpath_in_doctype(string, xpath):
261+
"""Test the XPath extraction from a Doctype."""
262+
doctype = Doctype.from_str(string)
263+
assert xpath == doctype.xpath()

0 commit comments

Comments
 (0)