Source code for groupy.object.listers
"""
.. module:: listers
:platform: Unix, Windows
:synopsis: A module containing classes used for lists of objects
.. moduleauthor:: Robert Grant <rhgrant10@gmail.com>
This module contains classes that provide filterable lists and message pagers.
"""
import operator
from ..api import errors
[docs]class FilterList(list):
"""A filterable list.
Acts just like a regular :class:`list`, except it can be filtered using a
special keyword syntax. Also, the first and last items are special
properties.
"""
[docs] def filter(self, **kwargs):
"""Filter the list and return a new instance.
Arguments are keyword arguments only, and can be appended with
operator method names to indicate relationships other than equals.
For example, to filter the list down to only items whose ``name``
property contains "ie":
.. code-block:: python
new_list = filter_list.filter(name__contains='ie')
As another example, this filters the list down to only those
with a ``created`` property that is less than 1234567890:
.. code-block:: python
new_list = filter_list.filter(created__lt=1234567890)
Acceptable operators are:
- ``__lt``: less than
- ``__gt``: greater than
- ``__contains``: contains
- ``__eq``: equal to
- ``__ne``: not equal to
- ``__le``: less than or equal to
- ``__ge``: greater than or equal to
Use of any operator listed here results in a
:class:`~groupy.api.errors.InvalidOperatorError`.
:return: a new list with potentially less items than the original
:rtype: :class:`~groupy.object.listers.FilterList`
"""
kvops = []
for k, v in kwargs.items():
if '__' in k[1:-1]: # don't use it if at the start or end of k
k, o = k.rsplit('__', 1)
try:
op = getattr(operator, o)
except AttributeError:
raise errors.InvalidOperatorError("__{}".format(o))
else:
op = operator.eq
kvops.append((k, v, op))
test = lambda i, k, v, op: hasattr(i, k) and op(getattr(i, k), v)
criteria = lambda i: all(test(i, k, v, op) for k, v, op in kvops)
return FilterList(filter(criteria, self))
@property
def first(self):
"""The first element in the list.
"""
try:
return self[0]
except IndexError:
return None
@property
def last(self):
"""The last element in the list.
"""
try:
return self[-1]
except IndexError:
return None
[docs]class MessagePager(FilterList):
"""A filterable, extendable page of messages.
:param group: the group from which to page through messages
:type group: :class:`~groupy.object.responses.Group`
:param list messages: the initial page of messages
:param bool backward: whether the oldest message is at index 0
"""
def __init__(self, group, messages, backward=False):
super().__init__(messages)
self.backward = backward
self.group = group
@property
def oldest(self):
"""Return the oldest message in the list.
:returns: the oldest message in the list
:rtype: :class:`~groupy.object.responses.Message`
"""
return self.first if self.backward else self.last
@property
def newest(self):
"""Return the newest message in the list.
:returns: the newest message in the list
:rtype: :class:`~groupy.object.responses.Message`
"""
return self.last if self.backward else self.first
[docs] def prepend(self, messages):
"""Prepend a list of messages to the list.
:param list messages: the messages to prepend
"""
for each in messages:
self.insert(0, each)
[docs] def newer(self):
"""Return the next (newer) page of messages.
:returns: a newer page of messages
:rtype: :class:`~groupy.object.listers.MessagePager`
"""
return self.group.messages(after=self.newest.id)
[docs] def older(self):
"""Return the previous (older) page of messages.
:returns: an older page of messages
:rtype: :class:`~groupy.object.listers.MessagePager`
"""
return self.group.messages(before=self.oldest.id)
[docs] def inewer(self):
"""Add in-place the next (newer) page of messages.
:returns: ``True`` if successful, ``False`` otherwise
:rtype: :obj:`bool`
"""
new = self.newer()
if not new:
return False
if self.backward:
self.extend(self.newer())
else:
self.prepend(self.newer())
return True
[docs] def iolder(self):
"""Add in-place the previous (older) page of messages.
:returns: ``True`` if successful, ``False`` otherwise
:rtype: :obj:`bool`
"""
old = self.older()
if not old:
return False
if self.backward:
self.prepend(self.older())
else:
self.extend(self.older())
return True