git_remote_helpers: add fastimport library
[git/dscho.git] / git_remote_helpers / fastimport / processor.py
blobbfb4226a46f9377f2ca008bc443af46a45287626
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
25 import logging
27 from git_remote_helpers.fastimport import errors
29 log = logging.getLogger(__name__)
32 class ImportProcessor(object):
33 """Base class for import processors.
35 Subclasses should override the pre_*, post_* and *_handler
36 methods as appropriate.
37 """
39 known_params = []
41 def __init__(self, params=None, verbose=False, outf=None):
42 if outf is None:
43 self.outf = sys.stdout
44 else:
45 self.outf = outf
46 self.verbose = verbose
47 if params is None:
48 self.params = {}
49 else:
50 self.params = params
51 self.validate_parameters()
53 # Handlers can set this to request exiting cleanly without
54 # iterating through the remaining commands
55 self.finished = False
57 def validate_parameters(self):
58 """Validate that the parameters are correctly specified."""
59 for p in self.params:
60 if p not in self.known_params:
61 raise errors.UnknownParameter(p, self.known_params)
63 def process(self, commands):
64 """Process a stream of fast-import commands from a parser.
66 :param commands: a sequence of commands.ImportCommand objects
67 """
68 self.pre_process()
69 for cmd in commands:
70 try:
71 handler = self.__class__.__dict__[cmd.name + "_handler"]
72 except KeyError:
73 raise errors.MissingHandler(cmd.name)
74 else:
75 self.pre_handler(cmd)
76 handler(self, cmd)
77 self.post_handler(cmd)
78 if self.finished:
79 break
80 self.post_process()
82 def pre_process(self):
83 """Hook for logic at start of processing.
85 Called just before process() starts iterating over its sequence
86 of commands.
87 """
88 pass
90 def post_process(self):
91 """Hook for logic at end of successful processing.
93 Called after process() finishes successfully iterating over its
94 sequence of commands (i.e. not called if an exception is raised
95 while processing commands).
96 """
97 pass
99 def pre_handler(self, cmd):
100 """Hook for logic before each handler starts."""
101 pass
103 def post_handler(self, cmd):
104 """Hook for logic after each handler finishes."""
105 pass
107 def progress_handler(self, cmd):
108 """Process a ProgressCommand."""
109 raise NotImplementedError(self.progress_handler)
111 def blob_handler(self, cmd):
112 """Process a BlobCommand."""
113 raise NotImplementedError(self.blob_handler)
115 def checkpoint_handler(self, cmd):
116 """Process a CheckpointCommand."""
117 raise NotImplementedError(self.checkpoint_handler)
119 def commit_handler(self, cmd):
120 """Process a CommitCommand."""
121 raise NotImplementedError(self.commit_handler)
123 def reset_handler(self, cmd):
124 """Process a ResetCommand."""
125 raise NotImplementedError(self.reset_handler)
127 def tag_handler(self, cmd):
128 """Process a TagCommand."""
129 raise NotImplementedError(self.tag_handler)
131 def feature_handler(self, cmd):
132 """Process a FeatureCommand."""
133 raise NotImplementedError(self.feature_handler)
136 class CommitHandler(object):
137 """Base class for commit handling.
139 Subclasses should override the pre_*, post_* and *_handler
140 methods as appropriate.
143 def __init__(self, command):
144 self.command = command
146 def process(self):
147 self.pre_process_files()
148 for fc in self.command.file_cmds:
149 try:
150 handler = self.__class__.__dict__[fc.name[4:] + "_handler"]
151 except KeyError:
152 raise errors.MissingHandler(fc.name)
153 else:
154 handler(self, fc)
155 self.post_process_files()
157 def _log(self, level, msg, *args):
158 log.log(level, msg + " (%s)", *(args + (self.command.id,)))
160 # Logging methods: unused in this library, but used by
161 # bzr-fastimport. Could be useful for other subclasses.
163 def note(self, msg, *args):
164 """log.info() with context about the command"""
165 self._log(logging.INFO, msg, *args)
167 def warning(self, msg, *args):
168 """log.warning() with context about the command"""
169 self._log(logging.WARNING, msg, *args)
171 def debug(self, msg, *args):
172 """log.debug() with context about the command"""
173 self._log(logging.DEBUG, msg, *args)
175 def pre_process_files(self):
176 """Prepare for committing."""
177 pass
179 def post_process_files(self):
180 """Save the revision."""
181 pass
183 def modify_handler(self, filecmd):
184 """Handle a filemodify command."""
185 raise NotImplementedError(self.modify_handler)
187 def delete_handler(self, filecmd):
188 """Handle a filedelete command."""
189 raise NotImplementedError(self.delete_handler)
191 def copy_handler(self, filecmd):
192 """Handle a filecopy command."""
193 raise NotImplementedError(self.copy_handler)
195 def rename_handler(self, filecmd):
196 """Handle a filerename command."""
197 raise NotImplementedError(self.rename_handler)
199 def deleteall_handler(self, filecmd):
200 """Handle a filedeleteall command."""
201 raise NotImplementedError(self.deleteall_handler)
204 def parseMany(filenames, parser_factory, processor):
205 """Parse multiple input files, sending the results all to
206 'processor'. parser_factory must be a callable that takes one input
207 file and returns an ImportParser instance, e.g. the ImportParser
208 class object itself. Each file in 'filenames' is opened, parsed,
209 and closed in turn. For filename \"-\", reads stdin.
211 for filename in filenames:
212 if filename == "-":
213 infile = sys.stdin
214 else:
215 infile = open(filename, "rb")
217 try:
218 parser = parser_factory(infile)
219 processor.process(parser.parse())
220 finally:
221 if filename != "-":
222 infile.close()