Skip to content

Commit

Permalink
More XPath 3 functions, plus count() now works on sequences.
Browse files Browse the repository at this point in the history
  • Loading branch information
rbwinslow committed Oct 26, 2016
1 parent 16f8cac commit b7539c9
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 8 deletions.
15 changes: 11 additions & 4 deletions hq/hquery/evaluation_error.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
from hq.hquery.object_type import is_node_set
from hq.hquery.object_type import is_node_set, is_sequence


class HqueryEvaluationError(RuntimeError):

@classmethod
def must_be_node_set(cls, presumed_node_set):
if not is_node_set(presumed_node_set):
raise HqueryEvaluationError('Expected a node set, but found a(n) {0}'.format(presumed_node_set.__class__.__name__))
def must_be_node_set(cls, obj):
if not is_node_set(obj):
raise HqueryEvaluationError('Expected a node set, but found a(n) {0}'.format(obj.__class__.__name__))

@classmethod
def must_be_node_set_or_sequence(cls, obj):
if not (is_node_set(obj) or is_sequence(obj)):
raise HqueryEvaluationError('Expected a node set or sequence, but found a(n) {0}'.format(
obj.__class__.__name__
))
6 changes: 3 additions & 3 deletions hq/hquery/functions/core_node_set.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
exports = ('count', 'id', 'last', 'name', 'position')


def count(nodes):
HqueryEvaluationError.must_be_node_set(nodes)
return number(len(nodes))
def count(sequence):
HqueryEvaluationError.must_be_node_set_or_sequence(sequence)
return number(len(sequence))


def id(ids):
Expand Down
33 changes: 32 additions & 1 deletion hq/hquery/functions/extend_string.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from hq.hquery.functions.core_boolean import boolean
from hq.hquery.object_type import string_value

exports = ('lower_case', 'matches', 'string_join', 'upper_case')
exports = ('lower_case', 'matches', 'replace', 'string_join', 'tokenize', 'upper_case')


def lower_case(value):
Expand All @@ -31,6 +31,22 @@ def matches(*args):
return boolean(re.search(pattern, input, flags))


def replace(*args):
argc = len(args)
if argc < 3 or argc > 4:
raise HqueryEvaluationError('replace() expects 3 or 4 arguments; was passed {0}'.format(argc))

input = string_value(args[0])
pattern = args[1]
replacement = args[2]
if argc == 4:
flags = _xpath_flags_to_re_flags(args[3])
else:
flags = 0

return re.sub(pattern, replacement, input, flags=flags)


def string_join(sequence, *args):
if len(args) > 0:
delimiter = args[0]
Expand All @@ -39,6 +55,21 @@ def string_join(sequence, *args):
return delimiter.join([string_value(x) for x in sequence])


def tokenize(*args):
argc = len(args)
if argc < 2 or argc > 3:
raise HqueryEvaluationError('replace() expects 2 or 3 arguments; was passed {0}'.format(argc))

input = string_value(args[0])
pattern = args[1]
if argc == 3:
flags = _xpath_flags_to_re_flags(args[2])
else:
flags = 0

return re.split(pattern, input, flags=flags)


def upper_case(value):
return string_value(value).upper()

Expand Down
26 changes: 26 additions & 0 deletions test/hquery/test_extended_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,35 @@ def test_matches_function_extends_to_using_context_node_when_passed_no_input_str
assert query_html_doc(html_body, '//p[matches("^f.+")]/text()') == expected_result('foo')


def test_replace_function_performs_regex_replacement_as_per_xpath_30_functions_spec():
assert query_html_doc('', 'replace("dog mattress dog", "^dog", "cat")') == 'cat mattress dog'


def test_replace_function_extends_standard_by_taking_string_value_of_any_type_of_input_object():
assert query_html_doc('<p>hello</p>', 'replace(//p, "h", "j")') == 'jello'


def test_string_join_function_accepts_sequence_as_first_parameter_and_delimiter_as_second():
assert query_html_doc('', 'string-join(1 to 3, ", ")') == '1, 2, 3'


def test_string_join_second_argument_is_optional():
assert query_html_doc('', 'string-join(1 to 2)') == '12'


def test_tokenize_function_breaks_up_strings_as_per_xpath_30_functions_spec():
assert query_html_doc('', 'tokenize("Moe:Larry:..Curly", ":\.*")') == expected_result("""
Moe
Larry
Curly""")
assert query_html_doc('', 'tokenize("HaxtaXpatience", "x", "i")') == expected_result("""
Ha
ta
patience""")
assert query_html_doc('', 'count(tokenize("haxtaxstax", "x"))') == '4'


def test_tokenize_function_extends_standard_by_supporting_any_object_as_input():
assert query_html_doc('<p>foo,bar</p>', 'tokenize(//p, ",")') == expected_result("""
foo
bar""")

0 comments on commit b7539c9

Please sign in to comment.