diff options
| author | Stan Seibert <stan@mtrr.org> | 2012-01-13 12:03:46 -0500 |
|---|---|---|
| committer | tlatorre <tlatorre@uchicago.edu> | 2021-05-09 08:42:38 -0700 |
| commit | 7947f295b077fb830517b70693afc6283074dfd5 (patch) | |
| tree | b8323e7ef612766d75832bd39534f78d3ef0f747 /chroma | |
| parent | 3076e6b2032d0acbd44966a9428fdc6de1bbcf3b (diff) | |
| download | chroma-7947f295b077fb830517b70693afc6283074dfd5.tar.gz chroma-7947f295b077fb830517b70693afc6283074dfd5.tar.bz2 chroma-7947f295b077fb830517b70693afc6283074dfd5.zip | |
Cache class for managing the geometry cache on disk.
The storage format is changing relative to the old format, so all
geometry files will be saved in the ~/.chroma/geo directory. For now,
the format is just a simple pickle. We know this is not optimal for
space or speed, but the Geometry class will be changing soon, and we
can optimize further after that.
This Cache class will also soon manage the separate BVH cache.
Diffstat (limited to 'chroma')
| -rw-r--r-- | chroma/cache.py | 160 |
1 files changed, 160 insertions, 0 deletions
diff --git a/chroma/cache.py b/chroma/cache.py new file mode 100644 index 0000000..fe7d180 --- /dev/null +++ b/chroma/cache.py @@ -0,0 +1,160 @@ +'''chroma.cache: On-disk geometry and bounding volume hierarchy cache. + +The ``Cache`` class is used to manage an on-disk cache of geometry and +BVH objects, which are slow to calculate. By default, the cache is in +$HOME/.chroma. + +Currently this cache is not thread or process-safe when a writing +process is present. +''' + +import os +import cPickle as pickle + +from chroma.log import logger + +class GeometryNotFoundError(Exception): + '''A requested geometry was not found in the on-disk cache.''' + def __init__(self, msg): + Exception.__init__(self, msg) + +def verify_or_create_dir(dirname, exception_msg, logger_msg=None): + '''Checks if ``dirname`` exists and is a directory. If it does not exist, + then it is created. If it does exist, but is not a directory, an IOError + is raised with ``exception_message`` as the description. + + If the directory is created, an info message will be sent to the + Chroma logger if ``logger_message`` is not None. + ''' + if not os.path.isdir(dirname): + if os.path.exists(dirname): + raise IOError(exception_msg) + else: + if logger_msg is not None: + logger.info(logger_msg) + os.mkdir(dirname) + +class Cache(object): + '''Class for manipulating a Chroma disk cache directory. + + Use this class to read and write cached geometries or bounding + volume hierarchies rather than reading and writing disk directly. + ''' + + def __init__(self, cache_dir=os.path.expanduser('~/.chroma/')): + '''Open a Chroma cache stored at ``cache_dir``. + + If ``cache_dir`` does not already exist, it will be created. By default, + the cache is in the ``.chroma`` directory under the user's home directory. + ''' + self.cache_dir = cache_dir + verify_or_create_dir(self.cache_dir, + exception_msg='Path for cache already exists, ' + 'but is not a directory: %s' % cache_dir, + logger_msg='Creating new Chroma cache directory at %s' + % cache_dir) + + + self.geo_dir = os.path.join(cache_dir, 'geo') + verify_or_create_dir(self.geo_dir, + exception_msg='Path for geometry directory in cache ' + 'already exists, but is not a directory: %s' + % self.geo_dir) + + self.bvh_dir = os.path.join(cache_dir, 'bvh') + verify_or_create_dir(self.bvh_dir, + exception_msg='Path for BVH directory in cache already ' + 'exists, but is not a directory: %s' % self.bvh_dir) + + + def get_geometry_filename(self, name): + '''Return the full pathname for the geometry file corresponding to + ``name``. + ''' + return os.path.join(self.geo_dir, name) + + def list_geometry(self): + '''Returns a list of all geometry names in the cache.''' + return os.listdir(self.geo_dir) + + def save_geometry(self, name, geometry): + '''Save ``geometry`` in the cache with the name ``name``.''' + geo_file = self.get_geometry_filename(name) + with open(geo_file, 'wb') as output_file: + pickle.dump(geometry.mesh.md5(), output_file, + pickle.HIGHEST_PROTOCOL) + pickle.dump(geometry, output_file, + pickle.HIGHEST_PROTOCOL) + + def load_geometry(self, name): + '''Returns the chroma.geometry.Geometry object associated with + ``name`` in the cache. + + Raises ``GeometryNotFoundError`` if ``name`` is not in the cache. + ''' + geo_file = self.get_geometry_filename(name) + if not os.path.exists(geo_file): + raise GeometryNotFoundError(name) + with open(geo_file, 'rb') as input_file: + _ = pickle.load(input_file) # skip mesh hash + geo = pickle.load(input_file) + return geo + + def remove_geometry(self, name): + '''Remove the geometry file associated with ``name`` from the cache. + + If ``name`` does not exist, no action is taken. + ''' + geo_file = self.get_geometry_filename(name) + if os.path.exists(geo_file): + os.remove(geo_file) + + def get_geometry_hash(self, name): + '''Get the mesh hash for the geometry associated with ``name``. + + This is faster than loading the entire geometry file and calling the + hash() method on the mesh. + ''' + geo_file = self.get_geometry_filename(name) + if not os.path.exists(geo_file): + raise GeometryNotFoundError(name) + with open(geo_file, 'rb') as input_file: + return pickle.load(input_file) + + def load_default_geometry(self): + '''Load the default geometry as set by previous call to + set_default_geometry(). + + If no geometry has been designated the default, raise + GeometryNotFoundError. + ''' + return self.load_geometry('.default') + + def set_default_geometry(self, name): + '''Set the geometry in the cache corresponding to ``name`` to + be the default geometry returned by load_default_geometry(). + ''' + default_geo_file = self.get_geometry_filename('.default') + geo_file = self.get_geometry_filename(name) + if not os.path.exists(geo_file): + raise GeometryNotFoundError(name) + + if os.path.exists(default_geo_file): + if os.path.islink(default_geo_file): + os.remove(default_geo_file) + else: + raise IOError('Non-symlink found where expected a symlink: ' + +default_geo_file) + os.symlink(geo_file, default_geo_file) + + + def list_bvh(self, mesh_hash): + pass + + def save_bvh(self, mesh_hash, name=None): + pass + + def load_bvh(self, mesh_hash, name=None): + pass + + |
