Back to index

obnam  1.1
repo_tree.py
Go to the documentation of this file.
00001 # Copyright 2010  Lars Wirzenius
00002 # 
00003 # This program is free software: you can redistribute it and/or modify
00004 # it under the terms of the GNU General Public License as published by
00005 # the Free Software Foundation, either version 3 of the License, or
00006 # (at your option) any later version.
00007 # 
00008 # This program is distributed in the hope that it will be useful,
00009 # but WITHOUT ANY WARRANTY; without even the implied warranty of
00010 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00011 # GNU General Public License for more details.
00012 # 
00013 # You should have received a copy of the GNU General Public License
00014 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
00015 
00016 
00017 import larch
00018 import tracing
00019 
00020 import obnamlib
00021 
00022 
00023 class RepositoryTree(object):
00024 
00025     '''A B-tree within an obnamlib.Repository.
00026     
00027     For read-only operation, call init_forest before doing anything.
00028     
00029     For read-write operation, call start_changes before doing anything,
00030     and commit afterwards. In between, self.tree is the new tree to be 
00031     modified. Note that self.tree is NOT available after init_forest.
00032     
00033     After init_forest or start_changes, self.forest is the opened forest.
00034     Unlike self.tree, it will not go away after commit.
00035     
00036     '''
00037 
00038     def __init__(self, fs, dirname, key_bytes, node_size, upload_queue_size,
00039                  lru_size, repo):
00040         self.fs = fs
00041         self.dirname = dirname
00042         self.key_bytes = key_bytes
00043         self.node_size = node_size
00044         self.upload_queue_size = upload_queue_size
00045         self.lru_size = lru_size
00046         self.repo = repo
00047         self.forest = None
00048         self.forest_allows_writes = False
00049         self.tree = None
00050         self.keep_just_one_tree = False
00051 
00052     def init_forest(self, allow_writes=False):
00053         if self.forest is None:
00054             tracing.trace('initializing forest dirname=%s', self.dirname)
00055             assert self.tree is None
00056             if not self.fs.exists(self.dirname):
00057                 tracing.trace('%s does not exist', self.dirname)
00058                 return False
00059             self.forest = larch.open_forest(key_size=self.key_bytes,
00060                                             node_size=self.node_size,
00061                                             dirname=self.dirname,
00062                                             upload_max=self.upload_queue_size,
00063                                             lru_size=self.lru_size,
00064                                             vfs=self.fs,
00065                                             allow_writes=allow_writes)
00066             self.forest_allows_writes = allow_writes
00067         return True
00068 
00069     def start_changes(self, create_tree=True):
00070         tracing.trace('start changes for %s', self.dirname)
00071         
00072         if self.forest is None or not self.forest_allows_writes:
00073             if not self.fs.exists(self.dirname):
00074                 need_init = True
00075             else:
00076                 filenames = self.fs.listdir(self.dirname)
00077                 need_init = filenames == [] or filenames == ['lock']
00078 
00079             if need_init:
00080                 if not self.fs.exists(self.dirname):
00081                     tracing.trace('create %s', self.dirname)
00082                     self.fs.mkdir(self.dirname)
00083                 self.repo.hooks.call('repository-toplevel-init', self.repo, 
00084                                      self.dirname)
00085             self.forest = None
00086             self.init_forest(allow_writes=True)
00087 
00088         assert self.forest is not None
00089         assert self.forest_allows_writes, \
00090             'it is "%s"' % repr(self.forest_allows_writes)
00091 
00092         if self.tree is None and create_tree:
00093             if self.forest.trees:
00094                 self.tree = self.forest.new_tree(self.forest.trees[-1])
00095                 tracing.trace('use newest tree %s (of %d)', self.tree.root.id,
00096                                 len(self.forest.trees))
00097             else:
00098                 self.tree = self.forest.new_tree()
00099                 tracing.trace('new tree root id %s', self.tree.root.id)
00100 
00101     def commit(self):
00102         tracing.trace('committing')
00103         if self.forest:
00104             if self.keep_just_one_tree:
00105                 while len(self.forest.trees) > 1:
00106                     tracing.trace('not keeping tree with root id %s',
00107                                   self.forest.trees[0].root.id)
00108                     self.forest.remove_tree(self.forest.trees[0])
00109             self.forest.commit()
00110             self.tree = None
00111