fix timezones in darcs-fast-export, take 2
[bzr-fastimport/rorcz.git] / processor.py
blob74f71832b4c438794ca9293a6f670bac900ca520
1 # Copyright (C) 2008 Canonical Ltd
3 # This program is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation; either version 2 of the License, or
6 # (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software
15 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 """Processor of import commands.
19 This module provides core processing functionality including an abstract class
20 for basing real processors on. See the processors package for examples.
21 """
23 import sys
24 import time
26 from bzrlib import debug
27 from bzrlib.errors import NotBranchError
28 from bzrlib.trace import (
29 mutter,
30 note,
31 warning,
33 import errors
36 class ImportProcessor(object):
37 """Base class for import processors.
39 Subclasses should override the pre_*, post_* and *_handler
40 methods as appropriate.
41 """
43 known_params = []
45 def __init__(self, bzrdir, params=None, verbose=False, outf=None):
46 if outf is None:
47 self.outf = sys.stdout
48 else:
49 self.outf = outf
50 self.verbose = verbose
51 if params is None:
52 self.params = {}
53 else:
54 self.params = params
55 self.validate_parameters()
56 self.bzrdir = bzrdir
57 if bzrdir is None:
58 # Some 'importers' don't need a repository to write to
59 self.working_tree = None
60 self.branch = None
61 self.repo = None
62 else:
63 try:
64 # Might be inside a branch
65 (self.working_tree, self.branch) = bzrdir._get_tree_branch()
66 self.repo = self.branch.repository
67 except NotBranchError:
68 # Must be inside a repository
69 self.working_tree = None
70 self.branch = None
71 self.repo = bzrdir.open_repository()
73 # Handlers can set this to request exiting cleanly without
74 # iterating through the remaining commands
75 self.finished = False
77 def validate_parameters(self):
78 """Validate that the parameters are correctly specified."""
79 for p in self.params:
80 if p not in self.known_params:
81 raise errors.UnknownParameter(p, self.known_params)
83 def process(self, command_iter):
84 """Import data into Bazaar by processing a stream of commands.
86 :param command_iter: an iterator providing commands
87 """
88 if self.working_tree is not None:
89 self.working_tree.lock_write()
90 elif self.branch is not None:
91 self.branch.lock_write()
92 elif self.repo is not None:
93 self.repo.lock_write()
94 try:
95 self._process(command_iter)
96 finally:
97 # If an unhandled exception occurred, abort the write group
98 if self.repo is not None and self.repo.is_in_write_group():
99 self.repo.abort_write_group()
100 # Release the locks
101 if self.working_tree is not None:
102 self.working_tree.unlock()
103 elif self.branch is not None:
104 self.branch.unlock()
105 elif self.repo is not None:
106 self.repo.unlock()
108 def _process(self, command_iter):
109 self.pre_process()
110 for cmd in command_iter():
111 try:
112 handler = self.__class__.__dict__[cmd.name + "_handler"]
113 except KeyError:
114 raise errors.MissingHandler(cmd.name)
115 else:
116 self.pre_handler(cmd)
117 handler(self, cmd)
118 self.post_handler(cmd)
119 if self.finished:
120 break
121 self.post_process()
123 def note(self, msg, *args):
124 """Output a note but timestamp it."""
125 msg = "%s %s" % (self._time_of_day(), msg)
126 note(msg, *args)
128 def warning(self, msg, *args):
129 """Output a warning but timestamp it."""
130 msg = "%s WARNING: %s" % (self._time_of_day(), msg)
131 warning(msg, *args)
133 def debug(self, mgs, *args):
134 """Output a debug message if the appropriate -D option was given."""
135 if "fast-import" in debug.debug_flags:
136 msg = "%s DEBUG: %s" % (self._time_of_day(), msg)
137 mutter(msg, *args)
139 def _time_of_day(self):
140 """Time of day as a string."""
141 # Note: this is a separate method so tests can patch in a fixed value
142 return time.strftime("%H:%M:%S")
144 def pre_process(self):
145 """Hook for logic at start of processing."""
146 pass
148 def post_process(self):
149 """Hook for logic at end of processing."""
150 pass
152 def pre_handler(self, cmd):
153 """Hook for logic before each handler starts."""
154 pass
156 def post_handler(self, cmd):
157 """Hook for logic after each handler finishes."""
158 pass
160 def progress_handler(self, cmd):
161 """Process a ProgressCommand."""
162 raise NotImplementedError(self.progress_handler)
164 def blob_handler(self, cmd):
165 """Process a BlobCommand."""
166 raise NotImplementedError(self.blob_handler)
168 def checkpoint_handler(self, cmd):
169 """Process a CheckpointCommand."""
170 raise NotImplementedError(self.checkpoint_handler)
172 def commit_handler(self, cmd):
173 """Process a CommitCommand."""
174 raise NotImplementedError(self.commit_handler)
176 def reset_handler(self, cmd):
177 """Process a ResetCommand."""
178 raise NotImplementedError(self.reset_handler)
180 def tag_handler(self, cmd):
181 """Process a TagCommand."""
182 raise NotImplementedError(self.tag_handler)
184 def feature_handler(self, cmd):
185 """Process a FeatureCommand."""
186 raise NotImplementedError(self.feature_handler)
189 class CommitHandler(object):
190 """Base class for commit handling.
192 Subclasses should override the pre_*, post_* and *_handler
193 methods as appropriate.
196 def __init__(self, command):
197 self.command = command
199 def process(self):
200 self.pre_process_files()
201 for fc in self.command.file_iter():
202 try:
203 handler = self.__class__.__dict__[fc.name[4:] + "_handler"]
204 except KeyError:
205 raise errors.MissingHandler(fc.name)
206 else:
207 handler(self, fc)
208 self.post_process_files()
210 def note(self, msg, *args):
211 """Output a note but add context."""
212 msg = "%s (%s)" % (msg, self.command.id)
213 note(msg, *args)
215 def warning(self, msg, *args):
216 """Output a warning but add context."""
217 msg = "WARNING: %s (%s)" % (msg, self.command.id)
218 warning(msg, *args)
220 def mutter(self, msg, *args):
221 """Output a mutter but add context."""
222 msg = "%s (%s)" % (msg, self.command.id)
223 mutter(msg, *args)
225 def debug(self, msg, *args):
226 """Output a mutter if the appropriate -D option was given."""
227 if "fast-import" in debug.debug_flags:
228 msg = "%s (%s)" % (msg, self.command.id)
229 mutter(msg, *args)
231 def pre_process_files(self):
232 """Prepare for committing."""
233 pass
235 def post_process_files(self):
236 """Save the revision."""
237 pass
239 def modify_handler(self, filecmd):
240 """Handle a filemodify command."""
241 raise NotImplementedError(self.modify_handler)
243 def delete_handler(self, filecmd):
244 """Handle a filedelete command."""
245 raise NotImplementedError(self.delete_handler)
247 def copy_handler(self, filecmd):
248 """Handle a filecopy command."""
249 raise NotImplementedError(self.copy_handler)
251 def rename_handler(self, filecmd):
252 """Handle a filerename command."""
253 raise NotImplementedError(self.rename_handler)
255 def deleteall_handler(self, filecmd):
256 """Handle a filedeleteall command."""
257 raise NotImplementedError(self.deleteall_handler)