From 5b6a1b4c9c980f11a228aa09f23b209841f6fec3 Mon Sep 17 00:00:00 2001 From: Ethan Dalool Date: Sat, 9 Oct 2021 12:12:18 -0700 Subject: [PATCH] Use abstract base class to help the subclass know what to do. --- voussoirkit/worms.py | 38 +++++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/voussoirkit/worms.py b/voussoirkit/worms.py index 5f893a6..f7a5491 100644 --- a/voussoirkit/worms.py +++ b/voussoirkit/worms.py @@ -1,6 +1,7 @@ ''' Worms is an SQL ORM with the strength and resilience of the humble earthworm. ''' +import abc import functools import re import typing @@ -57,7 +58,7 @@ def transaction(method): return wrapped_transaction -class Database: +class Database(metaclass=abc.ABCMeta): ''' When your class subclasses this class, you need to ensure the following: - self.COLUMNS is a dictionary of {table: [columns]} like what comes out of @@ -73,6 +74,25 @@ class Database: self.on_rollback_queue = [] self.savepoints = [] + @abc.abstractmethod + def _init_column_index(self): + ''' + Your subclass needs to set self.COLUMNS and self.COLUMN_INDEX, where + COLUMNS is a dictionary of {'table': ['column1', 'column2', ...]} and + COLUMN_INDEX is a dict of {'table': {'column1': 0, 'column2': 1}}. + + These outputs can come from sqlhelpers.extract_table_column_map and + reverse_table_column_map. + ''' + raise NotImplementedError + + @abc.abstractmethod + def _init_sql(self): + ''' + Your subclass needs to set self.sql, which is a database connection. + ''' + raise NotImplementedError + def assert_table_exists(self, table) -> None: if table not in self.get_tables(): raise BadTable(f'Table {table} does not exist.') @@ -334,11 +354,23 @@ class Database: query = f'UPDATE {table} {qmarks}' self.execute(query, bindings) -class DatabaseWithCaching(Database): +class DatabaseWithCaching(Database, metaclass=abc.ABCMeta): def __init__(self): super().__init__() self.caches = {} + def _init_caches(self): + ''' + Your subclass needs to set self.caches, which is a dictionary of + {object: cache} where object is one of your data object types + (use the class itself as the key) and cache is a dictionary or + cacheclass.Cache or anything that supports subscripting. + + If any types are omitted from this dictionary, objects of those + types will not be cached. + ''' + raise NotImplementedError + def clear_all_caches(self) -> None: for cache in self.caches: cache.clear() @@ -483,7 +515,7 @@ class DatabaseWithCaching(Database): for object_row in object_rows: yield self.get_cached_instance(object_class, object_row) -class Object: +class Object(metaclass=abc.ABCMeta): ''' When your objects subclass this class, you need to ensure the following: