1 # Copyright (C) 2011 W. Trevor King <wking@drexel.edu>
3 # This file is part of h5config.
5 # h5config is free software; you can redistribute it and/or modify it
6 # under the terms of the GNU General Public License as published by the
7 # Free Software Foundation, either version 3 of the License, or (at your
8 # option) any later version.
10 # h5config is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 # General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with h5config. If not, see <http://www.gnu.org/licenses/>.
18 """HDF5 backend implementation
21 import types as _types
25 from .. import LOG as _LOG
26 from .. import config as _config
27 from . import FileStorage as _FileStorage
30 def pprint_HDF5(*args, **kwargs):
31 print pformat_HDF5(*args, **kwargs)
33 def pformat_HDF5(filename, group='/'):
34 with _h5py.File(filename, 'r') as f:
36 ret = '\n'.join(_pformat_hdf5(cwg))
39 def _pformat_hdf5(cwg, depth=0):
41 lines.append(' '*depth + cwg.name)
43 for key,value in cwg.iteritems():
44 if isinstance(value, _h5py.Group):
45 lines.extend(_pformat_hdf5(value, depth))
46 elif isinstance(value, _h5py.Dataset):
47 lines.append(' '*depth + str(value))
48 lines.append(' '*(depth+1) + str(value[...]))
50 lines.append(' '*depth + str(value))
53 def h5_create_group(cwg, path, force=False):
54 "Create the group where the settings are stored (if necessary)."
58 for group in path.strip('/').split('/'):
60 if group not in cwg.keys():
61 _LOG.debug('creating group {} in {}'.format(
62 '/'.join(gpath), cwg.file))
63 cwg.create_group(group)
65 if isinstance(_cwg, _h5py.Dataset):
67 _LOG.info('overwrite {} in {} ({}) with a group'.format(
68 '/'.join(gpath), _cwg.file, _cwg))
70 _cwg = cwg.create_group(group)
72 raise ValueError(_cwg)
77 class HDF5_Storage (_FileStorage):
78 """Back a `Config` class with an HDF5 file.
80 The `.save` and `.load` methods have an optional `group` argument
81 that allows you to save and load settings from an externally
82 opened HDF5 file. This can make it easier to stash several
83 related `Config` classes in a single file. For example
87 >>> from ..test import TestConfig
88 >>> fd,filename = tempfile.mkstemp(
89 ... suffix='.'+HDF5_Storage.extension, prefix='pypiezo-')
92 >>> f = _h5py.File(filename, 'a')
93 >>> c = TestConfig(storage=HDF5_Storage(
94 ... filename='untouched_file.h5', group='/untouched/group'))
96 >>> group = f.create_group('base')
97 >>> c.save(group=group)
98 >>> pprint_HDF5(filename) # doctest: +REPORT_UDIFF
101 <HDF5 dataset "age": shape (), type "<f8">
103 <HDF5 dataset "alive": shape (), type "|b1">
105 <HDF5 dataset "bids": shape (3,), type "<f8">
107 <HDF5 dataset "children": shape (), type "|S1">
109 <HDF5 dataset "daisies": shape (), type "<i4">
111 <HDF5 dataset "name": shape (), type "|S1">
113 <HDF5 dataset "species": shape (), type "|S14">
115 <HDF5 dataset "spouse": shape (), type "|S1">
120 >>> c.load(group=group)
125 >>> os.remove(filename)
129 def __init__(self, group='/', **kwargs):
130 super(HDF5_Storage, self).__init__(**kwargs)
131 assert group.startswith('/'), group
132 if not group.endswith('/'):
135 self._file_checked = False
137 def _check_file(self):
138 if self._file_checked:
141 self._file_checked = True
143 def _setup_file(self):
144 with _h5py.File(self._filename, 'a') as f:
145 cwg = f # current working group
146 h5_create_group(cwg, self.group)
148 def _load(self, config, group=None):
153 f = _h5py.File(self._filename, 'r')
154 group = f[self.group]
155 for s in config.settings:
156 if s.name not in group.keys():
158 elif isinstance(s, (_config.BooleanSetting,
159 _config.NumericSetting,
160 _config.FloatListSetting)):
161 v = group[s.name][...]
162 if isinstance(v, _types.StringTypes):
163 # convert back from None, etc.
164 v = s.convert_from_text(v)
165 elif isinstance(s, _config.FloatListSetting):
166 v = list(v) # convert from numpy array
168 elif isinstance(s, _config.ConfigListSetting):
170 cwg = h5_create_group(group, s.name)
175 for i in sorted(int(x) for x in cwg.keys()):
176 instance = s.config_class()
178 _cwg = h5_create_group(cwg, str(i))
182 self._load(config=instance, group=_cwg)
183 value.append(instance)
184 config[s.name] = value
185 elif isinstance(s, _config.ConfigSetting):
187 cwg = h5_create_group(group, s.name)
191 if not config[s.name]:
192 config[s.name] = s.config_class()
193 self._load(config=config[s.name], group=cwg)
195 config[s.name] = s.convert_from_text(group[s.name][...])
200 def _save(self, config, group=None):
205 f = _h5py.File(self._filename, 'a')
206 group = f[self.group]
207 for s in config.settings:
209 if isinstance(s, (_config.BooleanSetting,
210 _config.NumericSetting,
211 _config.FloatListSetting)):
212 value = config[s.name]
213 if value in [None, []]:
214 value = s.convert_to_text(value)
215 elif isinstance(s, _config.ConfigListSetting):
216 configs = config[s.name]
218 cwg = h5_create_group(group, s.name, force=True)
219 for i,cfg in enumerate(configs):
220 _cwg = h5_create_group(cwg, str(i), force=True)
221 self._save(config=cfg, group=_cwg)
223 elif isinstance(s, _config.ConfigSetting):
226 cwg = h5_create_group(group, s.name, force=True)
227 self._save(config=cfg, group=cwg)
229 if value is None: # not set yet, or invalid
230 value = s.convert_to_text(config[s.name])
235 group[s.name] = value