1
# -*- coding: utf-8 -*-
4
# Copyright (C) 2005-2007 Carabos Coop. V. All rights reserved
5
# Copyright (C) 2008-2011 Vicent Mas. All rights reserved
7
# This program is free software: you can redistribute it and/or modify
8
# it under the terms of the GNU General Public License as published by
9
# the Free Software Foundation, either version 3 of the License, or
10
# (at your option) any later version.
12
# This program is distributed in the hope that it will be useful,
13
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU General Public License for more details.
17
# You should have received a copy of the GNU General Public License
18
# along with this program. If not, see <http://www.gnu.org/licenses/>.
20
# Author: Vicent Mas - vmas@vitables.org
23
This module provides a thin wrapper to the `tables.File` class.
25
The module provides methods for opening and closing PyTables databases. It also
26
contains methods for editing nodes (but not data stored in nodes).
30
__docformat__ = 'restructuredtext'
36
from PyQt4 import QtCore
37
from PyQt4 import QtGui
42
translate = QtGui.QApplication.translate
45
class DBDoc(QtCore.QObject):
47
A database contained in an hdf5/PyTables file.
51
- `filePath`: full path of the DB
52
- `mode`: indicate the open mode of the database file. It can be
53
'r'ead-only or 'a'ppend
54
- `is_tmp_dbdoc`: True if this object represents the temporary database
57
def __init__(self, filepath, mode, is_tmp_dbdoc=False):
59
Makes a data structure that defines a given database.
61
A `DBDoc` instance is defined by the next attributes:
66
- h5file, the File instance returned when filePath is opened
69
super(DBDoc, self).__init__()
74
# The full path of the database
75
self.filepath = filepath
77
# The filename of the database
78
self.filename = os.path.basename(filepath)
80
# The tables.File instance
81
self.h5file = self.openH5File()
83
# The hidden group is used as an intermediate storage
85
self.hidden_group = None
88
self.h5file.createGroup(u'/', u'_p_query_results',
89
u'Hide the result of queries')
93
def tieToTempDB(self, tmp_dbdoc):
94
"""Setup the `tmp_dbdoc` instance variable.
97
self.tmp_dbdoc = tmp_dbdoc
98
self.tmp_h5file = self.tmp_dbdoc.h5file
101
def openH5File(self):
102
"""Open the file tied to this instance."""
105
# If read-write access is required then test file writability
106
if self.mode != 'r' and os.path.isfile(self.filepath):
107
if not os.access(self.filepath, os.W_OK):
109
print(translate('DBDoc',
110
"""\nWarning: file access in read-write mode"""
111
""" is denied. It will be opened in read-only mode.""",
112
'A logger error message'))
115
h5file = tables.openFile(self.filepath, self.mode)
116
except IOError, inst:
117
print(translate('DBDoc',
118
"\nError: {0}.", 'A logger error message').format(inst))
120
vitables.utils.formatExceptionInfo()
121
print(translate('DBDoc',
122
"Please, if you think it is a bug, report it to developers.",
123
'A logger error message'))
128
def closeH5File(self):
129
"""Closes a tables.File instance."""
134
vitables.utils.formatExceptionInfo()
137
def getFileFormat(self):
139
The format of the database file.
141
This is an accessor method intended to be used by external
144
:Returns: the format of the database file
148
if self.h5file._isPTFile:
149
format = 'PyTables file'
151
format = 'Generic HDF5 file'
156
def getNode(self, where):
158
The node whose path is where.
160
:Parameter where: the full path of the retrieved node
164
node = self.h5file.getNode(where)
166
except tables.exceptions.NoSuchNodeError:
167
print(translate('DBDoc',
168
"""\nError: cannot open node {0} in file {1} """,
169
'Error message').format(where, self.filepath))
170
vitables.utils.formatExceptionInfo()
175
""":Returns: the recursive list of full nodepaths for the file"""
176
return [node._v_pathname for node in self.h5file.walkNodes('/')]
182
def copyFile(self, dst_filepath):
184
Copy the contents of this file to another one.
186
.. Note:: Given two open files in a ``ViTables`` session, overwriting
187
one of them via ``File --> Save As...`` command may work or not. It
188
depends on the operating system. On Unices it works because a process
189
can delete or truncate a file open by a different process (unless
190
file is blocked). On Windows it seems to work fine too.
192
If the overwriting is not done via ``ViTables`` but in an interactive
193
session it fails, due (probably) to `HDF5` memory protection.
195
:Parameter dst_filepath: the full path of the destination file
199
self.h5file.copyFile(dst_filepath.encode('utf_8'), overwrite=True)
200
except tables.exceptions.HDF5ExtError:
201
print(translate('DBDoc',
202
"""\nError: unable to save the file {0} as """
203
"""{1}. Beware that only closed files can be safely """
204
"""overwritten via Save As...""",
205
'A logger error message').format(self.filepath, dst_filepath))
206
vitables.utils.formatExceptionInfo()
209
def deleteNode(self, nodepath):
210
"""Delete a tables.Node.
214
- `nodepath`: the full path of the node being deleted
218
self.h5file.removeNode(where=nodepath, recursive=True)
221
vitables.utils.formatExceptionInfo()
224
def createHiddenGroup(self):
226
Create a hidden group for storing cut nodes.
229
group_name = '_p_' + unicode(uuid.uuid4())
230
self.hidden_group = '/' + group_name
231
self.h5file.createGroup('/', group_name, 'Hide cut nodes')
235
def cutNode(self, nodepath):
236
"""Moves a tables.Node to a hidden group of its database.
238
The cut node must be stored somewhere or it will no be possible
239
to paste it later. Storing it in the same database is extremely
240
fast independently of the node size. Storing it in other database
241
(i.e. in the temporary database) would have a cost which depends
242
on the size of the cut node.
246
- `nodepath`: the path of the node being cut
249
if not self.hidden_group:
250
self.createHiddenGroup()
251
nodename = os.path.basename(nodepath)
252
# The hidden group should contain at most 1 node
253
for node in self.h5file.listNodes(self.hidden_group):
254
self.deleteNode(node._v_pathname)
255
self.moveNode(nodepath, self, self.hidden_group, nodename)
258
def pasteNode(self, src_nodepath, parent, childname):
259
"""Copy a tables.Node to a different location.
263
- `src_nodepath`: the path of the copied node being pasted
264
- `parent`: the new parent of the node being pasted
265
- `childname`: the new name of the node being pasted
269
self.h5file.copyNode(src_nodepath, newparent=parent,
270
newname=childname, overwrite=True, recursive=True)
273
vitables.utils.formatExceptionInfo()
276
def renameNode(self, nodepath, new_name):
278
Rename the selected node from the object tree.
282
- `nodepath`: the full path of the node being renamed
283
- `new_name`: the node new name
287
where, current_name = os.path.split(nodepath)
290
h5file.renameNode(where, new_name, current_name, overwrite=1)
293
vitables.utils.formatExceptionInfo()
296
def createGroup(self, where, final_name):
298
Create a new group under the given location.
302
- `where`: the full path of the parent node
303
- `final_name`: the new group name
308
if final_name in h5file.getNode(where)._v_children.keys():
309
h5file.removeNode(where, final_name, recursive=True)
310
h5file.createGroup(where, final_name, title='')
313
vitables.utils.formatExceptionInfo()
316
def moveNode(self, childpath, dst_dbdoc, parentpath, childname):
317
"""Move a tables.Node to a different location.
321
- `childpath`: the full path of the node being moved
322
- `dst_dbdoc`: the destination database (a :meth:`DBDoc` instance)
323
- `parentpath`: the full path of the new parent node
324
- `childname`: the name of the node in its final location
328
dst_h5file = dst_dbdoc.h5file
329
parent_node = dst_h5file.getNode(parentpath)
330
if self.h5file is dst_h5file:
331
self.h5file.moveNode(childpath, newparent=parent_node,
332
newname=childname, overwrite=True)
334
self.h5file.copyNode(childpath, newparent=parent_node,
335
newname=childname, overwrite=True, recursive=True)
337
src_where, src_nodename = os.path.split(childpath)
338
self.h5file.removeNode(src_where, src_nodename, recursive=1)
341
vitables.utils.formatExceptionInfo()