/usr/lib/python2.7/dist-packages/cvs2svn_lib/symbol_transform.py is in cvs2svn 2.4.0-2.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 | # (Be in -*- python -*- mode.)
#
# ====================================================================
# Copyright (c) 2006-2009 CollabNet. All rights reserved.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
# are also available at http://subversion.tigris.org/license-1.html.
# If newer versions of this license are posted there, you may use a
# newer version instead, at your option.
#
# This software consists of voluntary contributions made by many
# individuals. For exact contribution history, see the revision
# history and logs, available at http://cvs2svn.tigris.org/.
# ====================================================================
"""This module contains classes to transform symbol names."""
import os
import re
from cvs2svn_lib.log import logger
from cvs2svn_lib.common import FatalError
from cvs2svn_lib.common import IllegalSVNPathError
from cvs2svn_lib.common import normalize_svn_path
from cvs2svn_lib.common import is_branch_revision_number
class SymbolTransform:
"""Transform symbol names arbitrarily."""
def transform(self, cvs_file, symbol_name, revision):
"""Possibly transform SYMBOL_NAME, which was found in CVS_FILE.
Return the transformed symbol name. If this SymbolTransform
doesn't apply, return the original SYMBOL_NAME. If this symbol
should be ignored entirely, return None. (Please note that
ignoring a branch via this mechanism only causes the branch *name*
to be ignored; the branch contents will still be converted.
Usually branches should be excluded using --exclude.)
REVISION contains the CVS revision number to which the symbol was
attached in the file as a string (with zeros removed).
This method is free to use the information in CVS_FILE (including
CVS_FILE.project) to decide whether and/or how to transform
SYMBOL_NAME."""
raise NotImplementedError()
class ReplaceSubstringsSymbolTransform(SymbolTransform):
"""Replace specific substrings in symbol names.
If the substring occurs multiple times, replace all copies."""
def __init__(self, old, new):
self.old = old
self.new = new
def transform(self, cvs_file, symbol_name, revision):
return symbol_name.replace(self.old, self.new)
class NormalizePathsSymbolTransform(SymbolTransform):
def transform(self, cvs_file, symbol_name, revision):
try:
return normalize_svn_path(symbol_name)
except IllegalSVNPathError, e:
raise FatalError('Problem with %s: %s' % (symbol_name, e,))
class CompoundSymbolTransform(SymbolTransform):
"""A SymbolTransform that applies other SymbolTransforms in series.
Each of the contained SymbolTransforms is applied, one after the
other. If any of them returns None, then None is returned (the
following SymbolTransforms are ignored)."""
def __init__(self, symbol_transforms):
"""Ininitialize a CompoundSymbolTransform.
SYMBOL_TRANSFORMS is an iterable of SymbolTransform instances."""
self.symbol_transforms = list(symbol_transforms)
def transform(self, cvs_file, symbol_name, revision):
for symbol_transform in self.symbol_transforms:
symbol_name = symbol_transform.transform(
cvs_file, symbol_name, revision
)
if symbol_name is None:
# Don't continue with other symbol transforms:
break
return symbol_name
class RegexpSymbolTransform(SymbolTransform):
"""Transform symbols by using a regexp textual substitution."""
def __init__(self, pattern, replacement):
"""Create a SymbolTransform that transforms symbols matching PATTERN.
PATTERN is a regular expression that should match the whole symbol
name. REPLACEMENT is the replacement text, which may include
patterns like r'\1' or r'\g<1>' or r'\g<name>' (where 'name' is a
reference to a named substring in the pattern of the form
r'(?P<name>...)')."""
self.pattern = re.compile('^' + pattern + '$')
self.replacement = replacement
def transform(self, cvs_file, symbol_name, revision):
return self.pattern.sub(self.replacement, symbol_name)
class SymbolMapper(SymbolTransform):
"""A SymbolTransform that transforms specific symbol definitions.
The user has to specify the exact CVS filename, symbol name, and
revision number to be transformed, and the new name (or None if the
symbol should be ignored). The mappings can be set via a
constructor argument or by calling __setitem__()."""
def __init__(self, items=[]):
"""Initialize the mapper.
ITEMS is a list of tuples (cvs_filename, symbol_name, revision,
new_name) which will be set as mappings."""
# A map {(cvs_filename, symbol_name, revision) : new_name}:
self._map = {}
for (cvs_filename, symbol_name, revision, new_name) in items:
self[cvs_filename, symbol_name, revision] = new_name
def __setitem__(self, (cvs_filename, symbol_name, revision), new_name):
"""Set a mapping for a particular file, symbol, and revision."""
cvs_filename = os.path.normcase(os.path.normpath(cvs_filename))
key = (cvs_filename, symbol_name, revision)
if key in self._map:
logger.warn(
'Overwriting symbol transform for\n'
' filename=%r symbol=%s revision=%s'
% (cvs_filename, symbol_name, revision,)
)
self._map[key] = new_name
def transform(self, cvs_file, symbol_name, revision):
# cvs_file.rcs_path is guaranteed to already be normalised the way
# os.path.normpath() normalises paths. No need to call it again.
cvs_filename = os.path.normcase(cvs_file.rcs_path)
return self._map.get(
(cvs_filename, symbol_name, revision), symbol_name
)
class SubtreeSymbolMapper(SymbolTransform):
"""A SymbolTransform that transforms symbols within a whole repo subtree.
The user has to specify a CVS repository path (a filename or
directory) and the original symbol name. All symbols under that
path will be renamed to the specified new name (which can be None if
the symbol should be ignored). The mappings can be set via a
constructor argument or by calling __setitem__(). Only the most
specific rule is applied."""
def __init__(self, items=[]):
"""Initialize the mapper.
ITEMS is a list of tuples (cvs_path, symbol_name, new_name)
which will be set as mappings. cvs_path is a string naming a
directory within the CVS repository."""
# A map {symbol_name : {cvs_path : new_name}}:
self._map = {}
for (cvs_path, symbol_name, new_name) in items:
self[cvs_path, symbol_name] = new_name
def __setitem__(self, (cvs_path, symbol_name), new_name):
"""Set a mapping for a particular file and symbol."""
try:
symbol_map = self._map[symbol_name]
except KeyError:
symbol_map = {}
self._map[symbol_name] = symbol_map
cvs_path = os.path.normcase(os.path.normpath(cvs_path))
if cvs_path in symbol_map:
logger.warn(
'Overwriting symbol transform for\n'
' directory=%r symbol=%s'
% (cvs_path, symbol_name,)
)
symbol_map[cvs_path] = new_name
def transform(self, cvs_file, symbol_name, revision):
try:
symbol_map = self._map[symbol_name]
except KeyError:
# No rules for that symbol name
return symbol_name
# cvs_file.rcs_path is guaranteed to already be normalised the way
# os.path.normpath() normalises paths. No need to call it again.
cvs_path = os.path.normcase(cvs_file.rcs_path)
while True:
try:
return symbol_map[cvs_path]
except KeyError:
new_cvs_path = os.path.dirname(cvs_path)
if new_cvs_path == cvs_path:
# No rules found for that path; return symbol name unaltered.
return symbol_name
else:
cvs_path = new_cvs_path
class IgnoreSymbolTransform(SymbolTransform):
"""Ignore symbols matching a specified regular expression."""
def __init__(self, pattern):
"""Create an SymbolTransform that ignores symbols matching PATTERN.
PATTERN is a regular expression that should match the whole symbol
name."""
self.pattern = re.compile('^' + pattern + '$')
def transform(self, cvs_file, symbol_name, revision):
if self.pattern.match(symbol_name):
return None
else:
return symbol_name
class SubtreeSymbolTransform(SymbolTransform):
"""A wrapper around another SymbolTransform, that limits it to a
specified subtree."""
def __init__(self, cvs_path, inner_symbol_transform):
"""Constructor.
CVS_PATH is the path in the repository. INNER_SYMBOL_TRANSFORM is
the SymbolTransform to wrap."""
assert isinstance(cvs_path, str)
self.__subtree = os.path.normcase(os.path.normpath(cvs_path))
self.__subtree_len = len(self.__subtree)
self.__inner = inner_symbol_transform
def __does_rule_apply_to(self, cvs_file):
#
# NOTE: This turns out to be a hot path through the code.
#
# It used to use logic similar to SubtreeSymbolMapper.transform(). And
# it used to take 44% of cvs2svn's total runtime on one real-world test.
# Now it's been optimized, it takes about 2%.
#
# This is called about:
# (num_files * num_symbols_per_file * num_subtree_symbol_transforms)
# times. On a large repository with several of these transforms,
# that can exceed 100,000,000 calls.
#
# cvs_file.rcs_path is guaranteed to already be normalised the way
# os.path.normpath() normalises paths. So we don't need to call
# os.path.normpath() again. (The os.path.normpath() function does
# quite a lot, so it's expensive).
#
# os.path.normcase is a no-op on POSIX systems (and therefore fast).
# Even on Windows it's only a memory allocation and case-change, it
# should be quite fast.
cvs_path = os.path.normcase(cvs_file.rcs_path)
# Do most of the work in a single call, without allocating memory.
if not cvs_path.startswith(self.__subtree):
# Different prefix.
# This is the common "no match" case.
return False
if len(cvs_path) == self.__subtree_len:
# Exact match.
#
# This is quite rare, as self.__subtree is usually a directory and
# cvs_path is always a file.
return True
# We know cvs_path starts with self.__subtree, check the next character
# is a '/' (or if we're on Windows, a '\\'). If so, then cvs_path is a
# file under the self.__subtree directory tree, so we match. If not,
# then it's not a match.
return cvs_path[self.__subtree_len] == os.path.sep
def transform(self, cvs_file, symbol_name, revision):
if self.__does_rule_apply_to(cvs_file):
return self.__inner.transform(cvs_file, symbol_name, revision)
else:
# Rule does not apply to that path; return symbol name unaltered.
return symbol_name
class TagOnlyTransform(SymbolTransform):
"""A wrapper around another SymbolTransform, that limits it to
CVS tags (not CVS branches)."""
def __init__(self, inner_symbol_transform):
"""Constructor.
INNER_SYMBOL_TRANSFORM is the SymbolTransform to wrap."""
self.__inner = inner_symbol_transform
def transform(self, cvs_file, symbol_name, revision):
if is_branch_revision_number(revision):
# It's a branch
return symbol_name
else:
# It's a tag
return self.__inner.transform(cvs_file, symbol_name, revision)
class BranchOnlyTransform(SymbolTransform):
"""A wrapper around another SymbolTransform, that limits it to
CVS branches (not CVS tags)."""
def __init__(self, inner_symbol_transform):
"""Constructor.
INNER_SYMBOL_TRANSFORM is the SymbolTransform to wrap."""
self.__inner = inner_symbol_transform
def transform(self, cvs_file, symbol_name, revision):
if is_branch_revision_number(revision):
# It's a branch
return self.__inner.transform(cvs_file, symbol_name, revision)
else:
# It's a tag
return symbol_name
|