Skip to content

Commit

Permalink
Range: add boundaries argument to support explicit range boundaries
Browse files Browse the repository at this point in the history
  • Loading branch information
swistakm committed Sep 6, 2016
1 parent c62a755 commit a10d8ff
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 5 deletions.
54 changes: 49 additions & 5 deletions src/solrq/__init__.py
Expand Up @@ -116,14 +116,26 @@ def __repr__(self):
class Range(Value):
"""Wrapper around range values.
Wraps two values with Solr's ``[<from> TO <to>]`` syntax with respect to
restricted character esaping.
Wrapper around range values. Wraps two values with Solr's
``[<from> TO <to>]`` (defaults to inclusive boundaries) syntax with respect
to restricted character escaping.
Wraps two values with Solr's ``[<from> TO <to>]`` (defaults to inclusive
boundaries) syntax with respect to restricted character escaping.
Args:
from_ (object): start of range, same as parameter ``raw`` in
:class:`Value`.
to (object): end of range, same as parameter ``raw`` in :class:`Value`.
boundaries (str): type of boundaries for the range. Defaults to
``'inclusive'``. Allowed values are:
* ``inclusive``, ``ii``, or ``[]``: translates to
``[<from> TO <to>]``
* ``exclusive``, ``ee``, or ``{}``: translates to
``{<from> TO <to>}``
* ``ei``, or ``{]``: translates to ``{<from> TO <to>]``
* ``ie``, or ``[}``: translates to ``[<from> TO <to>}``
Examples:
Expand All @@ -146,15 +158,46 @@ class Range(Value):
>>> Range(timedelta(days=2), timedelta())
<Range: [NOW+2DAYS+0SECONDS+0MILLISECONDS TO NOW]>
To use exclusive or mixed boundaries use ``boundaries`` argument:
>>> Range(0, 20, boundaries='exclusive')
<Range: {0 TO 20}>
>>> Range(0, 20, boundaries='ei')
<Range: {0 TO 20]>
>>> Range(0, 20, boundaries='[}')
<Range: [0 TO 20}>
Note:
We could treat any iterables always as ranges when initializing
:class:`Q` objects but "explicit is better than implicit" and also
this would require to handle value representation there and we don't
want to do that.
"""

def __init__(self, from_, to, safe=None):
"""Initialize range value."""
BOUNDARY_BRACKETS = {
'exclusive': '{}',
'inclusive': '[]',
'ee': '{}',
'ei': '{]',
'ii': '[]',
'ie': '[}'
}
# DRY
BOUNDARY_BRACKETS.update(
# compat: py26 does not support dict comprehensions
dict((value, value) for value in BOUNDARY_BRACKETS.values())
)

def __init__(self, from_, to, safe=None, boundaries='inclusive'):
"""Initialize range value and set boundary brackets."""
try:
brackets = self.BOUNDARY_BRACKETS[boundaries]
except KeyError:
raise ValueError(
"boundaries value must be one of {}"
"".format(self.BOUNDARY_BRACKETS.keys())
)

self.from_ = (
from_ if isinstance(from_, Value) else Value(from_, safe or False)
)
Expand All @@ -168,7 +211,8 @@ def __init__(self, from_, to, safe=None):
self.to.safe = safe

super(Range, self).__init__(
"[{from_} TO {to}]".format(from_=self.from_, to=self.to),
"{brackets[0]}{from_} TO {to}{brackets[1]}"
"".format(from_=self.from_, to=self.to, brackets=brackets),
# Note: parts will be safe'd or not so no need for further escaping
True
)
Expand Down
23 changes: 23 additions & 0 deletions tests/test_squery.py
Expand Up @@ -158,6 +158,29 @@ def test_range():
) == "[{from_} TO {to}]".format(from_=td_from, to=td_to)


def test_range_boundaries_unsupported():
with pytest.raises(ValueError):
Range(1, 2, boundaries='<>')

with pytest.raises(ValueError):
Range(1, 2, boundaries='anything')


def test_range_boundaries():
assert str(Range(0, 1, boundaries='inclusive')) == '[0 TO 1]'
assert str(Range(0, 1, boundaries='exclusive')) == '{0 TO 1}'

assert str(Range(0, 1, boundaries='ee')) == '{0 TO 1}'
assert str(Range(0, 1, boundaries='ii')) == '[0 TO 1]'
assert str(Range(0, 1, boundaries='ei')) == '{0 TO 1]'
assert str(Range(0, 1, boundaries='ie')) == '[0 TO 1}'

assert str(Range(0, 1, boundaries='{}')) == '{0 TO 1}'
assert str(Range(0, 1, boundaries='[]')) == '[0 TO 1]'
assert str(Range(0, 1, boundaries='{]')) == '{0 TO 1]'
assert str(Range(0, 1, boundaries='[}')) == '[0 TO 1}'


def test_special():
assert str(SET) == '[* TO *]'
assert str(ANY) == '*'
Expand Down

0 comments on commit a10d8ff

Please sign in to comment.