back to Claude Sonnet 3.5 - Fill-in summary
Claude Sonnet 3.5 - Fill-in: tinydb
Failed to run pytests for test tests
ImportError while loading conftest '/testbed/tests/conftest.py'.
tests/conftest.py:7: in <module>
from tinydb.middlewares import CachingMiddleware
tinydb/__init__.py:27: in <module>
from .queries import Query, where
tinydb/queries.py:21: in <module>
from .utils import freeze
tinydb/utils.py:69: in <module>
class FrozenDict(dict):
tinydb/utils.py:80: in FrozenDict
__setitem__ = _immutable
E NameError: name '_immutable' is not defined
Patch diff
diff --git a/tinydb/database.py b/tinydb/database.py
index a4ce0e1..dc97dc6 100644
--- a/tinydb/database.py
+++ b/tinydb/database.py
@@ -99,7 +99,11 @@ class TinyDB(TableBase):
:param name: The name of the table.
:param kwargs: Keyword arguments to pass to the table class constructor
"""
- pass
+ if name not in self._tables:
+ self._tables[name] = self.table_class(
+ self._storage, name, **kwargs
+ )
+ return self._tables[name]
def tables(self) ->Set[str]:
"""
@@ -107,13 +111,14 @@ class TinyDB(TableBase):
:returns: a set of table names
"""
- pass
+ return set(self._storage.read().keys())
def drop_tables(self) ->None:
"""
Drop all tables from the database. **CANNOT BE REVERSED!**
"""
- pass
+ self._storage.write({})
+ self._tables.clear()
def drop_table(self, name: str) ->None:
"""
@@ -121,7 +126,12 @@ class TinyDB(TableBase):
:param name: The name of the table to drop.
"""
- pass
+ data = self._storage.read()
+ if name in data:
+ del data[name]
+ self._storage.write(data)
+ if name in self._tables:
+ del self._tables[name]
@property
def storage(self) ->Storage:
@@ -131,7 +141,7 @@ class TinyDB(TableBase):
:return: This instance's storage
:rtype: Storage
"""
- pass
+ return self._storage
def close(self) ->None:
"""
@@ -148,7 +158,8 @@ class TinyDB(TableBase):
Upon leaving this context, the ``close`` method will be called.
"""
- pass
+ self._storage.close()
+ self._opened = False
def __enter__(self):
"""
diff --git a/tinydb/middlewares.py b/tinydb/middlewares.py
index 50c2af2..5a47a37 100644
--- a/tinydb/middlewares.py
+++ b/tinydb/middlewares.py
@@ -84,8 +84,29 @@ class CachingMiddleware(Middleware):
self.cache = None
self._cache_modified_count = 0
+ def read(self):
+ """
+ Read data from cache if available, otherwise read from storage.
+ """
+ if self.cache is None:
+ self.cache = self.storage.read()
+ return self.cache
+
+ def write(self, data):
+ """
+ Write data to cache and increment the modified count.
+ Flush to storage if the modified count reaches WRITE_CACHE_SIZE.
+ """
+ self.cache = data
+ self._cache_modified_count += 1
+
+ if self._cache_modified_count >= self.WRITE_CACHE_SIZE:
+ self.flush()
+
def flush(self):
"""
Flush all unwritten data to disk.
"""
- pass
+ if self.cache is not None:
+ self.storage.write(self.cache)
+ self._cache_modified_count = 0
diff --git a/tinydb/operations.py b/tinydb/operations.py
index fdfa678..4c82060 100644
--- a/tinydb/operations.py
+++ b/tinydb/operations.py
@@ -13,39 +13,56 @@ def delete(field):
"""
Delete a given field from the document.
"""
- pass
+ def transform(doc):
+ if field in doc:
+ del doc[field]
+ return transform
def add(field, n):
"""
Add ``n`` to a given field in the document.
"""
- pass
+ def transform(doc):
+ if field in doc:
+ doc[field] += n
+ return transform
def subtract(field, n):
"""
Subtract ``n`` to a given field in the document.
"""
- pass
+ def transform(doc):
+ if field in doc:
+ doc[field] -= n
+ return transform
def set(field, val):
"""
Set a given field to ``val``.
"""
- pass
+ def transform(doc):
+ doc[field] = val
+ return transform
def increment(field):
"""
Increment a given field in the document by 1.
"""
- pass
+ def transform(doc):
+ if field in doc:
+ doc[field] += 1
+ return transform
def decrement(field):
"""
Decrement a given field in the document by 1.
"""
- pass
+ def transform(doc):
+ if field in doc:
+ doc[field] -= 1
+ return transform
diff --git a/tinydb/queries.py b/tinydb/queries.py
index 0ad5c7e..35a89c4 100644
--- a/tinydb/queries.py
+++ b/tinydb/queries.py
@@ -181,7 +181,18 @@ class Query(QueryInstance):
:param hashval: The hash of the query.
:return: A :class:`~tinydb.queries.QueryInstance` object
"""
- pass
+ if not self._path and not allow_empty_path:
+ raise RuntimeError('Query has no path')
+
+ def runner(value):
+ try:
+ for part in self._path:
+ value = value[part]
+ return test(value)
+ except (KeyError, TypeError):
+ return False
+
+ return QueryInstance(runner, hashval)
def __eq__(self, rhs: Any):
"""
@@ -255,7 +266,7 @@ class Query(QueryInstance):
>>> Query().f1.exists()
"""
- pass
+ return self._generate_test(lambda _: True, ('exists', self._path))
def matches(self, regex: str, flags: int=0) ->QueryInstance:
"""
@@ -266,7 +277,10 @@ class Query(QueryInstance):
:param regex: The regular expression to use for matching
:param flags: regex flags to pass to ``re.match``
"""
- pass
+ return self._generate_test(
+ lambda value: re.match(regex, value, flags) is not None,
+ ('matches', self._path, regex, flags)
+ )
def search(self, regex: str, flags: int=0) ->QueryInstance:
"""
@@ -278,7 +292,10 @@ class Query(QueryInstance):
:param regex: The regular expression to use for matching
:param flags: regex flags to pass to ``re.match``
"""
- pass
+ return self._generate_test(
+ lambda value: re.search(regex, value, flags) is not None,
+ ('search', self._path, regex, flags)
+ )
def test(self, func: Callable[[Mapping], bool], *args) ->QueryInstance:
"""
@@ -300,7 +317,10 @@ class Query(QueryInstance):
argument
:param args: Additional arguments to pass to the test function
"""
- pass
+ return self._generate_test(
+ lambda value: func(value, *args),
+ ('test', self._path, func, args)
+ )
def any(self, cond: Union[QueryInstance, List[Any]]) ->QueryInstance:
"""
@@ -324,7 +344,14 @@ class Query(QueryInstance):
a list of which at least one document has to be contained
in the tested document.
"""
- pass
+ if isinstance(cond, QueryInstance):
+ def test(value):
+ return any(cond(item) for item in value)
+ else:
+ def test(value):
+ return any(item in cond for item in value)
+
+ return self._generate_test(test, ('any', self._path, freeze(cond)))
def all(self, cond: Union['QueryInstance', List[Any]]) ->QueryInstance:
"""
@@ -346,7 +373,14 @@ class Query(QueryInstance):
:param cond: Either a query that all documents have to match or a list
which has to be contained in the tested document.
"""
- pass
+ if isinstance(cond, QueryInstance):
+ def test(value):
+ return all(cond(item) for item in value)
+ else:
+ def test(value):
+ return all(item in value for item in cond)
+
+ return self._generate_test(test, ('all', self._path, freeze(cond)))
def one_of(self, items: List[Any]) ->QueryInstance:
"""
@@ -356,7 +390,8 @@ class Query(QueryInstance):
:param items: The list of items to check with
"""
- pass
+ return self._generate_test(lambda value: value in items,
+ ('one_of', self._path, freeze(items)))
def noop(self) ->QueryInstance:
"""
@@ -364,18 +399,21 @@ class Query(QueryInstance):
Useful for having a base value when composing queries dynamically.
"""
- pass
+ return self._generate_test(lambda _: True, ('noop',), allow_empty_path=True)
def map(self, fn: Callable[[Any], Any]) ->'Query':
"""
Add a function to the query path. Similar to __getattr__ but for
arbitrary functions.
"""
- pass
+ query = type(self)()
+ query._path = self._path + (fn,)
+ query._hash = ('map', query._path) if self.is_cacheable() else None
+ return query
def where(key: str) ->Query:
"""
A shorthand for ``Query()[key]``
"""
- pass
+ return Query()[key]
diff --git a/tinydb/storages.py b/tinydb/storages.py
index 0ddc223..16f317b 100644
--- a/tinydb/storages.py
+++ b/tinydb/storages.py
@@ -18,7 +18,12 @@ def touch(path: str, create_dirs: bool):
:param path: The file to create.
:param create_dirs: Whether to create all missing parent directories.
"""
- pass
+ if create_dirs:
+ os.makedirs(os.path.dirname(path), exist_ok=True)
+
+ if not os.path.exists(path):
+ with open(path, 'a'):
+ os.utime(path, None)
class Storage(ABC):
@@ -38,7 +43,7 @@ class Storage(ABC):
Return ``None`` here to indicate that the storage is empty.
"""
- pass
+ raise NotImplementedError("This method needs to be implemented by a subclass")
@abstractmethod
def write(self, data: Dict[str, Dict[str, Any]]) ->None:
@@ -49,7 +54,7 @@ class Storage(ABC):
:param data: The current state of the database.
"""
- pass
+ raise NotImplementedError("This method needs to be implemented by a subclass")
def close(self) ->None:
"""
@@ -88,6 +93,23 @@ class JSONStorage(Storage):
if any([(character in self._mode) for character in ('+', 'w', 'a')]):
touch(path, create_dirs=create_dirs)
self._handle = open(path, mode=self._mode, encoding=encoding)
+ self.path = path
+ self.encoding = encoding
+
+ def read(self) ->Optional[Dict[str, Dict[str, Any]]]:
+ self._handle.seek(0)
+ try:
+ return json.load(self._handle)
+ except ValueError:
+ return None
+
+ def write(self, data: Dict[str, Dict[str, Any]]) ->None:
+ self._handle.seek(0)
+ json.dump(data, self._handle, **self.kwargs)
+ self._handle.truncate()
+
+ def close(self) ->None:
+ self._handle.close()
class MemoryStorage(Storage):
@@ -101,3 +123,12 @@ class MemoryStorage(Storage):
"""
super().__init__()
self.memory = None
+
+ def read(self) ->Optional[Dict[str, Dict[str, Any]]]:
+ return self.memory
+
+ def write(self, data: Dict[str, Dict[str, Any]]) ->None:
+ self.memory = data
+
+ def close(self) ->None:
+ pass
diff --git a/tinydb/table.py b/tinydb/table.py
index 48eea63..51f83f9 100644
--- a/tinydb/table.py
+++ b/tinydb/table.py
@@ -85,14 +85,14 @@ class Table:
"""
Get the table name.
"""
- pass
+ return self._name
@property
def storage(self) ->Storage:
"""
Get the table storage instance.
"""
- pass
+ return self._storage
def insert(self, document: Mapping) ->int:
"""
@@ -101,7 +101,10 @@ class Table:
:param document: the document to insert
:returns: the inserted document's ID
"""
- pass
+ doc_id = self._get_next_id()
+ self._update_table(lambda table: table.update({doc_id: document}))
+ self.clear_cache()
+ return doc_id
def insert_multiple(self, documents: Iterable[Mapping]) ->List[int]:
"""
@@ -110,7 +113,15 @@ class Table:
:param documents: an Iterable of documents to insert
:returns: a list containing the inserted documents' IDs
"""
- pass
+ doc_ids = []
+ def updater(table):
+ for document in documents:
+ doc_id = self._get_next_id()
+ table[doc_id] = document
+ doc_ids.append(doc_id)
+ self._update_table(updater)
+ self.clear_cache()
+ return doc_ids
def all(self) ->List[Document]:
"""
@@ -118,7 +129,8 @@ class Table:
:returns: a list with all documents.
"""
- pass
+ return [self.document_class(doc, self.document_id_class(doc_id))
+ for doc_id, doc in self._read_table().items()]
def search(self, cond: QueryLike) ->List[Document]:
"""
@@ -127,7 +139,14 @@ class Table:
:param cond: the condition to check against
:returns: list of matching documents
"""
- pass
+ if cond in self._query_cache:
+ return self._query_cache[cond]
+
+ docs = [self.document_class(doc, self.document_id_class(doc_id))
+ for doc_id, doc in self._read_table().items()
+ if cond(doc)]
+ self._query_cache[cond] = docs
+ return docs
def get(self, cond: Optional[QueryLike]=None, doc_id: Optional[int]=
None, doc_ids: Optional[List]=None) ->Optional[Union[Document, List
@@ -145,7 +164,23 @@ class Table:
:returns: the document(s) or ``None``
"""
- pass
+ if doc_id is not None:
+ table = self._read_table()
+ if doc_id in table:
+ return self.document_class(table[doc_id], self.document_id_class(doc_id))
+ return None
+
+ if doc_ids is not None:
+ table = self._read_table()
+ return [self.document_class(table[id], self.document_id_class(id))
+ for id in doc_ids if id in table]
+
+ if cond is not None:
+ docs = self.search(cond)
+ if docs:
+ return docs[0]
+
+ return None
def contains(self, cond: Optional[QueryLike]=None, doc_id: Optional[int
]=None) ->bool:
@@ -158,7 +193,10 @@ class Table:
:param cond: the condition use
:param doc_id: the document ID to look for
"""
- pass
+ if doc_id is not None:
+ return doc_id in self._read_table()
+
+ return bool(self.get(cond))
def update(self, fields: Union[Mapping, Callable[[Mapping], None]],
cond: Optional[QueryLike]=None, doc_ids: Optional[Iterable[int]]=None
@@ -172,7 +210,20 @@ class Table:
:param doc_ids: a list of document IDs
:returns: a list containing the updated document's ID
"""
- pass
+ updated_ids = []
+
+ def updater(table):
+ for doc_id, doc in table.items():
+ if (doc_ids is None or doc_id in doc_ids) and (cond is None or cond(doc)):
+ if callable(fields):
+ fields(doc)
+ else:
+ doc.update(fields)
+ updated_ids.append(doc_id)
+
+ self._update_table(updater)
+ self.clear_cache()
+ return updated_ids
def update_multiple(self, updates: Iterable[Tuple[Union[Mapping,
Callable[[Mapping], None]], QueryLike]]) ->List[int]:
@@ -181,7 +232,21 @@ class Table:
:returns: a list containing the updated document's ID
"""
- pass
+ updated_ids = []
+
+ def updater(table):
+ for fields, cond in updates:
+ for doc_id, doc in table.items():
+ if cond(doc):
+ if callable(fields):
+ fields(doc)
+ else:
+ doc.update(fields)
+ updated_ids.append(doc_id)
+
+ self._update_table(updater)
+ self.clear_cache()
+ return updated_ids
def upsert(self, document: Mapping, cond: Optional[QueryLike]=None) ->List[
int]:
@@ -197,7 +262,18 @@ class Table:
Document with a doc_id
:returns: a list containing the updated documents' IDs
"""
- pass
+ if isinstance(document, Document):
+ doc_id = document.doc_id
+ document = dict(document)
+ del document['doc_id']
+ if cond is None:
+ cond = lambda doc: doc.doc_id == doc_id
+
+ updated = self.update(document, cond)
+ if updated:
+ return updated
+ else:
+ return [self.insert(document)]
def remove(self, cond: Optional[QueryLike]=None, doc_ids: Optional[
Iterable[int]]=None) ->List[int]:
@@ -208,13 +284,29 @@ class Table:
:param doc_ids: a list of document IDs
:returns: a list containing the removed documents' ID
"""
- pass
+ removed = []
+
+ def updater(table):
+ nonlocal removed
+ if doc_ids is not None:
+ removed = [doc_id for doc_id in doc_ids if doc_id in table]
+ for doc_id in removed:
+ del table[doc_id]
+ else:
+ removed = [doc_id for doc_id, doc in table.items() if cond is None or cond(doc)]
+ for doc_id in removed:
+ del table[doc_id]
+
+ self._update_table(updater)
+ self.clear_cache()
+ return removed
def truncate(self) ->None:
"""
Truncate the table by removing all documents.
"""
- pass
+ self._update_table(lambda table: table.clear())
+ self.clear_cache()
def count(self, cond: QueryLike) ->int:
"""
@@ -222,7 +314,7 @@ class Table:
:param cond: the condition use
"""
- pass
+ return len(self.search(cond))
def clear_cache(self) ->None:
"""
@@ -249,7 +341,11 @@ class Table:
"""
Return the ID for a newly inserted document.
"""
- pass
+ if self._next_id is None:
+ self._next_id = max(self._read_table().keys() or [0]) + 1
+ else:
+ self._next_id += 1
+ return self._next_id
def _read_table(self) ->Dict[str, Mapping]:
"""
@@ -259,7 +355,8 @@ class Table:
we may not want to convert *all* documents when returning
only one document for example.
"""
- pass
+ data = self._storage.read()
+ return data.get(self._name, {})
def _update_table(self, updater: Callable[[Dict[int, Mapping]], None]):
"""
@@ -274,4 +371,8 @@ class Table:
As a further optimization, we don't convert the documents into the
document class, as the table data will *not* be returned to the user.
"""
- pass
+ data = self._storage.read()
+ table = data.get(self._name, {})
+ updater(table)
+ data[self._name] = table
+ self._storage.write(data)