Spaces:
Paused
Paused
| """ | |
| PyRunner DataStore API for scripts. | |
| This module provides a simple key-value data store interface backed by | |
| the PyRunner SQLite database. Data stores must be created through the | |
| PyRunner web interface before they can be used in scripts. | |
| Usage: | |
| from pyrunner_datastore import DataStore | |
| # Open a data store (must exist in PyRunner UI) | |
| store = DataStore("my_store") | |
| # Store values (any JSON-serializable type) | |
| store["key"] = "value" | |
| store["config"] = {"retries": 3, "timeout": 30} | |
| store["results"] = [1, 2, 3, 4, 5] | |
| # Retrieve values | |
| value = store["key"] | |
| value = store.get("key", default=None) | |
| # Check existence | |
| if "key" in store: | |
| print(store["key"]) | |
| # Delete | |
| del store["key"] | |
| # Iterate | |
| for key, value in store.items(): | |
| print(f"{key}: {value}") | |
| # Utilities | |
| store.keys() # List all keys | |
| store.values() # List all values | |
| store.clear() # Delete all entries | |
| len(store) # Entry count | |
| """ | |
| import json | |
| import os | |
| import sqlite3 | |
| from typing import Any, Iterator, List, Optional, Tuple | |
| class DataStore: | |
| """ | |
| Simple key-value data store backed by SQLite. | |
| Provides dict-like access to stored data. | |
| """ | |
| def __init__(self, name: str): | |
| """ | |
| Open a data store by name. | |
| Args: | |
| name: The name of the data store (must exist in PyRunner) | |
| Raises: | |
| RuntimeError: If PYRUNNER_DB_PATH is not set | |
| ValueError: If the data store does not exist | |
| """ | |
| self.name = name | |
| self._db_path = os.environ.get("PYRUNNER_DB_PATH") | |
| if not self._db_path: | |
| raise RuntimeError( | |
| "PYRUNNER_DB_PATH not set. This module must be run from PyRunner." | |
| ) | |
| # Verify the data store exists | |
| self._store_id = self._get_store_id() | |
| if not self._store_id: | |
| raise ValueError(f"Data store '{name}' does not exist. Create it in the PyRunner UI first.") | |
| def _get_connection(self) -> sqlite3.Connection: | |
| """Get a database connection.""" | |
| conn = sqlite3.connect(self._db_path) | |
| conn.row_factory = sqlite3.Row | |
| return conn | |
| def _get_store_id(self) -> Optional[str]: | |
| """Get the UUID of this data store.""" | |
| with self._get_connection() as conn: | |
| cursor = conn.execute( | |
| "SELECT id FROM datastores WHERE name = ?", | |
| (self.name,) | |
| ) | |
| row = cursor.fetchone() | |
| return row["id"] if row else None | |
| def __getitem__(self, key: str) -> Any: | |
| """ | |
| Get a value by key. | |
| Args: | |
| key: The key to retrieve | |
| Returns: | |
| The stored value (deserialized from JSON) | |
| Raises: | |
| KeyError: If the key does not exist | |
| """ | |
| with self._get_connection() as conn: | |
| cursor = conn.execute( | |
| "SELECT value_json FROM datastore_entries " | |
| "WHERE datastore_id = ? AND key = ?", | |
| (self._store_id, key) | |
| ) | |
| row = cursor.fetchone() | |
| if row is None: | |
| raise KeyError(key) | |
| return json.loads(row["value_json"]) | |
| def __setitem__(self, key: str, value: Any) -> None: | |
| """ | |
| Set a value. Creates or updates the entry. | |
| Args: | |
| key: The key to set | |
| value: Any JSON-serializable value | |
| """ | |
| value_json = json.dumps(value) | |
| with self._get_connection() as conn: | |
| # Use INSERT OR REPLACE for SQLite upsert | |
| conn.execute( | |
| """ | |
| INSERT INTO datastore_entries (id, datastore_id, key, value_json, created_at, updated_at) | |
| VALUES (lower(hex(randomblob(16))), ?, ?, ?, datetime('now'), datetime('now')) | |
| ON CONFLICT(datastore_id, key) DO UPDATE SET | |
| value_json = excluded.value_json, | |
| updated_at = datetime('now') | |
| """, | |
| (self._store_id, key, value_json) | |
| ) | |
| conn.commit() | |
| def __delitem__(self, key: str) -> None: | |
| """ | |
| Delete a key. | |
| Args: | |
| key: The key to delete | |
| Raises: | |
| KeyError: If the key does not exist | |
| """ | |
| with self._get_connection() as conn: | |
| cursor = conn.execute( | |
| "DELETE FROM datastore_entries WHERE datastore_id = ? AND key = ?", | |
| (self._store_id, key) | |
| ) | |
| conn.commit() | |
| if cursor.rowcount == 0: | |
| raise KeyError(key) | |
| def __contains__(self, key: str) -> bool: | |
| """ | |
| Check if a key exists. | |
| Args: | |
| key: The key to check | |
| Returns: | |
| True if the key exists, False otherwise | |
| """ | |
| with self._get_connection() as conn: | |
| cursor = conn.execute( | |
| "SELECT 1 FROM datastore_entries WHERE datastore_id = ? AND key = ?", | |
| (self._store_id, key) | |
| ) | |
| return cursor.fetchone() is not None | |
| def __len__(self) -> int: | |
| """ | |
| Return the number of entries. | |
| Returns: | |
| The count of entries in this data store | |
| """ | |
| with self._get_connection() as conn: | |
| cursor = conn.execute( | |
| "SELECT COUNT(*) as count FROM datastore_entries WHERE datastore_id = ?", | |
| (self._store_id,) | |
| ) | |
| return cursor.fetchone()["count"] | |
| def __iter__(self) -> Iterator[str]: | |
| """ | |
| Iterate over keys. | |
| Yields: | |
| Each key in the data store | |
| """ | |
| return iter(self.keys()) | |
| def __repr__(self) -> str: | |
| return f"DataStore('{self.name}')" | |
| def get(self, key: str, default: Any = None) -> Any: | |
| """ | |
| Get a value with a default if not found. | |
| Args: | |
| key: The key to retrieve | |
| default: Value to return if key doesn't exist (default: None) | |
| Returns: | |
| The stored value or the default | |
| """ | |
| try: | |
| return self[key] | |
| except KeyError: | |
| return default | |
| def keys(self) -> List[str]: | |
| """ | |
| Return all keys in the data store. | |
| Returns: | |
| A list of all keys | |
| """ | |
| with self._get_connection() as conn: | |
| cursor = conn.execute( | |
| "SELECT key FROM datastore_entries WHERE datastore_id = ? ORDER BY key", | |
| (self._store_id,) | |
| ) | |
| return [row["key"] for row in cursor.fetchall()] | |
| def values(self) -> List[Any]: | |
| """ | |
| Return all values in the data store. | |
| Returns: | |
| A list of all values (deserialized from JSON) | |
| """ | |
| with self._get_connection() as conn: | |
| cursor = conn.execute( | |
| "SELECT value_json FROM datastore_entries WHERE datastore_id = ? ORDER BY key", | |
| (self._store_id,) | |
| ) | |
| return [json.loads(row["value_json"]) for row in cursor.fetchall()] | |
| def items(self) -> List[Tuple[str, Any]]: | |
| """ | |
| Return all key-value pairs. | |
| Returns: | |
| A list of (key, value) tuples | |
| """ | |
| with self._get_connection() as conn: | |
| cursor = conn.execute( | |
| "SELECT key, value_json FROM datastore_entries WHERE datastore_id = ? ORDER BY key", | |
| (self._store_id,) | |
| ) | |
| return [(row["key"], json.loads(row["value_json"])) for row in cursor.fetchall()] | |
| def clear(self) -> int: | |
| """ | |
| Delete all entries in the data store. | |
| Returns: | |
| The number of entries deleted | |
| """ | |
| with self._get_connection() as conn: | |
| cursor = conn.execute( | |
| "DELETE FROM datastore_entries WHERE datastore_id = ?", | |
| (self._store_id,) | |
| ) | |
| conn.commit() | |
| return cursor.rowcount | |
| def setdefault(self, key: str, default: Any = None) -> Any: | |
| """ | |
| Get a value, setting it to default if it doesn't exist. | |
| Args: | |
| key: The key to get/set | |
| default: Value to set if key doesn't exist (default: None) | |
| Returns: | |
| The existing value or the newly set default | |
| """ | |
| try: | |
| return self[key] | |
| except KeyError: | |
| self[key] = default | |
| return default | |
| def update(self, other: dict = None, **kwargs) -> None: | |
| """ | |
| Update the data store with key-value pairs. | |
| Args: | |
| other: A dictionary of key-value pairs | |
| **kwargs: Additional key-value pairs | |
| """ | |
| if other: | |
| for key, value in other.items(): | |
| self[key] = value | |
| for key, value in kwargs.items(): | |
| self[key] = value | |
| def pop(self, key: str, *default) -> Any: | |
| """ | |
| Remove and return a value. | |
| Args: | |
| key: The key to remove | |
| default: Optional default value if key doesn't exist | |
| Returns: | |
| The removed value or the default | |
| Raises: | |
| KeyError: If the key doesn't exist and no default is provided | |
| """ | |
| try: | |
| value = self[key] | |
| del self[key] | |
| return value | |
| except KeyError: | |
| if default: | |
| return default[0] | |
| raise | |