13174.1.1
by Martin Pool
Unify implementations of save-mail-to-librarian; both use uuids for file names |
1 |
# Copyright 2009-2011 Canonical Ltd. This software is licensed under the
|
8687.15.18
by Karl Fogel
Add the copyright header block to files under lib/canonical/. |
2 |
# GNU Affero General Public License version 3 (see the file LICENSE).
|
3 |
||
1955
by Canonical.com Patch Queue Manager
menus. [r=spiv] |
4 |
"""Various functions and classes that are useful across different parts of
|
5 |
launchpad.
|
|
6 |
||
7 |
Do not simply dump stuff in here. Think carefully as to whether it would
|
|
8 |
be better as a method on an existing content object or IFooSet object.
|
|
9 |
"""
|
|
1509
by Canonical.com Patch Queue Manager
make the bugtask listing team aware |
10 |
|
11 |
__metaclass__ = type |
|
12 |
||
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
13 |
from difflib import unified_diff |
1585
by Canonical.com Patch Queue Manager
Purging canonical/auth/. ubuntulinux.org forgottenpassword's page now redirects to launchpad. |
14 |
import re |
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
15 |
from StringIO import StringIO |
10293.1.1
by Barry Warsaw, Max Bowsher
Switch from using sha and md5 to hashlib. Also use hashlib.sha256 instead of |
16 |
import subprocess |
1513
by Canonical.com Patch Queue Manager
Merged LaunchpadPackagePoAttach spec implementation needed for Hoary Translations |
17 |
import tarfile |
1658
by Canonical.com Patch Queue Manager
Adding shortlist() to helpers.py and use it on some IPerson/IPersonSet methods. r=SteveA |
18 |
import warnings |
10293.1.1
by Barry Warsaw, Max Bowsher
Switch from using sha and md5 to hashlib. Also use hashlib.sha256 instead of |
19 |
|
7876.3.19
by Francis J. Lacoste
ForbiddenAttribute error, not TypeError is raised for missing __getslice__ on security proxy object. |
20 |
from zope.security.interfaces import ForbiddenAttribute |
7876.3.2
by Francis J. Lacoste
Use shortlist from lazr.Elifecycle. |
21 |
|
11532.7.5
by Curtis Hovey
Fixed test_doc harness. |
22 |
|
1922
by Canonical.com Patch Queue Manager
added general-purpose text-replacement function, and simplified various functions in helpers.py [trivial] |
23 |
def text_replaced(text, replacements, _cache={}): |
24 |
"""Return a new string with text replaced according to the dict provided.
|
|
25 |
||
11532.7.6
by Curtis Hovey
Hushed lint. |
26 |
The keys of the dict are substrings to find, the values are what to
|
27 |
replace found substrings with.
|
|
1922
by Canonical.com Patch Queue Manager
added general-purpose text-replacement function, and simplified various functions in helpers.py [trivial] |
28 |
|
2972.1.11
by Carlos Perelló Marín
Applied review comments |
29 |
:arg text: An unicode or str to do the replacement.
|
30 |
:arg replacements: A dictionary with the replacements that should be done
|
|
31 |
||
1922
by Canonical.com Patch Queue Manager
added general-purpose text-replacement function, and simplified various functions in helpers.py [trivial] |
32 |
>>> text_replaced('', {'a':'b'})
|
33 |
''
|
|
34 |
>>> text_replaced('a', {'a':'c'})
|
|
35 |
'c'
|
|
36 |
>>> text_replaced('faa bar baz', {'a': 'A', 'aa': 'X'})
|
|
37 |
'fX bAr bAz'
|
|
38 |
>>> text_replaced('1 2 3 4', {'1': '2', '2': '1'})
|
|
39 |
'2 1 3 4'
|
|
40 |
||
2972.1.11
by Carlos Perelló Marín
Applied review comments |
41 |
Unicode strings work too.
|
42 |
||
43 |
>>> text_replaced(u'1 2 3 4', {u'1': u'2', u'2': u'1'})
|
|
44 |
u'2 1 3 4'
|
|
45 |
||
1922
by Canonical.com Patch Queue Manager
added general-purpose text-replacement function, and simplified various functions in helpers.py [trivial] |
46 |
The argument _cache is used as a cache of replacements that were requested
|
47 |
before, so we only compute regular expressions once.
|
|
48 |
||
49 |
"""
|
|
50 |
assert replacements, "The replacements dict must not be empty." |
|
51 |
# The ordering of keys and values in the tuple will be consistent within a
|
|
52 |
# single Python process.
|
|
53 |
cachekey = tuple(replacements.items()) |
|
54 |
if cachekey not in _cache: |
|
55 |
L = [] |
|
2972.1.11
by Carlos Perelló Marín
Applied review comments |
56 |
if isinstance(text, unicode): |
57 |
list_item = u'(%s)' |
|
58 |
join_char = u'|' |
|
59 |
else: |
|
60 |
list_item = '(%s)' |
|
61 |
join_char = '|' |
|
1922
by Canonical.com Patch Queue Manager
added general-purpose text-replacement function, and simplified various functions in helpers.py [trivial] |
62 |
for find, replace in sorted(replacements.items(), |
63 |
key=lambda (key, value): len(key), |
|
64 |
reverse=True): |
|
2972.1.11
by Carlos Perelló Marín
Applied review comments |
65 |
L.append(list_item % re.escape(find)) |
1922
by Canonical.com Patch Queue Manager
added general-purpose text-replacement function, and simplified various functions in helpers.py [trivial] |
66 |
# Make a copy of the replacements dict, as it is mutable, but we're
|
67 |
# keeping a cached reference to it.
|
|
68 |
replacements_copy = dict(replacements) |
|
11532.7.6
by Curtis Hovey
Hushed lint. |
69 |
|
1922
by Canonical.com Patch Queue Manager
added general-purpose text-replacement function, and simplified various functions in helpers.py [trivial] |
70 |
def matchobj_replacer(matchobj): |
71 |
return replacements_copy[matchobj.group()] |
|
11532.7.6
by Curtis Hovey
Hushed lint. |
72 |
|
2972.1.11
by Carlos Perelló Marín
Applied review comments |
73 |
regexsub = re.compile(join_char.join(L)).sub |
11532.7.6
by Curtis Hovey
Hushed lint. |
74 |
|
1922
by Canonical.com Patch Queue Manager
added general-purpose text-replacement function, and simplified various functions in helpers.py [trivial] |
75 |
def replacer(s): |
76 |
return regexsub(matchobj_replacer, s) |
|
11532.7.6
by Curtis Hovey
Hushed lint. |
77 |
|
1922
by Canonical.com Patch Queue Manager
added general-purpose text-replacement function, and simplified various functions in helpers.py [trivial] |
78 |
_cache[cachekey] = replacer |
79 |
return _cache[cachekey](text) |
|
80 |
||
2900.2.15
by Matthew Paul Thomas
remove remaining non-Ascii characters from pagetests using backslashreplace() |
81 |
|
82 |
def backslashreplace(str): |
|
83 |
"""Return a copy of the string, with non-ASCII characters rendered as
|
|
84 |
xNN or uNNNN. Used to test data containing typographical quotes etc.
|
|
85 |
"""
|
|
86 |
return str.decode('UTF-8').encode('ASCII', 'backslashreplace') |
|
87 |
||
88 |
||
1513
by Canonical.com Patch Queue Manager
Merged LaunchpadPackagePoAttach spec implementation needed for Hoary Translations |
89 |
def string_to_tarfile(s): |
90 |
"""Convert a binary string containing a tar file into a tar file obj."""
|
|
91 |
||
92 |
return tarfile.open('', 'r', StringIO(s)) |
|
93 |
||
94 |
||
6374.15.13
by Barry Warsaw
mergeRF |
95 |
def simple_popen2(command, input, env=None, in_bufsize=1024, out_bufsize=128): |
1635
by Canonical.com Patch Queue Manager
RosettaOptimization! |
96 |
"""Run a command, give it input on its standard input, and capture its
|
97 |
standard output.
|
|
98 |
||
99 |
Returns the data from standard output.
|
|
100 |
||
101 |
This function is needed to avoid certain deadlock situations. For example,
|
|
102 |
if you popen2() a command, write its standard input, then read its
|
|
103 |
standard output, this can deadlock due to the parent process blocking on
|
|
104 |
writing to the child, while the child process is simultaneously blocking
|
|
1716.5.60
by kiko
Fix LIKE/ILIKE queries that were incorrectly using quote() instead of quote_like. Also fix uses of urljoin that should have been urlappends. Delintifies in places |
105 |
on writing to its parent. This function avoids that problem by using
|
106 |
subprocess.Popen.communicate().
|
|
1635
by Canonical.com Patch Queue Manager
RosettaOptimization! |
107 |
"""
|
108 |
||
1681.1.177
by Stuart Bishop
Make popen2 helper use subprocess module for great justice |
109 |
p = subprocess.Popen( |
6374.15.13
by Barry Warsaw
mergeRF |
110 |
command, env=env, stdin=subprocess.PIPE, |
11532.7.6
by Curtis Hovey
Hushed lint. |
111 |
stdout=subprocess.PIPE, stderr=subprocess.STDOUT) |
1681.1.177
by Stuart Bishop
Make popen2 helper use subprocess module for great justice |
112 |
(output, nothing) = p.communicate(input) |
1635
by Canonical.com Patch Queue Manager
RosettaOptimization! |
113 |
return output |
114 |
||
2938.2.10
by Brad Bollenbach
response to code review |
115 |
|
7876.3.9
by Francis J. Lacoste
Merged back fixes to shortlist. |
116 |
class ShortListTooBigError(Exception): |
117 |
"""This error is raised when the shortlist hardlimit is reached"""
|
|
118 |
||
119 |
||
120 |
def shortlist(sequence, longest_expected=15, hardlimit=None): |
|
121 |
"""Return a listified version of sequence.
|
|
122 |
||
123 |
If <sequence> has more than <longest_expected> items, a warning is issued.
|
|
124 |
||
125 |
>>> shortlist([1, 2])
|
|
126 |
[1, 2]
|
|
127 |
||
7876.3.14
by Francis J. Lacoste
Review comments. |
128 |
>>> shortlist([1, 2, 3], 2) #doctest: +NORMALIZE_WHITESPACE
|
7876.3.9
by Francis J. Lacoste
Merged back fixes to shortlist. |
129 |
Traceback (most recent call last):
|
130 |
...
|
|
7876.3.14
by Francis J. Lacoste
Review comments. |
131 |
UserWarning: shortlist() should not be used here. It's meant to listify
|
132 |
sequences with no more than 2 items. There were 3 items.
|
|
7876.3.9
by Francis J. Lacoste
Merged back fixes to shortlist. |
133 |
|
134 |
>>> shortlist([1, 2, 3, 4], hardlimit=2)
|
|
135 |
Traceback (most recent call last):
|
|
136 |
...
|
|
137 |
ShortListTooBigError: Hard limit of 2 exceeded.
|
|
138 |
||
7876.3.14
by Francis J. Lacoste
Review comments. |
139 |
>>> shortlist(
|
140 |
... [1, 2, 3, 4], 2, hardlimit=4) #doctest: +NORMALIZE_WHITESPACE
|
|
141 |
Traceback (most recent call last):
|
|
142 |
...
|
|
143 |
UserWarning: shortlist() should not be used here. It's meant to listify
|
|
144 |
sequences with no more than 2 items. There were 4 items.
|
|
145 |
||
146 |
It works on iterable also which don't support the extended slice protocol.
|
|
147 |
||
148 |
>>> xrange(5)[:1] #doctest: +ELLIPSIS
|
|
149 |
Traceback (most recent call last):
|
|
150 |
...
|
|
151 |
TypeError: ...
|
|
152 |
||
153 |
>>> shortlist(xrange(10), 5, hardlimit=8) #doctest: +ELLIPSIS
|
|
7876.3.9
by Francis J. Lacoste
Merged back fixes to shortlist. |
154 |
Traceback (most recent call last):
|
155 |
...
|
|
156 |
ShortListTooBigError: ...
|
|
157 |
||
158 |
"""
|
|
159 |
if hardlimit is not None: |
|
160 |
last = hardlimit + 1 |
|
161 |
else: |
|
7943.1.1
by Francis J. Lacoste
Don't crop results when no hardlimit is requested. |
162 |
last = None |
7876.3.14
by Francis J. Lacoste
Review comments. |
163 |
try: |
7876.3.9
by Francis J. Lacoste
Merged back fixes to shortlist. |
164 |
results = list(sequence[:last]) |
7876.3.19
by Francis J. Lacoste
ForbiddenAttribute error, not TypeError is raised for missing __getslice__ on security proxy object. |
165 |
except (TypeError, ForbiddenAttribute): |
7876.3.9
by Francis J. Lacoste
Merged back fixes to shortlist. |
166 |
results = [] |
167 |
for idx, item in enumerate(sequence): |
|
168 |
if hardlimit and idx > hardlimit: |
|
169 |
break
|
|
170 |
results.append(item) |
|
171 |
||
172 |
size = len(results) |
|
173 |
if hardlimit and size > hardlimit: |
|
174 |
raise ShortListTooBigError( |
|
175 |
'Hard limit of %d exceeded.' % hardlimit) |
|
176 |
elif size > longest_expected: |
|
177 |
warnings.warn( |
|
178 |
"shortlist() should not be used here. It's meant to listify"
|
|
179 |
" sequences with no more than %d items. There were %s items." |
|
180 |
% (longest_expected, size), stacklevel=2) |
|
181 |
return results |
|
182 |
||
183 |
||
1646
by Canonical.com Patch Queue Manager
Rosetta source code follows now the Launchpad standard layout rs=SteveA |
184 |
def is_tar_filename(filename): |
185 |
'''
|
|
186 |
Check whether a filename looks like a filename that belongs to a tar file,
|
|
187 |
possibly one compressed somehow.
|
|
188 |
'''
|
|
189 |
||
190 |
return (filename.endswith('.tar') or |
|
191 |
filename.endswith('.tar.gz') or |
|
2570.1.7
by Carlos Perello Marin
Lots of fixes + new code + tests updates |
192 |
filename.endswith('.tgz') or |
1646
by Canonical.com Patch Queue Manager
Rosetta source code follows now the Launchpad standard layout rs=SteveA |
193 |
filename.endswith('.tar.bz2')) |
194 |
||
1715
by Canonical.com Patch Queue Manager
Moved the translation form from potemplate context into pofile context. r=SteveA |
195 |
|
1702
by Canonical.com Patch Queue Manager
PO attach functional test, plus miscellaneous fixes (r=Steve) |
196 |
def test_diff(lines_a, lines_b): |
197 |
"""Generate a string indicating the difference between expected and actual
|
|
198 |
values in a test.
|
|
199 |
"""
|
|
200 |
||
201 |
return '\n'.join(list(unified_diff( |
|
202 |
a=lines_a, |
|
203 |
b=lines_b, |
|
204 |
fromfile='expected', |
|
205 |
tofile='actual', |
|
206 |
lineterm='', |
|
207 |
)))
|
|
208 |
||
2938.2.10
by Brad Bollenbach
response to code review |
209 |
|
1881
by Canonical.com Patch Queue Manager
Comments for 'gpghandler' and 'zeca' configuration sections |
210 |
def filenameToContentType(fname): |
2499
by Canonical.com Patch Queue Manager
[r=kiko] make malone admin aware, |
211 |
""" Return the a ContentType-like entry for arbitrary filenames
|
1881
by Canonical.com Patch Queue Manager
Comments for 'gpghandler' and 'zeca' configuration sections |
212 |
|
213 |
deb files
|
|
214 |
||
215 |
>>> filenameToContentType('test.deb')
|
|
216 |
'application/x-debian-package'
|
|
217 |
||
218 |
text files
|
|
219 |
||
220 |
>>> filenameToContentType('test.txt')
|
|
221 |
'text/plain'
|
|
222 |
||
223 |
Not recognized format
|
|
2499
by Canonical.com Patch Queue Manager
[r=kiko] make malone admin aware, |
224 |
|
1881
by Canonical.com Patch Queue Manager
Comments for 'gpghandler' and 'zeca' configuration sections |
225 |
>>> filenameToContentType('test.tgz')
|
226 |
'application/octet-stream'
|
|
227 |
"""
|
|
11532.7.6
by Curtis Hovey
Hushed lint. |
228 |
ftmap = {".dsc": "text/plain", |
229 |
".changes": "text/plain", |
|
230 |
".deb": "application/x-debian-package", |
|
231 |
".udeb": "application/x-debian-package", |
|
232 |
".txt": "text/plain", |
|
233 |
# For the build master logs
|
|
234 |
".txt.gz": "text/plain", |
|
1881
by Canonical.com Patch Queue Manager
Comments for 'gpghandler' and 'zeca' configuration sections |
235 |
}
|
236 |
for ending in ftmap: |
|
237 |
if fname.endswith(ending): |
|
238 |
return ftmap[ending] |
|
239 |
return "application/octet-stream" |
|
240 |
||
1872
by Canonical.com Patch Queue Manager
First cut of the email interface! Refactorings to SQLObjectFooEvents. First cut of banzai deeath scene. Fix bug 931. Add bug url declarations. And some more... r=stevea |
241 |
|
2372
by Canonical.com Patch Queue Manager
ShipItNG: Allow people to place new orders and shipit admins to search for existing requests and approve, cancel or change them. It's also possible to create new standard shipit requests, which are the options presented to users when they place new orders. r=kiko |
242 |
def intOrZero(value): |
2851.2.4
by Guilherme Salgado
Some small fixes Steve suggested. |
243 |
"""Return int(value) or 0 if the conversion fails.
|
2972.1.11
by Carlos Perelló Marín
Applied review comments |
244 |
|
2851.2.4
by Guilherme Salgado
Some small fixes Steve suggested. |
245 |
>>> intOrZero('1.23')
|
246 |
0
|
|
247 |
>>> intOrZero('1.ab')
|
|
248 |
0
|
|
249 |
>>> intOrZero('2')
|
|
250 |
2
|
|
251 |
>>> intOrZero(None)
|
|
252 |
0
|
|
253 |
>>> intOrZero(1)
|
|
254 |
1
|
|
2851.2.6
by Guilherme Salgado
Some extra tests for intOrZero() and positiveIntOrZero(), as kiko suggested. |
255 |
>>> intOrZero(-9)
|
256 |
-9
|
|
2851.2.4
by Guilherme Salgado
Some small fixes Steve suggested. |
257 |
"""
|
2372
by Canonical.com Patch Queue Manager
ShipItNG: Allow people to place new orders and shipit admins to search for existing requests and approve, cancel or change them. It's also possible to create new standard shipit requests, which are the options presented to users when they place new orders. r=kiko |
258 |
try: |
259 |
return int(value) |
|
2851.2.1
by Guilherme Salgado
ShipIt Reports, take 1 |
260 |
except (ValueError, TypeError): |
2372
by Canonical.com Patch Queue Manager
ShipItNG: Allow people to place new orders and shipit admins to search for existing requests and approve, cancel or change them. It's also possible to create new standard shipit requests, which are the options presented to users when they place new orders. r=kiko |
261 |
return 0 |
262 |
||
2938.2.10
by Brad Bollenbach
response to code review |
263 |
|
4199.1.5
by Jonathan Lange
Apply statik's review comments |
264 |
def truncate_text(text, max_length): |
4199.1.3
by Jonathan Lange
truncate_text helper function and use constants in BranchView, rather than |
265 |
"""Return a version of string no longer than max_length characters.
|
266 |
||
267 |
Tries not to cut off the text mid-word.
|
|
268 |
"""
|
|
4199.1.5
by Jonathan Lange
Apply statik's review comments |
269 |
words = re.compile(r'\s*\S+').findall(text, 0, max_length + 1) |
4199.1.3
by Jonathan Lange
truncate_text helper function and use constants in BranchView, rather than |
270 |
truncated = words[0] |
271 |
for word in words[1:]: |
|
272 |
if len(truncated) + len(word) > max_length: |
|
273 |
break
|
|
274 |
truncated += word |
|
275 |
return truncated[:max_length] |
|
5796.13.8
by Gavin Panella
New helper function, english_list. |
276 |
|
277 |
||
5796.13.15
by Gavin Panella
Use the term 'conjunction', and improve docs of english_list. |
278 |
def english_list(items, conjunction='and'): |
5796.13.8
by Gavin Panella
New helper function, english_list. |
279 |
"""Return all the items concatenated into a English-style string.
|
280 |
||
6484.9.19
by Gavin Panella
Correct chapter for Strunk & White. |
281 |
Follows the advice given in The Elements of Style, chapter I,
|
5796.13.15
by Gavin Panella
Use the term 'conjunction', and improve docs of english_list. |
282 |
section 2:
|
5796.13.8
by Gavin Panella
New helper function, english_list. |
283 |
|
5796.13.15
by Gavin Panella
Use the term 'conjunction', and improve docs of english_list. |
284 |
"In a series of three or more terms with a single conjunction, use
|
285 |
a comma after each term except the last."
|
|
6607.7.5
by Julian Edwards
allenap comments |
286 |
|
287 |
Beware that this is US English and is wrong for non-US.
|
|
5796.13.8
by Gavin Panella
New helper function, english_list. |
288 |
"""
|
289 |
items = list(items) |
|
290 |
if len(items) <= 2: |
|
5796.13.15
by Gavin Panella
Use the term 'conjunction', and improve docs of english_list. |
291 |
return (' %s ' % conjunction).join(items) |
5796.13.8
by Gavin Panella
New helper function, english_list. |
292 |
else: |
5796.13.15
by Gavin Panella
Use the term 'conjunction', and improve docs of english_list. |
293 |
items[-1] = '%s %s' % (conjunction, items[-1]) |
5796.13.8
by Gavin Panella
New helper function, english_list. |
294 |
return ', '.join(items) |
7675.166.315
by Stuart Bishop
Be more careful about casting to Unicode |
295 |
|
296 |
||
297 |
def ensure_unicode(string): |
|
7675.166.318
by Stuart Bishop
ensure_unicode should handle None |
298 |
r"""Return input as unicode. None is passed through unharmed. |
7675.166.315
by Stuart Bishop
Be more careful about casting to Unicode |
299 |
|
300 |
Do not use this method. This method exists only to help migration
|
|
301 |
of legacy code where str objects were being passed into contexts
|
|
302 |
where unicode objects are required. All invokations of
|
|
303 |
ensure_unicode() should eventually be removed.
|
|
304 |
||
305 |
This differs from the builtin unicode() function, as a TypeError
|
|
306 |
exception will be raised if the parameter is not a basestring or if
|
|
307 |
a raw string is not ASCII.
|
|
308 |
||
309 |
>>> ensure_unicode(u'hello')
|
|
310 |
u'hello'
|
|
311 |
||
312 |
>>> ensure_unicode('hello')
|
|
313 |
u'hello'
|
|
314 |
||
315 |
>>> ensure_unicode(u'A'.encode('utf-16')) # Not ASCII
|
|
316 |
Traceback (most recent call last):
|
|
317 |
...
|
|
318 |
TypeError: '\xff\xfeA\x00' is not US-ASCII
|
|
319 |
||
320 |
>>> ensure_unicode(42)
|
|
321 |
Traceback (most recent call last):
|
|
322 |
...
|
|
323 |
TypeError: 42 is not a basestring (<type 'int'>)
|
|
7675.166.318
by Stuart Bishop
ensure_unicode should handle None |
324 |
|
325 |
>>> ensure_unicode(None) is None
|
|
326 |
True
|
|
7675.166.315
by Stuart Bishop
Be more careful about casting to Unicode |
327 |
"""
|
7675.166.318
by Stuart Bishop
ensure_unicode should handle None |
328 |
if string is None: |
329 |
return None |
|
330 |
elif isinstance(string, unicode): |
|
7675.166.315
by Stuart Bishop
Be more careful about casting to Unicode |
331 |
return string |
332 |
elif isinstance(string, basestring): |
|
333 |
try: |
|
334 |
return string.decode('US-ASCII') |
|
335 |
except UnicodeDecodeError: |
|
336 |
raise TypeError("%s is not US-ASCII" % repr(string)) |
|
337 |
else: |
|
338 |
raise TypeError( |
|
339 |
"%r is not a basestring (%r)" % (string, type(string))) |