-
Notifications
You must be signed in to change notification settings - Fork 0
/
translator.py
305 lines (275 loc) · 11.6 KB
/
translator.py
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
# Perforce Defect Tracking Integration Project
# <http://www.ravenbrook.com/project/p4dti/>
#
# TRANSLATOR.PY -- TRANSLATE FIELDS BETWEEN DEFECT TRACKERS
#
# Gareth Rees, Ravenbrook Limited, 2001-03-13
#
#
# 1. INTRODUCTION
#
# This module defines the translator class. An instance of a translator
# class translates between corresponding fields in two defect trackers.
# For example, we could make a state translator that translates between
# an issue state in TeamTrack and a job state in Perforce.
#
# The translator class is generic: it doesn't know anything about the
# identities of the defect tracker it translates between, so it calls
# them "defect tracker 0" and "defect tracker 1". In the P4DTI, defect
# tracker 1 is conventionally Perforce.
#
# Each kind of field will need its own translator subclass; for example,
# in the TeamTrack integration we have date_translator,
# single_select_translator, state_translator, text_translator,
# user_translator and so on. When the P4DTI starts up, the
# configuration generator makes instances of each translator subclass,
# passing additional data where necessary. For example, the
# single_select_translator needs to know the name of the TeamTrack field
# it is translating, so that it can pick out the selections that apply
# to that field.
#
# The aim of the translator class is to make the replicator implentation
# cleaner and easier to maintain and understand. Rather than having a
# big switch statement, or a fixed set of field types, the replicator
# instead has a list of (field in defect tracker 0, field in defect
# tracker 1, translator) triples which it can iterate over.
#
# I wish I could think of better names than translate_0_to_1 and
# translate_1_to_0 for the translation methods.
#
# The intended readership of this document is project developers.
#
# This document is not confidential.
import dt_interface
import re
import string
# 2. TRANSLATOR CLASS
#
# This is an abstract class that defines the interface for all
# translators.
#
# The translator class itself implements the null translator. It
# doesn't change values, but its methods do check the types of its
# arguments.
#
# See [GDR 2000-10-16, 6.5] for more documentation.
class translator:
# 2.1. TRANSLATE FROM DEFECT TRACKER 0 TO DEFECT TRACKER 1
#
# Translate a value from defect tracker 0 to defect tracker 1.
# Arguments:
#
# value The value (in defect tracker 0) being translated.
# dt0 Defect tracker 0.
# dt1 Defect tracker 1.
# issue0 The issue in defect tracker 0 from which the value comes,
# or None if the value doesn't come from an issue.
# issue1 The issue in defect tracker 0 from which the field comes,
# or None if the value doesn't come from an issue.
#
# Returns a suitable translation of value for defect tracker 1, or
# raises an error if translation is impossible.
#
# This method takes defect trackers as arguments because it may need
# to query the defect tracker to carry out the translation. For
# example, in the TeamTrack integration the user translator needs to
# discover all the users in TeamTrack and Perforce so that it can
# match them up by e-mail address. A translator that does not need
# these arguments may specify defaults for them and be used without
# them (for example, the keyword translator in section 3 below does
# this so that it can be used during configuration before any defect
# tracker objects have been constructed).
#
# This method takes issues as arguments because some translators
# need to know about the whole issue in order to carry out the
# translation. For example, in the TeamTrack integration the state
# translator needs to know the project to which the issue belongs
# (because different projects may have different states with the
# same name which correspond to the same Perforce state). Most
# translators will ignore the issue arguments.
def translate_0_to_1(self, value, dt0, dt1, issue0 = None,
issue1 = None):
assert isinstance(dt0, dt_interface.defect_tracker)
assert isinstance(dt1, dt_interface.defect_tracker)
assert (issue0 == None
or isinstance(issue0, dt_interface.defect_tracker_issue))
# The issue1 argument is conventionally the Perforce job.
# Unfortunately, Perforce jobs are just ordinary dictionaries,
# and don't belong to a subclass of defect_tracker_issue, so we
# can't check the type of the issue1 argument. This should be
# corrected.
#assert (issue1 == None
# or isinstance(issue1, dt_interface.defect_tracker_issue))
return value
# 2.2. TRANSLATE FROM DEFECT TRACKER 1 TO DEFECT TRACKER 0
#
# Translate a value from defect tracker 0 to defect tracker 1.
# Arguments:
#
# value The value (in defect tracker 1) being translated.
# ... (other arguments are the same as translate_0_to_1)
#
# Returns a suitable translation of value for defect tracker 0, or
# raises an error if translation is impossible.
#
# The notes for translate_0_to_1() in section 2.1 also apply to this
# method.
def translate_1_to_0(self, value, dt0, dt1, issue0 = None,
issue1 = None):
assert isinstance(dt0, dt_interface.defect_tracker)
assert isinstance(dt1, dt_interface.defect_tracker)
assert (issue0 == None
or isinstance(issue0, dt_interface.defect_tracker_issue))
# See comment in translate_0_to_1 above.
#assert (issue1 == None
# or isinstance(issue1, dt_interface.defect_tracker_issue))
return value
# 3. KEYWORD TRANSLATOR CLASS
#
# This class translates "keywords" between any defect tracker and
# Perforce. By "keywords" I mean job field names in Perforce, and
# values in "select" fields in Perforce jobs.
#
# Defect tracker keywords can (in general) contain whitespace and
# punctuation characters.
#
# But Perforce job field names can't contain whitespace, hashes, or
# double quotes. Values in "select" fields in Perforce jobspecs also
# can't contain semicolons or slashes.
#
# The translation must be one-to-one so that values in "select" fields
# in Perforce can be accurately translated back to the defect tracker.
#
# We use the following translation:
#
# DT Perforce Description
# ---------------------------------
# _ (space to underscore)
# \ \\ (we use backslash to escape so it must escape itself)
# _ \_ (underscore to backslash underscore)
# ; \: (semicolon to backslash colon)
# # \= (hash to backslash equals)
# / \| (slash to backslash bar)
# " \' (double quote to backslash apostrophe)
# c \xab (where c is some other whitespace character and ab is
# its hex representation).
#
# See job000195 for the motivation behind this design.
class keyword_translator(translator):
# 3.1. Fixed translations
#
# specials is a list of pairs (defect tracker string, Perforce
# string). dt_to_p4 is a map from defect tracker string to Perforce
# string. p4_to_dt is a map from Perforce string to defect tracker
# string.
#
# 'dt_to_p4' and 'p4_to_dt' are generated from specials when an
# instance is created.
specials = [(' ', '_'),
('_', '\\_'),
('\\', '\\\\'),
(';', '\\:'),
('/', '\\|'),
('#', '\\='),
('"', "\\'"),
]
dt_to_p4 = {}
p4_to_dt = {}
def __init__(self):
for (dt,p4) in self.specials:
self.dt_to_p4[dt] = p4
self.p4_to_dt[p4] = dt
# 3.2. Translate a matched single character to an escape sequence
def char_to_p4(self, match):
if self.dt_to_p4.has_key(match.group(0)):
return self.dt_to_p4[match.group(0)]
else:
return '\\x%02x' % ord(match.group(0))
# 3.3. Translate a matched escape sequence to a single character
def p4_to_char(self, match):
if self.p4_to_dt.has_key(match.group(0)):
return self.p4_to_dt[match.group(0)]
else:
return chr(string.atoi(match.group(1)[2:], 0x10))
# 3.4. Translate a keyword from the defect tracker to Perforce
#
# This method ignores its arguments dt0 and dt1 so that it can be
# called during confguration generation, before any defect tracker
# objects have been constructed. See configure_bugzilla.py.
def translate_0_to_1(self, s, dt0 = None, dt1 = None, issue0 = None,
issue1 = None):
return re.sub('[\\s_;/#"\\\\]', self.char_to_p4, s)
# 3.5. Translate a keyword from Perforce to the defect tracker.
#
# See the comment for translate_0_to_1() in section 3.4 above.
def translate_1_to_0(self, s, dt0 = None, dt1 = None, issue0 = None,
issue1 = None):
return re.sub("_|\\\\([_\\\\:|=']|x[0-9a-f]{2})",
self.p4_to_char, s)
# 4. USER TRANSLATOR CLASS
#
# A user translator is a translator between users in two defect
# trackers, but it implements the additional method unmatched_users.
#
# This class is the abstract base class for all user translators.
#
# See [GDR 2000-10-16, 7.5.3] for documentation.
class user_translator(translator):
pass
# A. REFERENCES
#
# [GDR 2000-10-16] "Perforce Defect Tracking Integration Integrator's
# Guide"; Gareth Rees; Ravenbrook Limited; 2000-10-16;
# <http://www.ravenbrook.com/project/p4dti/version/2.4/manual/ig/>.
#
#
# B. DOCUMENT HISTORY
#
# 2001-02-21 GDR Created (as keyword.py).
#
# 2001-03-02 RB Transferred copyright to Perforce under their license.
#
# 2001-03-13 GDR Renamed as translator.py. Formatted as a document.
# Included translator interface (from replicator.py). Moved unit test
# for keyword_translator to test/ directory.
#
# 2001-03-19 GDR Added user_translator class.
#
# 2001-03-21 GDR Corrected description of unmatched_users return value.
# Added references to IG.
#
# 2003-05-21 NB Fixed some broken comments.
#
#
# C. COPYRIGHT AND LICENSE
#
# This file is copyright (c) 2001 Perforce Software, Inc. All rights
# reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
# DAMAGE.
#
#
# $Id: //info.ravenbrook.com/project/p4dti/version/2.4/code/replicator/translator.py#2 $