2# +==== BEGIN CatFeeder =================+
5# ...............)..(.')
7# ...............\(__)|
8# Inspired by Joan Stark
9# source https://www.asciiart.eu/
13# FILE: sql_redis_cache_rebinds.py
14# CREATION DATE: 18-11-2025
15# LAST Modified: 14:52:36 19-12-2025
17# This is the backend server in charge of making the actual website work.
19# COPYRIGHT: (c) Cat Feeder
20# PURPOSE: This is the redis cache handler for the sql library using the underlying redis wrapper.
22# +==== END CatFeeder =================+
24from typing
import Any, Callable, Dict, List, Optional, Tuple, Union, Literal, overload
26from display_tty
import Disp, initialise_logger
28from redis
import Redis
30from ..redis
import RedisCaching, RedisArgs
34 """Redis cache rebind layer for the SQL library.
36 Thin subclass of ``RedisCaching`` that adds structured debug logging and a
37 convenience initializer for binding Redis caching to SQL fetcher/writer
38 callables. It does not alter caching semantics; all logic for key
39 construction, serialization, TTL handling, and invalidation lives in the
40 base class. This class simply centralizes instrumentation (``log_debug``)
41 and provides a clearer integration point for the SQL manager without
42 changing its source. No artificial line wrapping is used; paragraphs are
43 kept intact for readability.
46 disp_inner: Disp = initialise_logger(__qualname__,
False)
48 def __new__(cls, *args, existing_instance: Optional[
"RedisCaching"] =
None, **kwargs):
52 if isinstance(existing_instance, RedisCaching)
and not isinstance(existing_instance, SQLRedisCacheRebinds):
56 obj.__dict__ = existing_instance.__dict__.copy()
58 obj._adopted_from_base =
True
62 def __init__(self, client: Optional[Union[Redis, RedisArgs]] =
None, debug: bool =
False, *, namespace: str =
"sql", db_label: Optional[str] =
None, default_ttls: Optional[Dict[str, int]] =
None, existing_instance: Optional[
"RedisCaching"] =
None,) ->
None:
64 self.
disp.update_disp_debug(debug)
65 self.
disp.log_debug(
"Initialising...")
67 if getattr(self,
"_adopted_from_base",
False):
70 if hasattr(self,
"_adopted_from_base"):
71 delattr(self,
"_adopted_from_base")
78 default_ttls=default_ttls,
79 existing_instance=existing_instance
if isinstance(
80 existing_instance, SQLRedisCacheRebinds)
else None,
83 f
"Initialized SQLRedisCacheRebinds namespace='{self.namespace}', db_label='{self.db_label}'"
85 self.
disp.log_debug(
"Initialised")
94 fetcher: Callable[[], Optional[Tuple[int, int, int]]],
95 ttl_seconds: Optional[int] =
None,
96 ) -> Optional[Tuple[int, int, int]]:
97 """Get and cache the database semantic version.
100 fetcher (Callable[[], Optional[Tuple[int,int,int]]]): Function that returns the database version as a 3-tuple or ``None``.
101 ttl_seconds (int | None): Optional TTL override in seconds.
104 tuple[int,int,int] | None: Version tuple or ``None``.
106 key = self.
_key(
"meta",
"db_version")
107 self.
disp_inner.log_debug(f
"Get database version using key='{key}'")
109 if cached
is not None:
110 self.
disp_inner.log_debug(
"Cache hit for database version")
111 if isinstance(cached, (list, tuple))
and len(cached) == 3:
113 major, minor, patch = (
114 int(cached[0]), int(cached[1]), int(cached[2]))
115 return (major, minor, patch)
116 except (ValueError, TypeError):
120 "Cache miss for database version; invoking fetcher")
122 if ttl_seconds
is None:
125 ttl_to_use = int(ttl_seconds)
127 f
"Caching database version with ttl={ttl_to_use}s")
134 fetcher: Callable[[], Union[int, List[str]]],
135 ttl_seconds: Optional[int] =
None,
136 error_token: Optional[int] =
None,
137 ) -> Union[int, List[str]]:
138 """Get and cache the list of non-internal table names.
141 fetcher (Callable[[], int | list[str]]): Function fetching table names or an error token.
142 ttl_seconds (int | None): Optional TTL override.
143 error_token (int | None): Error sentinel; result equal to this is not cached.
146 int | list[str]: Error token or list of table names.
148 key = self.
_key(
"schema",
"table_names")
149 self.
disp_inner.log_debug(f
"Get table names using key='{key}'")
151 if cached
is not None:
152 self.
disp_inner.log_debug(
"Cache hit for table names")
155 "Cache miss for table names; invoking fetcher")
158 if ttl_seconds
is None:
161 ttl_to_use = int(ttl_seconds)
163 f
"Caching table names with ttl={ttl_to_use}s")
171 fetcher: Callable[[str], Union[List[str], int]],
172 ttl_seconds: Optional[int] =
None,
173 error_token: Optional[int] =
None,
174 ) -> Union[List[str], int]:
175 """Get and cache the list of column names for a table.
178 table_name (str): Target table name.
179 fetcher (Callable[[str], list[str] | int]): Function returning columns or error token.
180 ttl_seconds (int | None): Optional TTL override.
181 error_token (int | None): Error sentinel; result equal to this is not cached.
184 list[str] | int: Column names or error token.
186 key = self.
_key(
"schema", f
"columns:{table_name}")
188 f
"Get column names for table='{table_name}' using key='{key}'")
190 if cached
is not None:
191 self.
disp_inner.log_debug(
"Cache hit for table columns")
194 "Cache miss for table columns; invoking fetcher")
195 value = fetcher(table_name)
197 if ttl_seconds
is None:
200 ttl_to_use = int(ttl_seconds)
202 f
"Caching columns for '{table_name}' with ttl={ttl_to_use}s")
210 fetcher: Callable[[str], Union[int, List[Any]]],
211 ttl_seconds: Optional[int] =
None,
212 error_token: Optional[int] =
None,
213 ) -> Union[int, List[Any]]:
214 """Get and cache a table schema description.
217 table (str): Table name.
218 fetcher (Callable[[str], int | list[Any]]): Function returning description rows or error token.
219 ttl_seconds (int | None): Optional TTL override.
220 error_token (int | None): Error sentinel; result equal to this is not cached.
223 int | list[Any]: Error token or description rows.
225 key = self.
_key(
"schema", f
"describe:{table}")
227 f
"Describe table='{table}' using key='{key}'")
229 if cached
is not None:
230 self.
disp_inner.log_debug(
"Cache hit for table description")
233 "Cache miss for table description; invoking fetcher")
234 value = fetcher(table)
236 if ttl_seconds
is None:
239 ttl_to_use = int(ttl_seconds)
241 f
"Caching description for '{table}' with ttl={ttl_to_use}s")
248 fetcher: Callable[[], Union[int, Dict[str, str]]],
249 ttl_seconds: Optional[int] =
None,
250 error_token: Optional[int] =
None,
251 ) -> Union[int, Dict[str, str]]:
252 """Get and cache all triggers with their SQL definitions.
255 fetcher (Callable[[], int | dict[str,str]]): Function returning mapping or error token.
256 ttl_seconds (int | None): Optional TTL override.
257 error_token (int | None): Error sentinel; result equal to this is not cached.
260 int | dict[str, str]: Error token or triggers mapping.
262 key = self.
_key(
"schema",
"triggers")
263 self.
disp_inner.log_debug(f
"Get triggers using key='{key}'")
265 if cached
is not None:
266 self.
disp_inner.log_debug(
"Cache hit for triggers")
268 self.
disp_inner.log_debug(
"Cache miss for triggers; invoking fetcher")
271 if ttl_seconds
is None:
274 ttl_to_use = int(ttl_seconds)
276 f
"Caching triggers with ttl={ttl_to_use}s")
284 fetcher: Callable[[str], Union[int, str]],
285 ttl_seconds: Optional[int] =
None,
286 error_token: Optional[int] =
None,
287 ) -> Union[int, str]:
288 """Get and cache a single trigger definition.
291 trigger_name (str): Trigger identifier.
292 fetcher (Callable[[str], int | str]): Function returning SQL text or error token.
293 ttl_seconds (int | None): Optional TTL override.
294 error_token (int | None): Error sentinel; result equal to this is not cached.
297 int | str: Error token or trigger SQL.
299 key = self.
_key(
"schema", f
"trigger:{trigger_name}")
301 f
"Get trigger='{trigger_name}' using key='{key}'")
303 if cached
is not None:
304 self.
disp_inner.log_debug(
"Cache hit for trigger definition")
307 "Cache miss for trigger definition; invoking fetcher")
308 value = fetcher(trigger_name)
310 ttl_to_use = self.
default_ttls[
"schema"]
if ttl_seconds
is None else int(
313 f
"Caching trigger '{trigger_name}' with ttl={ttl_to_use}s")
320 fetcher: Callable[[], Union[int, List[str]]],
321 ttl_seconds: Optional[int] =
None,
322 error_token: Optional[int] =
None,
323 ) -> Union[int, List[str]]:
324 """Get and cache the list of trigger names.
327 fetcher (Callable[[], int | list[str]]): Function returning trigger names or error token.
328 ttl_seconds (int | None): Optional TTL override.
329 error_token (int | None): Error sentinel; result equal to this is not cached.
332 int | list[str]: Error token or trigger names.
334 key = self.
_key(
"schema",
"trigger_names")
335 self.
disp_inner.log_debug(f
"Get trigger names using key='{key}'")
337 if cached
is not None:
338 self.
disp_inner.log_debug(
"Cache hit for trigger names")
341 "Cache miss for trigger names; invoking fetcher")
344 if ttl_seconds
is None:
347 ttl_to_use = int(ttl_seconds)
349 f
"Caching trigger names with ttl={ttl_to_use}s")
356 column: Union[str, List[str]],
357 where: Union[str, List[str]] =
"",
359 fetcher: Callable[[str, Union[str, List[str]], Union[str, List[str]]], int],
360 ttl_seconds: Optional[int] =
None,
362 """Get and cache the number of rows matching an optional filter.
365 table (str): Table name.
366 column (str | list[str]): Column to count over (usually ``"*"``).
367 where (str | list[str]): Optional WHERE clause or conditions.
368 fetcher (Callable[[str, str | list[str], str | list[str]], int]): Function returning the count.
369 ttl_seconds (int | None): Optional TTL override.
379 key = self.
_key(
"count", table, params)
381 f
"Get table size for table='{table}' using key='{key}'"
384 if cached
is not None:
386 self.
disp_inner.log_debug(
"Cache hit for table size")
389 "Cache miss for table size; invoking fetcher")
390 value = int(fetcher(table, column, where))
391 if ttl_seconds
is None:
394 ttl_to_use = int(ttl_seconds)
395 self.
disp_inner.log_debug(f
"Caching table size with ttl={ttl_to_use}s")
403 column: Union[str, List[str]],
404 where: Union[str, List[str]] =
"",
406 beautify: Literal[
True] =
True,
407 fetcher: Callable[[], Union[int, List[Dict[str, Any]]]],
408 ttl_seconds: Optional[int] =
None,
409 error_token: Optional[int] =
None,
410 ) -> Union[int, List[Dict[str, Any]]]:
417 column: Union[str, List[str]],
418 where: Union[str, List[str]] =
"",
420 beautify: Literal[
False],
421 fetcher: Callable[[], Union[int, List[Tuple[Any, ...]]]],
422 ttl_seconds: Optional[int] =
None,
423 error_token: Optional[int] =
None,
424 ) -> Union[int, List[Tuple[Any, ...]]]:
430 column: Union[str, List[str]],
431 where: Union[str, List[str]] =
"",
433 beautify: bool =
True,
434 fetcher: Callable[[], Union[int, List[Dict[str, Any]], List[Tuple[Any, ...]]]],
435 ttl_seconds: Optional[int] =
None,
436 error_token: Optional[int] =
None,
437 ) -> Union[int, Union[List[Dict[str, Any]], List[Tuple[Any, ...]]]]:
438 """Get and cache rows from a table.
440 When ``beautify`` is True the fetcher must return a list of dictionaries. When False it must return a list of tuples (or a list of lists convertible to tuples). On error the fetcher may return an integer error token.
443 table (str): Table name.
444 column (str | list[str]): Column name(s) or ``"*"``.
445 where (str | list[str]): Optional WHERE clause or conditions.
446 beautify (bool): Whether rows are dictionary-shaped.
447 fetcher (Callable[[], int | list[dict[str, Any]] | list[tuple[Any,...]]]): Function that executes the actual SQL call.
448 ttl_seconds (int | None): Optional TTL override.
449 error_token (int | None): Error sentinel; result equal to this is not cached.
452 int | list[dict[str, Any]] | list[tuple[Any, ...]]: Error token or rows.
458 "beautify": bool(beautify),
460 key = self.
_key(
"data", table, params)
462 f
"Get data from table='{table}', beautify={bool(beautify)} using key='{key}'"
465 if cached
is not None:
466 self.
disp_inner.log_debug(
"Cache hit for table data")
470 if isinstance(cached, list)
and (
not cached
or isinstance(cached[0], list)):
471 rows: List[Tuple[Any, ...]] = []
473 rows.append(tuple(row))
475 "Converted cached rows to tuples for non-beautified result")
479 "Cache miss for table data; invoking fetcher")
482 if ttl_seconds
is None:
485 ttl_to_use = int(ttl_seconds)
487 f
"Caching table data with ttl={ttl_to_use}s")
497 columns: List[Tuple[str, str]],
499 writer: Callable[[str, List[Tuple[str, str]]], int],
501 """Create a table and invalidate related cache entries on success.
504 table (str): New table name.
505 columns (list[tuple[str,str]]): Column definitions.
506 writer (Callable[[str, list[tuple[str,str]]], int]): Function performing the SQL execution.
509 int: Writer return code.
511 result = writer(table, columns)
513 f
"Writer executed for create_table, result={result}")
514 if isinstance(result, int)
and result == 0:
516 "Invalidating caches after create_table success")
519 f
"{self.namespace}:{self.db_label}:schema:table_names",
520 f
"{self.namespace}:{self.db_label}:schema:columns:{table}:*",
521 f
"{self.namespace}:{self.db_label}:schema:describe:{table}:*",
532 writer: Callable[[str, str, str, str], int],
534 """Insert a trigger and invalidate trigger caches on success.
537 trigger_name (str): Trigger name.
538 table_name (str): Related table name.
539 timing_event (str): Trigger timing/event (e.g., ``BEFORE INSERT``).
540 body (str): SQL body.
541 writer (Callable[[str,str,str,str], int]): Function performing the SQL execution.
544 int: Writer return code.
546 result = writer(trigger_name, table_name, timing_event, body)
548 f
"Writer executed for insert_trigger '{trigger_name}', result={result}")
549 if isinstance(result, int)
and result == 0:
551 "Invalidating caches after insert_trigger success")
554 f
"{self.namespace}:{self.db_label}:schema:triggers",
555 f
"{self.namespace}:{self.db_label}:schema:trigger_names",
567 writer: Callable[[str, str, str, str], int],
569 """Alias to :py:meth:`insert_trigger`.
572 trigger_name (str): Trigger name.
573 table_name (str): Related table name.
574 timing_event (str): Trigger timing/event.
575 body (str): SQL body.
576 writer (Callable[[str,str,str,str], int]): SQL executor.
579 int: Writer return code.
581 return self.
insert_trigger(trigger_name, table_name, timing_event, body, writer=writer)
586 data: Union[List[List[Union[str,
None, int, float]]], List[Union[str,
None, int, float]]],
587 column: Optional[List[str]] =
None,
589 writer: Callable[[str, Union[List[List[Union[str,
None, int, float]]], List[Union[str,
None, int, float]]], Optional[List[str]]], int],
591 """Insert row(s) into a table and invalidate related caches.
594 table (str): Target table.
595 data (list[list[str|None|int|float]] | list[str|None|int|float]): Row or rows.
596 column (list[str] | None): Optional column list.
597 writer (Callable[..., int]): Function performing the SQL execution.
600 int: Writer return code.
602 result = writer(table, data, column)
604 f
"Writer executed for insert_data_into_table on '{table}', result={result}")
605 if isinstance(result, int)
and result == 0:
607 "Invalidating table caches after insert_data_into_table success")
614 data: Union[List[List[Union[str,
None, int, float]]], List[Union[str,
None, int, float]]],
616 where: Union[str, List[str]] =
"",
618 writer: Callable[[str, Union[List[List[Union[str,
None, int, float]]], List[Union[str,
None, int, float]]], List[str], Union[str, List[str]]], int],
620 """Update row(s) in a table and invalidate related caches.
623 table (str): Target table.
624 data (list[list[str|None|int|float]] | list[str|None|int|float]): New values.
625 column (list[str]): Column names corresponding to ``data``.
626 where (str | list[str]): WHERE clause/conditions.
627 writer (Callable[..., int]): Function performing the SQL execution.
630 int: Writer return code.
633 "Update cacher, going to run the writer function."
635 result = writer(table, data, column, where)
637 f
"Writer executed for update_data_in_table on '{table}', result={result}")
638 if isinstance(result, int)
and result == 0:
640 "Invalidating table caches after update_data_in_table success"
648 data: Union[List[List[Union[str,
None, int, float]]], List[Union[str,
None, int, float]]],
649 columns: Optional[List[str]] =
None,
651 writer: Callable[[str, Union[List[List[Union[str,
None, int, float]]], List[Union[str,
None, int, float]]], Optional[List[str]]], int],
653 """Insert or update row(s) and invalidate related caches.
656 table (str): Target table.
657 data (list[list[str|None|int|float]] | list[str|None|int|float]): Rows.
658 columns (list[str] | None): Optional columns.
659 writer (Callable[..., int]): Function performing the SQL execution.
662 int: Writer return code.
664 result = writer(table, data, columns)
666 f
"Writer executed for insert_or_update_data_into_table on '{table}', result={result}")
667 if isinstance(result, int)
and result == 0:
669 "Invalidating table caches after insert_or_update_data_into_table success")
680 writer: Callable[[str, str, str, str], int],
682 """Insert or update a trigger and invalidate related caches.
685 trigger_name (str): Trigger name.
686 table_name (str): Related table name.
687 timing_event (str): Trigger timing/event.
688 body (str): SQL body.
689 writer (Callable[[str,str,str,str], int]): SQL executor.
692 int: Writer return code.
694 result = writer(trigger_name, table_name, timing_event, body)
696 f
"Writer executed for insert_or_update_trigger '{trigger_name}', result={result}")
697 if isinstance(result, int)
and result == 0:
699 "Invalidating caches after insert_or_update_trigger success")
702 f
"{self.namespace}:{self.db_label}:schema:triggers",
703 f
"{self.namespace}:{self.db_label}:schema:trigger_names",
710 where: Union[str, List[str]] =
"",
712 writer: Callable[[str, Union[str, List[str]]], int],
714 """Delete row(s) from a table and invalidate related caches.
717 table (str): Target table.
718 where (str | list[str]): WHERE clause/conditions.
719 writer (Callable[[str, str | list[str]], int]): SQL executor.
722 int: Writer return code.
724 result = writer(table, where)
726 f
"Writer executed for remove_data_from_table on '{table}', result={result}")
727 if isinstance(result, int)
and result == 0:
729 "Invalidating table caches after remove_data_from_table success")
738 writer: Callable[[str], int],
740 """Alias to :py:meth:`remove_data_from_table` without a WHERE clause."""
741 result = writer(table)
743 f
"Writer executed for drop_data_from_table on '{table}', result={result}")
744 if isinstance(result, int)
and result == 0:
746 "Invalidating table caches after drop_data_from_table success")
754 writer: Callable[[str], int],
756 """Drop a table and invalidate related caches.
759 table (str): Table name.
760 writer (Callable[[str], int]): SQL executor.
763 int: Writer return code.
765 result = writer(table)
767 f
"Writer executed for remove_table '{table}', result={result}")
768 if isinstance(result, int)
and result == 0:
770 "Invalidating caches after remove_table success")
774 [f
"{self.namespace}:{self.db_label}:schema:table_names"])
782 writer: Callable[[str], int],
784 """Alias to :py:meth:`remove_table`."""
791 writer: Callable[[str], int],
793 """Drop a trigger and invalidate related caches.
796 trigger_name (str): Trigger identifier.
797 writer (Callable[[str], int]): SQL executor.
800 int: Writer return code.
802 result = writer(trigger_name)
804 f
"Writer executed for remove_trigger '{trigger_name}', result={result}")
805 if isinstance(result, int)
and result == 0:
807 "Invalidating caches after remove_trigger success")
810 f
"{self.namespace}:{self.db_label}:schema:triggers",
811 f
"{self.namespace}:{self.db_label}:schema:trigger_names",
820 writer: Callable[[str], int],
822 """Alias to :py:meth:`remove_trigger`."""
str _key(self, str category, str name, Optional[Any] params=None)
None _invalidate_patterns(self, List[str] patterns)
Any _normalize_selector(self, Union[str, List[str]] selector)
Any _get_cached(self, str key)
None _set_cached(self, str key, Any value, int ttl_seconds)
None invalidate_trigger(self, Optional[str] trigger_name=None)
bool _should_cache_union_result(self, Any result, Optional[int] error_token)
None invalidate_table(self, str table)
None __init__(self, Optional[Union[Redis, RedisArgs]] client=None, bool debug=False, *, str namespace="sql", Optional[str] db_label=None, Optional[Dict[str, int]] default_ttls=None, Optional["RedisCaching"] existing_instance=None)
Union[int, str] get_trigger(self, str trigger_name, *, Callable[[str], Union[int, str]] fetcher, Optional[int] ttl_seconds=None, Optional[int] error_token=None)
int insert_or_update_data_into_table(self, str table, Union[List[List[Union[str, None, int, float]]], List[Union[str, None, int, float]]] data, Optional[List[str]] columns=None, *, Callable[[str, Union[List[List[Union[str, None, int, float]]], List[Union[str, None, int, float]]], Optional[List[str]]], int] writer)
Union[int, List[Any]] describe_table(self, str table, *, Callable[[str], Union[int, List[Any]]] fetcher, Optional[int] ttl_seconds=None, Optional[int] error_token=None)
Union[List[str], int] get_table_column_names(self, str table_name, *, Callable[[str], Union[List[str], int]] fetcher, Optional[int] ttl_seconds=None, Optional[int] error_token=None)
int insert_trigger(self, str trigger_name, str table_name, str timing_event, str body, *, Callable[[str, str, str, str], int] writer)
int remove_data_from_table(self, str table, Union[str, List[str]] where="", *, Callable[[str, Union[str, List[str]]], int] writer)
int create_trigger(self, str trigger_name, str table_name, str timing_event, str body, *, Callable[[str, str, str, str], int] writer)
int create_table(self, str table, List[Tuple[str, str]] columns, *, Callable[[str, List[Tuple[str, str]]], int] writer)
Union[int, Dict[str, str]] get_triggers(self, *, Callable[[], Union[int, Dict[str, str]]] fetcher, Optional[int] ttl_seconds=None, Optional[int] error_token=None)
int get_table_size(self, str table, Union[str, List[str]] column, Union[str, List[str]] where="", *, Callable[[str, Union[str, List[str]], Union[str, List[str]]], int] fetcher, Optional[int] ttl_seconds=None)
int update_data_in_table(self, str table, Union[List[List[Union[str, None, int, float]]], List[Union[str, None, int, float]]] data, List[str] column, Union[str, List[str]] where="", *, Callable[[str, Union[List[List[Union[str, None, int, float]]], List[Union[str, None, int, float]]], List[str], Union[str, List[str]]], int] writer)
int drop_trigger(self, str trigger_name, *, Callable[[str], int] writer)
Union[int, List[str]] get_trigger_names(self, *, Callable[[], Union[int, List[str]]] fetcher, Optional[int] ttl_seconds=None, Optional[int] error_token=None)
int insert_or_update_trigger(self, str trigger_name, str table_name, str timing_event, str body, *, Callable[[str, str, str, str], int] writer)
int remove_table(self, str table, *, Callable[[str], int] writer)
int remove_trigger(self, str trigger_name, *, Callable[[str], int] writer)
int drop_data_from_table(self, str table, *, Callable[[str], int] writer)
int drop_table(self, str table, *, Callable[[str], int] writer)
Union[int, List[str]] get_table_names(self, *, Callable[[], Union[int, List[str]]] fetcher, Optional[int] ttl_seconds=None, Optional[int] error_token=None)
int insert_data_into_table(self, str table, Union[List[List[Union[str, None, int, float]]], List[Union[str, None, int, float]]] data, Optional[List[str]] column=None, *, Callable[[str, Union[List[List[Union[str, None, int, float]]], List[Union[str, None, int, float]]], Optional[List[str]]], int] writer)
__new__(cls, *args, Optional["RedisCaching"] existing_instance=None, **kwargs)
Union[int, List[Dict[str, Any]]] get_data_from_table(self, str table, Union[str, List[str]] column, Union[str, List[str]] where="", *, Literal[True] beautify=True, Callable[[], Union[int, List[Dict[str, Any]]]] fetcher, Optional[int] ttl_seconds=None, Optional[int] error_token=None)
Optional[Tuple[int, int, int]] get_database_version(self, *, Callable[[], Optional[Tuple[int, int, int]]] fetcher, Optional[int] ttl_seconds=None)