Skip to content

Commit

Permalink
Copies some doctests to the pytest suite
Browse files Browse the repository at this point in the history
  • Loading branch information
funkyfuture committed Nov 11, 2021
1 parent e9aed80 commit 09a3e90
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 60 deletions.
62 changes: 31 additions & 31 deletions _delb/xpath.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,37 @@
BOOLEAN_FUNCTIONS = ("contains", "not", "starts-with")


def _in_parenthesis(expression: str) -> bool:
"""
Determines whether an expression is surrounded by parenthesis in its
entirety.
>>> _in_parenthesis('foo and bar')
False
>>> _in_parenthesis('(foo)(bar)')
False
>>> _in_parenthesis('foo)') == _in_parenthesis('(foo') == False
True
>>> _in_parenthesis('((foo)(bar))')
True
"""
if not expression.startswith("(") or not expression.endswith(")"):
return False
level = 1
for character in expression[1:-1]:
if character == "(":
level += 1
elif character == ")":
level -= 1
if level < 1:
return False
return True


def _partition_terms(expression: str) -> List[str]: # noqa: C901
"""
Split up expression into partitions under consideration of quotes and
Expand Down Expand Up @@ -314,37 +345,6 @@ def __str__(self):
return self.data


def _in_parenthesis(expression: str) -> bool:
"""
Determines whether an expression is surrounded by parenthesis in its
entirety.
>>> _in_parenthesis('foo and bar')
False
>>> _in_parenthesis('(foo)(bar)')
False
>>> _in_parenthesis('foo)') == _in_parenthesis('(foo') == False
True
>>> _in_parenthesis('((foo)(bar))')
True
"""
if not expression.startswith("(") or not expression.endswith(")"):
return False
level = 1
for character in expression[1:-1]:
if character == "(":
level += 1
elif character == ")":
level -= 1
if level < 1:
return False
return True


class PredicateExpression(ABC):
def evaluate(self, node_set: Set["TagNode"]) -> Set["TagNode"]:
# shall not be implemented
Expand Down
111 changes: 82 additions & 29 deletions tests/test_xpath.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
from pkg_resources import get_distribution

from _delb.xpath import (
_in_parenthesis,
_partition_terms,
_reduce_whitespace,
_split,
AttributePredicate,
BooleanPredicate,
FunctionExpression,
Expand All @@ -14,14 +18,45 @@
DELB_VERSION = get_distribution("delb").parsed_version.release


def test_parsing_1():
assert (
str(XPathExpression('.//pb[@n="I"]'))
== 'self::node()/descendant-or-self::node()/child::pb[@n="I"]'
def test_function():
with pytest.raises(AssertionError):
PredicateExpression.parse(")")
predicates = PredicateExpression.parse("starts-with(@foo,'a(b(c)')")

assert isinstance(predicates, FunctionExpression)
assert predicates.name == "starts-with"
assert len(predicates.arguments) == 2

assert isinstance(predicates.arguments[0], AttributePredicate)
assert predicates.arguments[0].name == "foo"

assert isinstance(predicates.arguments[1], Literal)
assert predicates.arguments[1].value == "a(b(c)"


@pytest.mark.xfail(
DELB_VERSION < (0, 4),
reason="A solid XPath parser will be implemented when a supported subset of XPath "
"has been defined.",
)
def test_function_args():
parsed_expression = XPathExpression(
'.//a[@href and not(starts-with(@href, "https://"))]'
)
predicates = parsed_expression.location_paths[0].location_steps[-1].predicates

assert str(predicates) == '(@href and not(starts-with(@href,"https://")))'


def test_parsing_2():
def test_in_parenthesis():
assert not _in_parenthesis("foo and bar")
assert not _in_parenthesis("(foo)(bar)")
assert not _in_parenthesis("foo)")
assert not _in_parenthesis("(foo")
assert _in_parenthesis("((foo)(bar))")


def test_multiple_predicates():
parsed_expression = XPathExpression(
'./entry/sense/cit[@type="translation"][@lang="en"]'
)
Expand All @@ -38,7 +73,32 @@ def test_parsing_2():
)


def test_parsing_3():
def test_partition_terms():
assert _partition_terms('(@foo="1") and (@bar="2")') == [
'@foo="1"',
"and",
'@bar="2"',
]
assert _partition_terms('@foo = "1"') == ["@foo", "=", '"1"']
assert _partition_terms('((@foo="1") or (@bar="2")) and @baz!="3"') == [
'(@foo="1") or (@bar="2")',
"and",
'@baz!="3"',
]
assert _partition_terms('@href and starts-with(@href,"https://")') == [
"@href",
"and",
'starts-with(@href,"https://")',
]
assert _partition_terms('@href and not(starts-with(@href,"https://"))') == [
"@href",
"and",
'not(starts-with(@href,"https://"))',
]
assert _partition_terms("not(@foo) and (@bar)") == ["not(@foo)", "and", "@bar"]


def test_predicate_order():
parsed_expression = XPathExpression("./node[@foo and (@this or @that)]")
predicates = parsed_expression.location_paths[0].location_steps[1].predicates

Expand All @@ -61,31 +121,24 @@ def test_parsing_3():
assert right_operand.name == "that"


@pytest.mark.xfail(
DELB_VERSION < (0, 4),
reason="A solid XPath parser will be implemented when a supported subset of XPath "
"has been defined.",
)
def test_parsing_4():
parsed_expression = XPathExpression(
'.//a[@href and not(starts-with(@href, "https://"))]'
def test_reduce_whitespace():
assert (
_reduce_whitespace('[@a = "1" or @b = "2"][@c = "3"]')
== '[@a="1" or @b="2"][@c="3"]'
)
predicates = parsed_expression.location_paths[0].location_steps[-1].predicates

assert str(predicates) == '(@href and not(starts-with(@href,"https://")))'

assert _reduce_whitespace('[contains(@a, "1")]') == '[contains(@a,"1")]'

def test_parsing_5():
with pytest.raises(AssertionError):
PredicateExpression.parse(")")
predicates = PredicateExpression.parse("starts-with(@foo,'a(b(c)')")

assert isinstance(predicates, FunctionExpression)
assert predicates.name == "starts-with"
assert len(predicates.arguments) == 2
def test_semantic_consistency():
assert (
str(XPathExpression('.//pb[@n="I"]'))
== 'self::node()/descendant-or-self::node()/child::pb[@n="I"]'
)

assert isinstance(predicates.arguments[0], AttributePredicate)
assert predicates.arguments[0].name == "foo"

assert isinstance(predicates.arguments[1], Literal)
assert predicates.arguments[1].value == "a(b(c)"
def test_split():
assert list(_split('./root/path[@a="n/a"]', "/")) == [".", "root", 'path[@a="n/a"]']
assert list(_split('@type="translation" and @xml:lang="en"', " and ")) == [
'@type="translation"',
'@xml:lang="en"',
]

0 comments on commit 09a3e90

Please sign in to comment.