Persistent Data Storage with Tome Store
Many tome Commands need to save information between runs – perhaps user preferences, cached data, logs, or application-specific files. Instead of hardcoding file paths or scattering data across the user's system, tome provides a dedicated Store feature for this purpose.
The tome_api
object, passed to your Python command functions, has an attribute
tome_api.store.folder
. This attribute contains the absolute path to a
directory within the tome home (typically ~/.tome/storage/
) where your
scripts can reliably read and write files.
How to Use tome_api.store.folder
Let's illustrate with an example. Imagine a command that counts how many times it has been executed.
Example: A Simple Execution Counter
We'll create a command stats:runcount
that increments a counter each time it's
run and displays the current count.
Setup the Script: First, create the script using tome new
:
$ mkdir my-tome
$ cd my-tome
$ tome new stats:runcount --description "Counts times it has been run."
$ tome install . -e
Implement stats/runcount.py
:
# stats/runcount.py
from tome.command import tome_command
from tome.api.output import TomeOutput
import json
import os
COUNTER_FILENAME = "run_counter.json"
@tome_command()
def runcount(tome_api, parser, *args):
"""Counts times it has been run."""
parsed_args = parser.parse_args(*args) # Process any potential future args
output = TomeOutput(stdout=True)
# Define a path for this command's data within the tome store
command_storage_dir = os.path.join(tome_api.store.folder, "stats_runcount")
os.makedirs(command_storage_dir, exist_ok=True) # Ensure the directory exists
counter_file_path = os.path.join(command_storage_dir, COUNTER_FILENAME)
count = 0
# Load existing count
if os.path.exists(counter_file_path):
try:
with open(counter_file_path, 'r') as f:
data = json.load(f)
count = data.get("run_count", 0)
except (IOError, json.JSONDecodeError):
# If file is corrupt or unreadable, start count from 0
TomeOutput().warning(f"Could not read counter file, resetting count.")
count = 0
# Increment count
count += 1
# Save new count
try:
with open(counter_file_path, 'w') as f:
json.dump({"run_count": count}, f)
output.info(f"This command has been run {count} time(s).")
output.info(f"(Counter data stored in: {counter_file_path})")
except IOError as e:
TomeOutput().error(f"Could not save run count: {e}")
output.info(f"Current run (not saved) would be #{count}.")
Running the Command: Each time you run the command, the count should increment:
$ tome stats:runcount
This command has been run 1 time(s).
(Counter data stored in: /<tome_home>/storage/stats_runcount/run_counter.json)
$ tome stats:runcount
This command has been run 2 time(s).
(Counter data stored in: /<tome_home>/storage/stats_runcount/run_counter.json)
$ tome stats:runcount
This command has been run 3 time(s).
(Counter data stored in: /<tome_home>/storage/stats_runcount/run_counter.json)
You can inspect the run_counter.json
file in the specified path to see the
stored count.
Best Practices for Using the Store API
- Organize with Subdirectories: As shown, create subdirectories within
tome_api.store.folder
(e.g., based on your Namespace and/or Command name likeos.path.join(tome_api.store.folder, "your_namespace", "your_command")
). This prevents file naming conflicts if multiple Tomes or Commands use the store. - Handle File I/O Errors: Always wrap file operations (reading, writing) in
try...except IOError
blocks to manage potential issues gracefully. - Inform Users (If Necessary): If your command stores significant data or user-configurable files, consider documenting where this data is located (i.e., within the tome store) so users can understand, manage, or back it up if needed.