Exemplo n.º 1
0
def test_lru_cache_clear():
    cache = LRUCache(capacity=3)
    cache["a"] = 1
    cache["b"] = 2
    cache.clear()

    assert cache.lru == []
Exemplo n.º 2
0
def test_lru_cache_get():
    cache = LRUCache(capacity=3)
    cache["a"] = 1
    cache["b"] = 1
    cache["c"] = 1
    cache.get("a")
    cache["d"] = 4

    assert cache.lru == ["c", "a", "d"]
Exemplo n.º 3
0
def test_lru_cache_set_multiple():
    cache = LRUCache(capacity=3)
    cache["a"] = 1
    cache["a"] = 2
    cache["a"] = 3
    cache["a"] = 4

    assert cache.lru == ["a"]
Exemplo n.º 4
0
    def __init__(self, storage, cache_size=10):
        """
        Get access to a table.

        :param storage: Access to the storage
        :type storage: StorageProxyus
        :param cache_size: Maximum size of query cache.
        """

        self._storage = storage
        self._query_cache = LRUCache(capacity=cache_size)

        data = self._read()
        if data:
            self._last_id = max(i for i in data)
        else:
            self._last_id = 0
Exemplo n.º 5
0
def test_lru_cache():
    cache = LRUCache(capacity=3)
    cache["a"] = 1
    cache["b"] = 2
    cache["c"] = 3
    _ = cache["a"]  # move to front in lru queue
    cache["d"] = 4  # move oldest item out of lru queue

    assert cache.lru == ["c", "a", "d"]
Exemplo n.º 6
0
class DBTable(Table):
    """
    Represents a single TinyDB Table.
    """
    def __init__(self, storage, cache_size=10):
        """
        Get access to a table.

        :param storage: Access to the storage
        :type storage: StorageProxyus
        :param cache_size: Maximum size of query cache.
        """

        self._storage = storage
        self._query_cache = LRUCache(capacity=cache_size)

        data = self._read()
        if data:
            self._last_id = max(i for i in data)
        else:
            self._last_id = 0

    def _read(self):
        """
        Reading access to the DB.

        :returns: all values
        :rtype: dict
        """

        return self._storage.read()

    def _write(self, values):
        """
        Writing access to the DB.

        :param values: the new values to write
        :type values: dict
        """

        self._query_cache.clear()
        self._storage.write(values)
Exemplo n.º 7
0
    def __init__(self, name, db, cache_size=10):
        """
        Get access to a table.

        :param name: The name of the table.
        :type name: str
        :param db: The parent database.
        :type db: tinydb.database.TinyDB
        :param cache_size: Maximum size of query cache.
        """

        self.name = name
        self._db = db
        self._query_cache = LRUCache(capacity=cache_size)

        old_ids = self._read().keys()
        if old_ids:
            self._last_id = max(i for i in old_ids)
        else:
            self._last_id = 0
Exemplo n.º 8
0
def test_lru_cache_delete():
    cache = LRUCache(capacity=3)
    cache["a"] = 1
    cache["b"] = 2
    del cache["a"]

    try:
        del cache['f']
    except KeyError:
        pass

    assert cache.lru == ["b"]
Exemplo n.º 9
0
def test_lru_cache():
    cache = LRUCache(capacity=3)
    cache["a"] = 1
    cache["b"] = 2
    cache["c"] = 3
    _ = cache["a"]  # move to front in lru queue
    cache["d"] = 4  # move oldest item out of lru queue

    try:
        _ = cache['f']
    except KeyError:
        pass

    assert cache.lru == ["c", "a", "d"]
Exemplo n.º 10
0
    def __init__(self, storage, cache_size=10):
        """
        Get access to a table.

        :param storage: Access to the storage
        :type storage: StorageProxyus
        :param cache_size: Maximum size of query cache.
        """

        self._storage = storage
        self._query_cache = LRUCache(capacity=cache_size)

        data = self._read()
        if data:
            self._last_id = max(i for i in data)
        else:
            self._last_id = 0
Exemplo n.º 11
0
    def __init__(self, name, db, cache_size=10):
        """
        Get access to a table.

        :param name: The name of the table.
        :type name: str
        :param db: The parent database.
        :type db: tinydb.database.TinyDB
        :param cache_size: Maximum size of query cache.
        """

        self.name = name
        self._db = db
        self._query_cache = LRUCache(capacity=cache_size)

        old_ids = self._read().keys()
        if old_ids:
            self._last_id = max(i for i in old_ids)
        else:
            self._last_id = 0
Exemplo n.º 12
0
class Table(object):
    def __init__(self, cache_size=10):
        """
        Implement a single table object.

        """

        self._query_cache = LRUCache(capacity=cache_size)

        self.data = {}
        self.column_type = {}
        self._last_id = 0

    def process_elements(self, func, cond=None, eids=None):
        """
        Helper function for processing all elements specified by condition
        or IDs.

        A repeating pattern in TinyDB is to run some code on all elements
        that match a condition or are specified by their ID. This is
        implemented in this function.
        The function passed as ``func`` has to be a callable. It's first
        argument will be the data currently in the database. It's second
        argument is the element ID of the currently processed element.

        See: :meth:`~.update`, :meth:`.remove`

        :param func: the function to execute on every included element.
                     first argument: all data
                     second argument: the current eid
        :param cond: elements to use, or
        :param eids: elements to use
        :returns: the element IDs that were affected during processed
        """

        data = self._read()

        if eids is not None:
            # Processed element specified by id
            for eid in eids:
                func(data, eid)

        else:
            # Collect affected eids
            eids = []

            # Processed elements specified by condition
            for eid in list(data):
                if cond(data[eid]):
                    func(data, eid)
                    eids.append(eid)

        self._write(data)

        return eids

    def clear_cache(self):
        """
        Clear the query cache.

        A simple helper that clears the internal query cache.
        """
        self._query_cache.clear()

    def _get_next_id(self):
        """
        Increment the ID used the last time and return it
        """

        current_id = self._last_id + 1
        self._last_id = current_id

        return current_id

    def _read(self):
        """
        Reading access to the DB.

        :returns: all values
        :rtype: dict
        """

        return self.data

    def _write(self, values):
        """
        Writing access to the DB.

        :param values: the new values to write
        :type values: dict
        """

        self._query_cache.clear()
        self.data = values

    def __len__(self):
        """
        Get the total number of elements in the table.
        """
        return len(self._read())

    def all(self):
        """
        Get all elements stored in the table.

        :returns: a list with all elements.
        :rtype: list[Element]
        """

        return list(itervalues(self._read()))

    def get_column_type(self, name):
        """
        Get the type of a specified column.
        """
        return self.column_type[name]

    @property
    def column_name(self):
        """
        Get all the column names.
        """
        names = None
        if self.data:
            names = self.data[0].keys()
        return names

    def insert(self, element):
        """
        Insert a new element into the table.

        :param element: the element to insert
        :returns: the inserted element's ID
        """

        eid = self._get_next_id()

        if not isinstance(element, dict):
            raise ValueError('Element is not a dictionary')

        data = self._read()
        data[eid] = element
        self._write(data)

        if not self.column_type:
            for key, val in element.iteritems():
                self.column_type[key] = type(val)

        return eid

    def insert_multiple(self, elements):
        """
        Insert multiple elements into the table.

        :param elements: a list of elements to insert
        :returns: a list containing the inserted elements' IDs
        """

        eids = []
        data = self._read()

        for element in elements:
            eid = self._get_next_id()
            eids.append(eid)

            data[eid] = element

        self._write(data)

        if not self.column_type:
            for key, val in elements[0].iteritems():
                self.column_type[key] = type(val)

        return eids

    def remove(self, cond=None, eids=None):
        """
        Remove all matching elements.

        :param cond: the condition to check against
        :type cond: query
        :param eids: a list of element IDs
        :type eids: list
        :returns: a list containing the removed element's ID
        """

        return self.process_elements(lambda data, eid: data.pop(eid), cond,
                                     eids)

    def update(self, fields, cond=None, eids=None):
        """
        Update all matching elements to have a given set of fields.

        :param fields: the fields that the matching elements will have
                       or a method that will update the elements
        :type fields: dict | dict -> None
        :param cond: which elements to update
        :type cond: query
        :param eids: a list of element IDs
        :type eids: list
        :returns: a list containing the updated element's ID
        """

        if callable(fields):
            return self.process_elements(lambda data, eid: fields(data[eid]),
                                         cond, eids)
        else:
            return self.process_elements(
                lambda data, eid: data[eid].update(fields), cond, eids)

    def purge(self):
        """
        Purge the table by removing all elements.
        """

        self._write({})
        self._last_id = 0

    def search(self, cond):
        """
        Search for all elements matching a 'where' cond.

        :param cond: the condition to check against
        :type cond: Query

        :returns: list of matching elements
        :rtype: list[Element]
        """

        if cond in self._query_cache:
            return self._query_cache[cond][:]

        elements = [element for element in self.all() if cond(element)]
        self._query_cache[cond] = elements

        return elements[:]

    def get(self, cond=None, eid=None):
        """
        Get exactly one element specified by a query or and ID.

        Returns ``None`` if the element doesn't exist

        :param cond: the condition to check against
        :type cond: Query

        :param eid: the element's ID

        :returns: the element or None
        :rtype: Element | None
        """

        # Cannot use process_elements here because we want to return a
        # specific element

        if eid is not None:
            # Element specified by ID
            return self._read().get(eid, None)

        # Element specified by condition
        for element in self.all():
            if cond(element):
                return element

    def denotation(self, name, cond=None):
        """
        Get the denotation of a query.
        
        :param name: the column name
        :type name: string

        :param cond: the condition to check against
        :type cond: Query

        :returns: the denotation
        :rtype: list (not set)
        """
        rows = []
        if cond:
            rows = self.search(cond)
        else:
            rows = self.all()
        entries = [r[name] for r in rows]
        return entries

    def denotation_flatten(self, name, cond=None):
        """
        Get the flattened denotation list of a query.
        """
        return reduce(lambda x, y: x + y, self.denotation(name, cond))

    def denotation_set(self, name, cond=None):
        """
        Get the denotation set of a query.
        """
        return set(self.denotation(name, cond))

    def denotation_flatten_set(self, name, cond=None):
        """
        Get the flattened denotation set of a query.
        """
        return set(self.denotation_flatten(name, cond))

    def count(self, cond=None):
        """
        Count the elements matching a condition.

        :param cond: the condition use
        :type cond: Query
        """
        if cond:
            return len(self.search(cond))
        else:
            return len(self.all())

    def argmax(self, name):
        """
        Get the row where the value in the given column is the maximum
        """
        s = sorted(self.all(), key=lambda k: k[name], reverse=True)
        targmax = [s[0]]
        for i in xrange(1, len(s)):
            if s[i] == s[0]:
                targmax.append(s[i])
            else:
                break
        return targmax

    def argmin(self, name):
        """
        Get the row where the value in the given column is the minimum
        """
        s = sorted(self.all(), key=lambda k: k[name])
        targmin = [s[0]]
        for i in xrange(1, len(s)):
            if s[i] == s[0]:
                targmin.append(s[i])
            else:
                break
        return targmin

    def count_argmax(self, name):
        """
        Get the row where the amount of values in the given column is the maximum
        """
        s = sorted(self.all(), key=lambda k: len(k[name]), reverse=True)
        cargmax = [s[0]]
        for i in xrange(1, len(s)):
            if s[i] == s[0]:
                cargmax.append(s[i])
            else:
                break
        return cargmax

    def count_argmin(self, name):
        """
        Get the row where the amount of values in the given column is the minimum
        """
        s = sorted(self.all(), key=lambda k: len(k[name]))
        cargmin = [s[0]]
        for i in xrange(1, len(s)):
            if s[i] == s[0]:
                cargmin.append(s[i])
            else:
                break
        return cargmin

    def contains(self, cond=None, eids=None):
        """
        Check wether the database contains an element matching a condition or
        an ID.

        If ``eids`` is set, it checks if the db contains an element with one
        of the specified.

        :param cond: the condition use
        :type cond: Query
        :param eids: the element IDs to look for
        """

        if eids is not None:
            # Elements specified by ID
            return any(self.get(eid=eid) for eid in eids)

        # Element specified by condition
        return self.get(cond) is not None
Exemplo n.º 13
0
class Table(object):
    """
    Represents a single TinyDB Table.
    """

    def __init__(self, name, db, cache_size=10):
        """
        Get access to a table.

        :param name: The name of the table.
        :type name: str
        :param db: The parent database.
        :type db: tinydb.database.TinyDB
        :param cache_size: Maximum size of query cache.
        """

        self.name = name
        self._db = db
        self._query_cache = LRUCache(capacity=cache_size)

        old_ids = self._read().keys()
        if old_ids:
            self._last_id = max(i for i in old_ids)
        else:
            self._last_id = 0

    def process_elements(self, func, cond=None, eids=None):
        """
        Helper function for processing all elements specified by condition
        or IDs.

        A repeating pattern in TinyDB is to run some code on all elements
        that match a condition or are specified by their ID. This is
        implemented in this function.
        The function passed as ``func`` has to be a callable. It's first
        argument will be the data currently in the database. It's second
        argument is the element ID of the currently processed element.

        See: :meth:`~.update`, :meth:`.remove`

        :param func: the function to execute on every included element.
                     first argument: all data
                     second argument: the current eid
        :param cond: elements to use
        :param eids: elements to use
        """

        data = self._read()

        if eids is not None:
            # Processed element specified by id
            for eid in eids:
                func(data, eid)

        else:
            # Processed elements specified by condition
            for eid in list(data):
                if cond(data[eid]):
                    func(data, eid)

        self._write(data)

    def _get_next_id(self):
        """
        Increment the ID used the last time and return it
        """

        current_id = self._last_id + 1
        self._last_id = current_id

        return current_id

    def _read(self):
        """
        Reading access to the DB.

        :returns: all values
        :rtype: dict
        """

        raw_data = self._db._read_table(self.name)
        data = {}
        for key in list(raw_data):
            eid = int(key)
            data[eid] = Element(raw_data[key], eid)

        return data

    def _write(self, values):
        """
        Writing access to the DB.

        :param values: the new values to write
        :type values: dict
        """

        self._query_cache.clear()
        self._db._write_table(values, self.name)

    def __len__(self):
        """
        Get the total number of elements in the table.
        """
        return len(self._read())

    def all(self):
        """
        Get all elements stored in the table.

        :returns: a list with all elements.
        :rtype: list[Element]
        """

        return list(self._read().values())

    def insert(self, element):
        """
        Insert a new element into the table.

        :param element: the element to insert
        :returns: the inserted element's ID
        """

        eid = self._get_next_id()

        data = self._read()
        data[eid] = element
        self._write(data)

        return eid

    def insert_multiple(self, elements):
        """
        Insert multiple elements into the table.

        :param elements: a list of elements to insert
        :returns: a list containing the inserted elements' IDs
        """

        eids = []
        data = self._read()

        for element in elements:
            eid = self._get_next_id()
            eids.append(eid)

            data[eid] = element

        self._write(data)

        return eids

    def remove(self, cond=None, eids=None):
        """
        Remove all matching elements.

        :param cond: the condition to check against
        :type cond: query
        :param eids: a list of element IDs
        :type eids: list
        """

        self.process_elements(lambda data, eid: data.pop(eid), cond, eids)

    def update(self, fields, cond=None, eids=None):
        """
        Update all matching elements to have a given set of fields.

        :param fields: the fields that the matching elements will have
                       or a method that will update the elements
        :type fields: dict | (dict, int) -> None
        :param cond: which elements to update
        :type cond: query
        :param eids: a list of element IDs
        :type eids: list
        """

        if callable(fields):
            _update = lambda data, eid: fields(data[eid])
        else:
            _update = lambda data, eid: data[eid].update(fields)

        self.process_elements(_update, cond, eids)

    def purge(self):
        """
        Purge the table by removing all elements.
        """

        self._write({})
        self._last_id = 0

    def search(self, cond):
        """
        Search for all elements matching a 'where' cond.

        :param cond: the condition to check against
        :type cond: Query

        :returns: list of matching elements
        :rtype: list[Element]
        """

        if cond in self._query_cache:
            return self._query_cache[cond]

        elements = [element for element in self.all() if cond(element)]
        self._query_cache[cond] = elements

        return elements

    def get(self, cond=None, eid=None):
        """
        Get exactly one element specified by a query or and ID.

        Returns ``None`` if the element doesn't exist

        :param cond: the condition to check against
        :type cond: Query

        :param eid: the element's ID

        :returns: the element or None
        :rtype: Element | None
        """

        # Cannot use process_elements here because we want to return a
        # specific element

        if eid is not None:
            # Element specified by ID
            return self._read().get(eid, None)

        # Element specified by condition
        for element in self.all():
            if cond(element):
                return element

    def count(self, cond):
        """
        Count the elements matching a condition.

        :param cond: the condition use
        :type cond: Query
        """

        return len(self.search(cond))

    def contains(self, cond=None, eids=None):
        """
        Check wether the database contains an element matching a condition or
        an ID.

        If ``eids`` is set, it checks if the db contains an element with one
        of the specified.

        :param cond: the condition use
        :type cond: Query
        :param eids: the element IDs to look for
        """

        if eids is not None:
            # Elements specified by ID
            return any(self.get(eid=eid) for eid in eids)

        # Element specified by condition
        return self.get(cond) is not None

    def __enter__(self):
        """
        Allow the database to be used as a context manager.

        :return: the table instance
        """

        return self

    def __exit__(self, *args):
        """
        Try to close the storage after being used as a context manager.
        """

        _ = args
        self._db._storage.close()

    close = __exit__
Exemplo n.º 14
0
def test_lru_cache_unlimited_explicit():
    cache = LRUCache(capacity=None)
    for i in range(100):
        cache[i] = i

    assert len(cache.lru) == 100
Exemplo n.º 15
0
def test_lru_cache_unlimited():
    cache = LRUCache()
    for i in range(100):
        cache[i] = i

    assert len(cache.lru) == 100
Exemplo n.º 16
0
class Table(object):
    """
    Represents a single TinyDB Table.
    """
    def __init__(self, storage, cache_size=10):
        """
        Get access to a table.

        :param storage: Access to the storage
        :type storage: StorageProxyus
        :param cache_size: Maximum size of query cache.
        """

        self._storage = storage
        self._query_cache = LRUCache(capacity=cache_size)

        data = self._read()
        if data:
            self._last_id = max(i for i in data)
        else:
            self._last_id = 0

    def process_elements(self, func, cond=None, eids=None):
        """
        Helper function for processing all elements specified by condition
        or IDs.

        A repeating pattern in TinyDB is to run some code on all elements
        that match a condition or are specified by their ID. This is
        implemented in this function.
        The function passed as ``func`` has to be a callable. It's first
        argument will be the data currently in the database. It's second
        argument is the element ID of the currently processed element.

        See: :meth:`~.update`, :meth:`.remove`

        :param func: the function to execute on every included element.
                     first argument: all data
                     second argument: the current eid
        :param cond: elements to use, or
        :param eids: elements to use
        :returns: the element IDs that were affected during processed
        """

        data = self._read()

        if eids is not None:
            # Processed element specified by id
            for eid in eids:
                func(data, eid)

        else:
            # Collect affected eids
            eids = []

            # Processed elements specified by condition
            for eid in list(data):
                if cond(data[eid]):
                    func(data, eid)
                    eids.append(eid)

        self._write(data)

        return eids

    def clear_cache(self):
        """
        Clear the query cache.

        A simple helper that clears the internal query cache.
        """
        self._query_cache.clear()

    def _get_next_id(self):
        """
        Increment the ID used the last time and return it
        """

        current_id = self._last_id + 1
        self._last_id = current_id

        return current_id

    def _read(self):
        """
        Reading access to the DB.

        :returns: all values
        :rtype: dict
        """

        return self._storage.read()

    def _write(self, values):
        """
        Writing access to the DB.

        :param values: the new values to write
        :type values: dict
        """

        self._query_cache.clear()
        self._storage.write(values)

    def __len__(self):
        """
        Get the total number of elements in the table.
        """
        return len(self._read())

    def all(self):
        """
        Get all elements stored in the table.

        :returns: a list with all elements.
        :rtype: list[Element]
        """

        return list(itervalues(self._read()))

    def insert(self, element):
        """
        Insert a new element into the table.

        :param element: the element to insert
        :returns: the inserted element's ID
        """

        eid = self._get_next_id()

        if not isinstance(element, dict):
            raise ValueError('Element is not a dictionary')

        data = self._read()
        data[eid] = element
        self._write(data)

        return eid

    def insert_multiple(self, elements):
        """
        Insert multiple elements into the table.

        :param elements: a list of elements to insert
        :returns: a list containing the inserted elements' IDs
        """

        eids = []
        data = self._read()

        for element in elements:
            eid = self._get_next_id()
            eids.append(eid)

            data[eid] = element

        self._write(data)

        return eids

    def remove(self, cond=None, eids=None):
        """
        Remove all matching elements.

        :param cond: the condition to check against
        :type cond: query
        :param eids: a list of element IDs
        :type eids: list
        :returns: a list containing the removed element's ID
        """

        return self.process_elements(lambda data, eid: data.pop(eid), cond,
                                     eids)

    def update(self, fields, cond=None, eids=None):
        """
        Update all matching elements to have a given set of fields.

        :param fields: the fields that the matching elements will have
                       or a method that will update the elements
        :type fields: dict | dict -> None
        :param cond: which elements to update
        :type cond: query
        :param eids: a list of element IDs
        :type eids: list
        :returns: a list containing the updated element's ID
        """

        if callable(fields):
            return self.process_elements(lambda data, eid: fields(data[eid]),
                                         cond, eids)
        else:
            return self.process_elements(
                lambda data, eid: data[eid].update(fields), cond, eids)

    def purge(self):
        """
        Purge the table by removing all elements.
        """

        self._write({})
        self._last_id = 0

    def search(self, cond):
        """
        Search for all elements matching a 'where' cond.

        :param cond: the condition to check against
        :type cond: Query

        :returns: list of matching elements
        :rtype: list[Element]
        """

        if cond in self._query_cache:
            return self._query_cache[cond]

        elements = [element for element in self.all() if cond(element)]
        self._query_cache[cond] = elements

        return elements

    def get(self, cond=None, eid=None):
        """
        Get exactly one element specified by a query or and ID.

        Returns ``None`` if the element doesn't exist

        :param cond: the condition to check against
        :type cond: Query

        :param eid: the element's ID

        :returns: the element or None
        :rtype: Element | None
        """

        # Cannot use process_elements here because we want to return a
        # specific element

        if eid is not None:
            # Element specified by ID
            return self._read().get(eid, None)

        # Element specified by condition
        for element in self.all():
            if cond(element):
                return element

    def count(self, cond):
        """
        Count the elements matching a condition.

        :param cond: the condition use
        :type cond: Query
        """

        return len(self.search(cond))

    def contains(self, cond=None, eids=None):
        """
        Check wether the database contains an element matching a condition or
        an ID.

        If ``eids`` is set, it checks if the db contains an element with one
        of the specified.

        :param cond: the condition use
        :type cond: Query
        :param eids: the element IDs to look for
        """

        if eids is not None:
            # Elements specified by ID
            return any(self.get(eid=eid) for eid in eids)

        # Element specified by condition
        return self.get(cond) is not None
Exemplo n.º 17
0
class Table(object):
    """
    Represents a single TinyDB Table.
    """

    def __init__(self, name, db, cache_size=10):
        """
        Get access to a table.

        :param name: The name of the table.
        :type name: str
        :param db: The parent database.
        :type db: tinydb.database.TinyDB
        :param cache_size: Maximum size of query cache.
        """

        self.name = name
        self._db = db
        self._query_cache = LRUCache(capacity=cache_size)

        old_ids = self._read().keys()
        if old_ids:
            self._last_id = max(i for i in old_ids)
        else:
            self._last_id = 0

    def process_elements(self, func, cond=None, eids=None):
        """
        Helper function for processing all elements specified by condition
        or IDs.

        A repeating pattern in TinyDB is to run some code on all elements
        that match a condition or are specified by their ID. This is
        implemented in this function.
        The function passed as ``func`` has to be a callable. It's first
        argument will be the data currently in the database. It's second
        argument is the element ID of the currently processed element.

        See: :meth:`~.update`, :meth:`.remove`

        :param func: the function to execute on every included element.
                     first argument: all data
                     second argument: the current eid
        :param cond: elements to use
        :param eids: elements to use
        """

        data = self._read()

        if eids is not None:
            # Processed element specified by id
            for eid in eids:
                func(data, eid)

        else:
            # Processed elements specified by condition
            for eid in list(data):
                if cond(data[eid]):
                    func(data, eid)

        self._write(data)

    def _get_next_id(self):
        """
        Increment the ID used the last time and return it
        """

        current_id = self._last_id + 1
        self._last_id = current_id

        return current_id

    def _read(self):
        """
        Reading access to the DB.

        :returns: all values
        :rtype: dict
        """

        raw_data = self._db._read(self.name)
        data = {}
        for key in list(raw_data):
            eid = int(key)
            data[eid] = Element(raw_data[key], eid)

        return data

    def _write(self, values):
        """
        Writing access to the DB.

        :param values: the new values to write
        :type values: dict
        """

        self._query_cache.clear()
        self._db._write(values, self.name)

    def __len__(self):
        """
        Get the total number of elements in the table.
        """
        return len(self._read())

    def all(self):
        """
        Get all elements stored in the table.

        :returns: a list with all elements.
        :rtype: list[Element]
        """

        return list(self._read().values())

    def insert(self, element):
        """
        Insert a new element into the table.

        :param element: the element to insert
        :returns: the inserted element's ID
        """

        eid = self._get_next_id()

        data = self._read()
        data[eid] = element
        self._write(data)

        return eid

    def insert_multiple(self, elements):
        """
        Insert multiple elements into the table.

        :param elements: a list of elements to insert
        :returns: a list containing the inserted elements' IDs
        """

        return [self.insert(element) for element in elements]

    def remove(self, cond=None, eids=None):
        """
        Remove all matching elements.

        :param cond: the condition to check against
        :type cond: query
        :param eids: a list of element IDs
        :type eids: list
        """

        self.process_elements(lambda data, eid: data.pop(eid), cond, eids)

    def update(self, fields, cond=None, eids=None):
        """
        Update all matching elements to have a given set of fields.

        :param fields: the fields that the matching elements will have
                       or a method that will update the elements
        :type fields: dict | (dict, int) -> None
        :param cond: which elements to update
        :type cond: query
        :param eids: a list of element IDs
        :type eids: list
        """

        if callable(fields):
            _update = lambda data, eid: fields(data[eid])
        else:
            _update = lambda data, eid: data[eid].update(fields)

        self.process_elements(_update, cond, eids)

    def purge(self):
        """
        Purge the table by removing all elements.
        """

        self._write({})
        self._last_id = 0

    def search(self, cond):
        """
        Search for all elements matching a 'where' cond.

        :param cond: the condition to check against
        :type cond: Query

        :returns: list of matching elements
        :rtype: list[Element]
        """

        if cond in self._query_cache:
            return self._query_cache[cond]

        elements = [e for e in self.all() if cond(e)]
        self._query_cache[cond] = elements

        return elements

    def get(self, cond=None, eid=None):
        """
        Get exactly one element specified by a query or and ID.

        Returns ``None`` if the element doesn't exist

        :param cond: the condition to check against
        :type cond: Query

        :param eid: the element's ID

        :returns: the element or None
        :rtype: Element | None
        """

        # Cannot use process_elements here because we want to return a
        # specific element

        if eid is not None:
            # Element specified by ID
            return self._read().get(eid, None)

        # Element specified by condition
        elements = self.search(cond)
        if elements:
            return elements[0]

    def count(self, cond):
        """
        Count the elements matching a condition.

        :param cond: the condition use
        :type cond: Query
        """

        return len(self.search(cond))

    def contains(self, cond=None, eids=None):
        """
        Check wether the database contains an element matching a condition or
        an ID.

        If ``eids`` is set, it checks if the db contains an element with one
        of the specified.

        :param cond: the condition use
        :type cond: Query
        :param eids: the element IDs to look for
        """

        if eids is not None:
            # Elements specified by ID
            return any(self.get(eid=eid) for eid in eids)

        # Element specified by condition
        return self.count(cond) > 0

    def __enter__(self):
        """
        Allow the database to be used as a context manager.

        :return: the table instance
        """

        return self

    def __exit__(self, *args):
        """
        Try to close the storage after being used as a context manager.
        """

        _ = args
        self._db._storage.close()

    close = __exit__
Exemplo n.º 18
0
class Table(object):
    """
    Represents a single TinyDB Table.
    """

    def __init__(self, storage, cache_size=10):
        """
        Get access to a table.

        :param storage: Access to the storage
        :type storage: StorageProxyus
        :param cache_size: Maximum size of query cache.
        """

        self._storage = storage
        self._query_cache = LRUCache(capacity=cache_size)

        data = self._read()
        if data:
            self._last_id = max(i for i in data)
        else:
            self._last_id = 0

    def process_elements(self, func, cond=None, eids=None):
        """
        Helper function for processing all elements specified by condition
        or IDs.

        A repeating pattern in TinyDB is to run some code on all elements
        that match a condition or are specified by their ID. This is
        implemented in this function.
        The function passed as ``func`` has to be a callable. It's first
        argument will be the data currently in the database. It's second
        argument is the element ID of the currently processed element.

        See: :meth:`~.update`, :meth:`.remove`

        :param func: the function to execute on every included element.
                     first argument: all data
                     second argument: the current eid
        :param cond: elements to use, or
        :param eids: elements to use
        :returns: the element IDs that were affected during processed
        """

        data = self._read()

        if eids is not None:
            # Processed element specified by id
            for eid in eids:
                func(data, eid)

        else:
            # Collect affected eids
            eids = []

            # Processed elements specified by condition
            for eid in list(data):
                if cond(data[eid]):
                    func(data, eid)
                    eids.append(eid)

        self._write(data)

        return eids

    def clear_cache(self):
        """
        Clear the query cache.

        A simple helper that clears the internal query cache.
        """
        self._query_cache.clear()

    def _get_next_id(self):
        """
        Increment the ID used the last time and return it
        """

        current_id = self._last_id + 1
        self._last_id = current_id

        return current_id

    def _read(self):
        """
        Reading access to the DB.

        :returns: all values
        :rtype: dict
        """

        return self._storage.read()

    def _write(self, values):
        """
        Writing access to the DB.

        :param values: the new values to write
        :type values: dict
        """

        self._query_cache.clear()
        self._storage.write(values)

    def __len__(self):
        """
        Get the total number of elements in the table.
        """
        return len(self._read())

    def all(self):
        """
        Get all elements stored in the table.

        :returns: a list with all elements.
        :rtype: list[Element]
        """

        return list(itervalues(self._read()))

    def insert(self, element):
        """
        Insert a new element into the table.

        :param element: the element to insert
        :returns: the inserted element's ID
        """

        eid = self._get_next_id()

        if not isinstance(element, dict):
            raise ValueError("Element is not a dictionary")

        data = self._read()
        data[eid] = element
        self._write(data)

        return eid

    def insert_multiple(self, elements):
        """
        Insert multiple elements into the table.

        :param elements: a list of elements to insert
        :returns: a list containing the inserted elements' IDs
        """

        eids = []
        data = self._read()

        for element in elements:
            eid = self._get_next_id()
            eids.append(eid)

            data[eid] = element

        self._write(data)

        return eids

    def remove(self, cond=None, eids=None):
        """
        Remove all matching elements.

        :param cond: the condition to check against
        :type cond: query
        :param eids: a list of element IDs
        :type eids: list
        :returns: a list containing the removed element's ID
        """

        return self.process_elements(lambda data, eid: data.pop(eid), cond, eids)

    def update(self, fields, cond=None, eids=None):
        """
        Update all matching elements to have a given set of fields.

        :param fields: the fields that the matching elements will have
                       or a method that will update the elements
        :type fields: dict | dict -> None
        :param cond: which elements to update
        :type cond: query
        :param eids: a list of element IDs
        :type eids: list
        :returns: a list containing the updated element's ID
        """

        if callable(fields):
            return self.process_elements(lambda data, eid: fields(data[eid]), cond, eids)
        else:
            return self.process_elements(lambda data, eid: data[eid].update(fields), cond, eids)

    def purge(self):
        """
        Purge the table by removing all elements.
        """

        self._write({})
        self._last_id = 0

    def search(self, cond):
        """
        Search for all elements matching a 'where' cond.

        :param cond: the condition to check against
        :type cond: Query

        :returns: list of matching elements
        :rtype: list[Element]
        """

        if cond in self._query_cache:
            return self._query_cache[cond]

        elements = [element for element in self.all() if cond(element)]
        self._query_cache[cond] = elements

        return elements

    def get(self, cond=None, eid=None):
        """
        Get exactly one element specified by a query or and ID.

        Returns ``None`` if the element doesn't exist

        :param cond: the condition to check against
        :type cond: Query

        :param eid: the element's ID

        :returns: the element or None
        :rtype: Element | None
        """

        # Cannot use process_elements here because we want to return a
        # specific element

        if eid is not None:
            # Element specified by ID
            return self._read().get(eid, None)

        # Element specified by condition
        for element in self.all():
            if cond(element):
                return element

    def count(self, cond):
        """
        Count the elements matching a condition.

        :param cond: the condition use
        :type cond: Query
        """

        return len(self.search(cond))

    def contains(self, cond=None, eids=None):
        """
        Check wether the database contains an element matching a condition or
        an ID.

        If ``eids`` is set, it checks if the db contains an element with one
        of the specified.

        :param cond: the condition use
        :type cond: Query
        :param eids: the element IDs to look for
        """

        if eids is not None:
            # Elements specified by ID
            return any(self.get(eid=eid) for eid in eids)

        # Element specified by condition
        return self.get(cond) is not None