-
Notifications
You must be signed in to change notification settings - Fork 36
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement the H5StoreManager class. #129
Changes from all commits
694a840
ccabe8f
965e0eb
0a699fd
642ab51
e2598e7
7f0aef2
8c10735
dae9ea9
ed4ddde
ce814b4
32b1b1b
eb5bd73
fa32152
f42ed84
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
# Copyright (c) 2019 The Regents of the University of Michigan | ||
# All rights reserved. | ||
# This software is licensed under the BSD 3-Clause License. | ||
"Basic wrapper to access multiple different data stores." | ||
import os | ||
import re | ||
import errno | ||
import uuid | ||
|
||
from ..common import six | ||
|
||
|
||
class DictManager(object): | ||
|
||
cls = None | ||
suffix = None | ||
|
||
__slots__ = ['_prefix', '_dict_registry'] | ||
|
||
def __init__(self, prefix): | ||
assert self.cls is not None, "Subclasses of DictManager must define the cls variable." | ||
assert self.suffix is not None, "Subclasses of DictManager must define the suffix variable." | ||
self._prefix = os.path.abspath(prefix) | ||
self._dict_registry = dict() | ||
|
||
@property | ||
def prefix(self): | ||
return self._prefix | ||
|
||
def __eq__(self, other): | ||
return os.path.realpath(self.prefix) == os.path.realpath(other.prefix) and \ | ||
self.suffix == other.suffix | ||
|
||
def __repr__(self): | ||
return "{}(prefix='{}')".format(type(self).__name__, os.path.relpath(self.prefix)) | ||
|
||
__str__ = __repr__ | ||
|
||
def __getitem__(self, key): | ||
if key not in self._dict_registry: | ||
self._dict_registry[key] = self.cls(os.path.join(self.prefix, key) + self.suffix) | ||
return self._dict_registry[key] | ||
|
||
def __setitem__(self, key, value): | ||
tmp_key = str(uuid.uuid4()) | ||
try: | ||
self[tmp_key].update(value) | ||
if six.PY2: | ||
os.rename(self[tmp_key].filename, self[key].filename) | ||
else: | ||
os.replace(self[tmp_key].filename, self[key].filename) | ||
except (IOError, OSError) as error: | ||
if error.errno == errno.ENOENT and not len(value): | ||
raise ValueError("Cannot asssign empty value!") | ||
else: | ||
raise error | ||
except Exception as error: | ||
try: | ||
del self[tmp_key] | ||
except KeyError: | ||
pass | ||
raise error | ||
else: | ||
del self._dict_registry[key] | ||
|
||
def __delitem__(self, key): | ||
try: | ||
os.unlink(self[key].filename) | ||
except (IOError, OSError) as error: | ||
if error.errno == errno.ENOENT: | ||
raise KeyError(key) | ||
else: | ||
raise error | ||
|
||
def __getattr__(self, name): | ||
try: | ||
return super(DictManager, self).__getattribute__(name) | ||
except AttributeError: | ||
if name.startswith('__') or name in self.__slots__: | ||
raise | ||
try: | ||
return self.__getitem__(name) | ||
except KeyError: | ||
raise AttributeError(name) | ||
|
||
def __setattr__(self, name, value): | ||
if name.startswith('__') or name in self.__slots__: | ||
super(DictManager, self).__setattr__(name, value) | ||
else: | ||
self.__setitem__(name, value) | ||
|
||
def __delattr__(self, name): | ||
if name.startswith('__') or name in self.__slots__: | ||
super(DictManager, self).__delattr__(name) | ||
else: | ||
self.__delitem__(name) | ||
|
||
def __iter__(self): | ||
for fn in os.listdir(self.prefix): | ||
m = re.match('(.*){}'.format(self.suffix), fn) | ||
if m: | ||
yield m.groups()[0] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can't directly extract the key name with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suppose you'd still have to do something like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just tried it. I would also need to strip off the directory name, so I'm sticking to the original implementation. However, should we explicitly skip hidden h5-files? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think that's necessary. I view creating and editing hidden hd5 files as a feature, not a bug. If users explicitly access a hidden file I think it's reasonable to expect that they know what they're doing. |
||
|
||
def keys(self): | ||
return iter(self) | ||
|
||
def __len__(self): | ||
return len(list(self.keys())) | ||
|
||
def __getstate__(self): | ||
return dict(_prefix=self._prefix, _dict_registry=self._dict_registry) | ||
|
||
def __setstate__(self, d): | ||
self._prefix = d['_prefix'] | ||
self._dict_registry = d['_dict_registry'] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't follow why this is necessary. The file corresponding to
key
was successfully created if we reach this point, why does it need to be removed from the tracked list?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not really necessary, I figured it might be a good idea to replace any references of "old" H5Store under the same key, but there is virtually no difference. I'll remove that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Turns out this is actually necessary. I tried to delete this line and the
test_assign
andtest_assign_data
unit tests started to fail.