Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 49 additions & 5 deletions src/solrq/__init__.py
Original file line number Diff line number Diff line change
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
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment seems duplicated.

``[<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
Original file line number Diff line number Diff line change
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