Introduce proper XHTML boilerplate into the cvs2svn webpages.
[cvs2svn.git] / cvs2svn_lib / pass_manager.py
blob835a375d7725b619b8f8413daa3a5798d7282e18
1 # (Be in -*- python -*- mode.)
3 # ====================================================================
4 # Copyright (c) 2000-2006 CollabNet. All rights reserved.
6 # This software is licensed as described in the file COPYING, which
7 # you should have received as part of this distribution. The terms
8 # are also available at http://subversion.tigris.org/license-1.html.
9 # If newer versions of this license are posted there, you may use a
10 # newer version instead, at your option.
12 # This software consists of voluntary contributions made by many
13 # individuals. For exact contribution history, see the revision
14 # history and logs, available at http://cvs2svn.tigris.org/.
15 # ====================================================================
17 """This module contains tools to manage the passes of a conversion."""
20 import time
22 from boolean import *
23 from common import FatalError
24 import config
25 from context import Ctx
26 from log import Log
27 from stats_keeper import StatsKeeper
28 from artifact_manager import artifact_manager
31 class InvalidPassError(FatalError):
32 def __init__(self, msg):
33 FatalError.__init__(
34 self, msg + '\nUse --help-passes for more information.')
37 class PassManager:
38 """Manage a list of passes that can be executed separately or all at once.
40 Passes are numbered starting with 1."""
42 def __init__(self, passes):
43 """Construct a PassManager with the specified PASSES.
45 Internally, passes are numbered starting with 1. So PASSES[0] is
46 considered to be pass number 1."""
48 self.passes = passes
49 self.num_passes = len(self.passes)
51 def get_pass_number(self, pass_name, default=None):
52 """Return the number of the pass indicated by PASS_NAME.
54 PASS_NAME should be a string containing the name or number of a
55 pass. If a number, it should be in the range 1 <= value <=
56 self.num_passes. Return an integer in the same range. If
57 PASS_NAME is the empty string and DEFAULT is specified, return
58 DEFAULT. Raise InvalidPassError if PASS_NAME cannot be converted
59 into a valid pass number."""
61 if not pass_name and default is not None:
62 assert 1 <= default <= self.num_passes
63 return default
65 try:
66 # Does pass_name look like an integer?
67 pass_number = int(pass_name)
68 if not 1 <= pass_number <= self.num_passes:
69 raise InvalidPassError(
70 'illegal value (%d) for pass number. Must be 1 through %d or\n'
71 'the name of a known pass.'
72 % (pass_number,))
73 return pass_number
74 except ValueError:
75 # Is pass_name the name of one of the passes?
76 for i in range(len(self.passes)):
77 if self.passes[i].name == pass_name:
78 return i + 1
79 raise InvalidPassError('Unknown pass name (%r).' % (pass_name,))
81 def run(self, start_pass, end_pass):
82 """Run the specified passes, one after another.
84 START_PASS is the number of the first pass that should be run.
85 END_PASS is the number of the last pass that should be run. It
86 must be that 1 <= START_PASS <= END_PASS <= self.num_passes."""
88 # Convert start_pass and end_pass into the indices of the passes
89 # to execute, using the Python index range convention (i.e., first
90 # pass executed and first pass *after* the ones that should be
91 # executed).
92 index_start = start_pass - 1
93 index_end = end_pass
95 artifact_manager.register_temp_file(config.STATISTICS_FILE, self)
97 StatsKeeper().set_start_time(time.time())
99 # Inform the artifact manager when artifacts are created and used:
100 for the_pass in self.passes:
101 # The statistics object is needed for every pass:
102 artifact_manager.register_temp_file_needed(
103 config.STATISTICS_FILE, the_pass)
104 the_pass.register_artifacts()
106 # Tell the artifact manager about passes that are being skipped this run:
107 for the_pass in self.passes[0:index_start]:
108 artifact_manager.pass_skipped(the_pass)
110 start_time = time.time()
111 for i in range(index_start, index_end):
112 the_pass = self.passes[i]
113 Log().write(Log.QUIET,
114 '----- pass %d (%s) -----' % (i + 1, the_pass.name,))
115 the_pass.run()
116 end_time = time.time()
117 StatsKeeper().log_duration_for_pass(end_time - start_time, i + 1)
118 start_time = end_time
119 # Dispose of items in Ctx() not intended to live past the end of the
120 # pass (identified by exactly one leading underscore)
121 for attr in dir(Ctx()):
122 if (attr.startswith('_') and not attr.startswith('__')
123 and not attr.startswith('_Ctx__')):
124 delattr(Ctx(), attr)
125 StatsKeeper().set_end_time(time.time())
126 # Allow the artifact manager to clean up artifacts that are no
127 # longer needed:
128 artifact_manager.pass_done(the_pass)
130 # Tell the artifact manager about passes that are being deferred:
131 for the_pass in self.passes[index_end:]:
132 artifact_manager.pass_deferred(the_pass)
134 Log().write(Log.QUIET, StatsKeeper())
135 Log().write(Log.NORMAL, StatsKeeper().timings())
137 if index_end == self.num_passes:
138 # The overall conversion is done:
139 artifact_manager.pass_done(self)
140 else:
141 # The end is yet to come:
142 artifact_manager.pass_deferred(self)
144 # Consistency check:
145 artifact_manager.check_clean()
147 def help_passes(self):
148 """Output (to sys.stdout) the indices and names of available passes."""
150 print 'PASSES:'
151 for i in range(len(self.passes)):
152 print '%5d : %s' % (i + 1, self.passes[i].name,)