94
95
return str.decode('UTF-8').encode('ASCII', 'backslashreplace')
98
def join_lines(*lines):
99
"""Concatenate a list of strings, adding a newline at the end of each."""
101
return ''.join([x + '\n' for x in lines])
97
104
def string_to_tarfile(s):
98
105
"""Convert a binary string containing a tar file into a tar file obj."""
100
107
return tarfile.open('', 'r', StringIO(s))
110
def shortest(sequence):
111
"""Return a list with the shortest items in sequence.
113
Return an empty list if the sequence is empty.
116
shortest_length = None
118
for item in list(sequence):
119
new_length = len(item)
121
if shortest_length is None:
123
shortest_list.append(item)
124
shortest_length = new_length
125
elif new_length == shortest_length:
126
# Same length than shortest item found, we append it to the list.
127
shortest_list.append(item)
128
elif min(new_length, shortest_length) != shortest_length:
129
# Shorter than our shortest length found, discard old values.
130
shortest_list = [item]
131
shortest_length = new_length
136
def getRosettaBestBinaryPackageName(sequence):
137
"""Return the best binary package name from a list.
139
It follows the Rosetta policy:
141
We don't need a concrete value from binary package name, we use shortest
142
function as a kind of heuristic to choose the shortest binary package
143
name that we suppose will be the more descriptive one for our needs with
144
PO templates. That's why we get always the first element.
146
return shortest(sequence)[0]
149
def getRosettaBestDomainPath(sequence):
150
"""Return the best path for a concrete .pot file from a list of paths.
152
It follows the Rosetta policy for this path:
154
We don't need a concrete value from domain_paths list, we use shortest
155
function as a kind of heuristic to choose the shortest path if we have
156
more than one, usually, we will have only one element.
158
return shortest(sequence)[0]
161
def getValidNameFromString(invalid_name):
162
"""Return a valid name based on a string.
164
A name in launchpad has a set of restrictions that not all strings follow.
165
This function converts any string in another one that follows our name
168
To know more about all restrictions, please, look at valid_name function
171
# All chars should be lower case, underscores and spaces become dashes.
172
return text_replaced(invalid_name.lower(), {'_': '-', ' ': '-'})
103
175
def browserLanguages(request):
104
176
"""Return a list of Language objects based on the browser preferences."""
105
177
return IRequestPreferredLanguages(request).getPreferredLanguages()
142
214
for mail_person in get_recipients(person))
217
replacements = {0: {'.': ' |dot| ',
230
def obfuscateEmail(emailaddr, idx=None):
231
"""Return an obfuscated version of the provided email address.
233
Randomly chose a set of replacements for some email address characters and
234
replace them. This will make harder for email harvesters to fetch email
235
address from launchpad.
237
>>> obfuscateEmail('foo@bar.com', 0)
238
'foo |at| bar |dot| com'
239
>>> obfuscateEmail('foo.bar@xyz.com.br', 1)
240
'foo ! bar {} xyz ! com ! br'
243
idx = random.randint(0, len(replacements) - 1)
244
return text_replaced(emailaddr, replacements[idx])
145
247
class ShortListTooBigError(Exception):
146
248
"""This error is raised when the shortlist hardlimit is reached"""
436
def positiveIntOrZero(value):
437
"""Return 0 if int(value) fails or if int(value) is less than 0.
439
Return int(value) otherwise.
441
>>> positiveIntOrZero(None)
443
>>> positiveIntOrZero(-9)
445
>>> positiveIntOrZero(1)
447
>>> positiveIntOrZero('-3')
449
>>> positiveIntOrZero('5')
451
>>> positiveIntOrZero(3.1415)
454
value = intOrZero(value)
334
460
def get_email_template(filename, app=None):
335
461
"""Returns the email template with the given file name.