~ubuntu-branches/ubuntu/oneiric/vitables/oneiric

« back to all changes in this revision

Viewing changes to vitables/h5db/dbDoc.py

  • Committer: Bazaar Package Importer
  • Author(s): Picca Frédéric-Emmanuel
  • Date: 2011-06-19 12:35:53 UTC
  • Revision ID: james.westby@ubuntu.com-20110619123553-sxhg7ifegpogzfaj
Tags: upstream-2.1
Import upstream version 2.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- coding: utf-8 -*-
 
2
#!/usr/bin/env python
 
3
 
 
4
#       Copyright (C) 2005-2007 Carabos Coop. V. All rights reserved
 
5
#       Copyright (C) 2008-2011 Vicent Mas. All rights reserved
 
6
#
 
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.
 
11
#
 
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.
 
16
#
 
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/>.
 
19
#
 
20
#       Author:  Vicent Mas - vmas@vitables.org
 
21
 
 
22
"""
 
23
This module provides a thin wrapper to the `tables.File` class.
 
24
 
 
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).
 
27
 
 
28
"""
 
29
 
 
30
__docformat__ = 'restructuredtext'
 
31
 
 
32
import os
 
33
import uuid
 
34
 
 
35
import tables
 
36
from PyQt4 import QtCore
 
37
from PyQt4 import QtGui
 
38
 
 
39
 
 
40
import vitables.utils
 
41
 
 
42
translate = QtGui.QApplication.translate
 
43
 
 
44
 
 
45
class DBDoc(QtCore.QObject):
 
46
    """
 
47
    A database contained in an hdf5/PyTables file.
 
48
 
 
49
    :Parameters:
 
50
 
 
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
 
55
    """
 
56
 
 
57
    def __init__(self, filepath, mode, is_tmp_dbdoc=False):
 
58
        """
 
59
        Makes a data structure that defines a given database.
 
60
 
 
61
        A `DBDoc` instance is defined by the next attributes:
 
62
 
 
63
        - filepath
 
64
        - filename
 
65
        - mode
 
66
        - h5file, the File instance returned when filePath is opened
 
67
        """
 
68
 
 
69
        super(DBDoc, self).__init__()
 
70
 
 
71
        # The opening mode
 
72
        self.mode = mode
 
73
 
 
74
        # The full path of the database
 
75
        self.filepath = filepath
 
76
 
 
77
        # The filename of the database
 
78
        self.filename = os.path.basename(filepath)
 
79
 
 
80
        # The tables.File instance
 
81
        self.h5file = self.openH5File()
 
82
 
 
83
        # The hidden group is used as an intermediate storage
 
84
        # in cut operations
 
85
        self.hidden_group = None
 
86
 
 
87
        if is_tmp_dbdoc:
 
88
            self.h5file.createGroup(u'/', u'_p_query_results', 
 
89
                u'Hide the result of queries')
 
90
            self.h5file.flush()
 
91
 
 
92
 
 
93
    def tieToTempDB(self, tmp_dbdoc):
 
94
        """Setup the `tmp_dbdoc` instance variable.
 
95
        """
 
96
 
 
97
        self.tmp_dbdoc = tmp_dbdoc
 
98
        self.tmp_h5file = self.tmp_dbdoc.h5file
 
99
 
 
100
 
 
101
    def openH5File(self):
 
102
        """Open the file tied to this instance."""
 
103
 
 
104
        h5file = None
 
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):
 
108
                self.mode = 'r'
 
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'))
 
113
 
 
114
        try:
 
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))
 
119
        except:
 
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'))
 
124
 
 
125
        return h5file
 
126
 
 
127
 
 
128
    def closeH5File(self):
 
129
        """Closes a tables.File instance."""
 
130
 
 
131
        try:
 
132
            self.h5file.close()
 
133
        except:
 
134
            vitables.utils.formatExceptionInfo()
 
135
 
 
136
 
 
137
    def getFileFormat(self):
 
138
        """
 
139
        The format of the database file.
 
140
 
 
141
        This is an accessor method intended to be used by external
 
142
        classes.
 
143
 
 
144
        :Returns: the format of the database file
 
145
        """
 
146
        format = None
 
147
        if self.h5file:
 
148
            if self.h5file._isPTFile:
 
149
                format = 'PyTables file'
 
150
            else:
 
151
                format = 'Generic HDF5 file'
 
152
 
 
153
        return format
 
154
 
 
155
 
 
156
    def getNode(self, where):
 
157
        """
 
158
        The node whose path is where.
 
159
 
 
160
        :Parameter where: the full path of the retrieved node
 
161
        """
 
162
 
 
163
        try:
 
164
            node = self.h5file.getNode(where)
 
165
            return node
 
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()
 
171
            return None
 
172
 
 
173
 
 
174
    def listNodes(self):
 
175
        """:Returns: the recursive list of full nodepaths for the file"""
 
176
        return [node._v_pathname for node in self.h5file.walkNodes('/')]
 
177
 
 
178
    # 
 
179
    # Editing databases
 
180
    # 
 
181
 
 
182
    def copyFile(self, dst_filepath):
 
183
        """
 
184
        Copy the contents of this file to another one.
 
185
 
 
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.
 
191
 
 
192
        If the overwriting is not done via ``ViTables`` but in an interactive
 
193
        session it fails, due (probably) to `HDF5` memory protection.
 
194
 
 
195
        :Parameter dst_filepath: the full path of the destination file
 
196
        """
 
197
 
 
198
        try:
 
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()
 
207
 
 
208
 
 
209
    def deleteNode(self, nodepath):
 
210
        """Delete a tables.Node.
 
211
 
 
212
        :Parameters:
 
213
 
 
214
        - `nodepath`: the full path of the node being deleted
 
215
        """
 
216
 
 
217
        try:
 
218
            self.h5file.removeNode(where=nodepath, recursive=True)
 
219
            self.h5file.flush()
 
220
        except:
 
221
            vitables.utils.formatExceptionInfo()
 
222
 
 
223
 
 
224
    def createHiddenGroup(self):
 
225
        """
 
226
        Create a hidden group for storing cut nodes.
 
227
        """
 
228
 
 
229
        group_name = '_p_' + unicode(uuid.uuid4())
 
230
        self.hidden_group = '/' + group_name
 
231
        self.h5file.createGroup('/', group_name, 'Hide cut nodes')
 
232
        self.h5file.flush()
 
233
 
 
234
 
 
235
    def cutNode(self, nodepath):
 
236
        """Moves a tables.Node to a hidden group of its database.
 
237
 
 
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.
 
243
 
 
244
        :Parameters:
 
245
 
 
246
        - `nodepath`: the path of the node being cut
 
247
        """
 
248
 
 
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)
 
256
 
 
257
 
 
258
    def pasteNode(self, src_nodepath, parent, childname):
 
259
        """Copy a tables.Node to a different location.
 
260
 
 
261
        :Parameters:
 
262
 
 
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
 
266
        """
 
267
 
 
268
        try:
 
269
            self.h5file.copyNode(src_nodepath, newparent=parent,
 
270
                newname=childname, overwrite=True, recursive=True)
 
271
            self.h5file.flush()
 
272
        except:
 
273
            vitables.utils.formatExceptionInfo()
 
274
 
 
275
 
 
276
    def renameNode(self, nodepath, new_name):
 
277
        """
 
278
        Rename the selected node from the object tree.
 
279
 
 
280
        :Parameters:
 
281
 
 
282
        - `nodepath`: the full path of the node being renamed
 
283
        - `new_name`: the node new name
 
284
        """
 
285
 
 
286
        h5file = self.h5file
 
287
        where, current_name = os.path.split(nodepath)
 
288
 
 
289
        try:
 
290
            h5file.renameNode(where, new_name, current_name, overwrite=1)
 
291
            h5file.flush()
 
292
        except:
 
293
            vitables.utils.formatExceptionInfo()
 
294
 
 
295
 
 
296
    def createGroup(self, where, final_name):
 
297
        """
 
298
        Create a new group under the given location.
 
299
 
 
300
        :Parameters:
 
301
 
 
302
        - `where`: the full path of the parent node
 
303
        - `final_name`: the new group name
 
304
        """
 
305
 
 
306
        h5file = self.h5file
 
307
        try:
 
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='')
 
311
            h5file.flush()
 
312
        except:
 
313
            vitables.utils.formatExceptionInfo()
 
314
 
 
315
 
 
316
    def moveNode(self, childpath, dst_dbdoc, parentpath, childname):
 
317
        """Move a tables.Node to a different location.
 
318
 
 
319
        :Parameters:
 
320
 
 
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
 
325
        """
 
326
 
 
327
        try:
 
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)
 
333
            else:
 
334
                self.h5file.copyNode(childpath, newparent=parent_node,
 
335
                    newname=childname, overwrite=True, recursive=True)
 
336
                dst_h5file.flush()
 
337
                src_where, src_nodename = os.path.split(childpath)
 
338
                self.h5file.removeNode(src_where, src_nodename, recursive=1)
 
339
            self.h5file.flush()
 
340
        except:
 
341
            vitables.utils.formatExceptionInfo()