163
139
traceback.print_exc(file=tb)
164
140
return tb.getvalue()
142
def get_lines_from_file(filename, lineno, context=0):
143
"""Return `content` number of lines before and after the specified
144
`lineno` from the file identified by `filename`.
146
Returns a `(lines_before, line, lines_after)` tuple.
148
if os.path.isfile(filename):
149
fileobj = open(filename, 'U')
151
lines = fileobj.readlines()
152
lbound = max(0, lineno - context)
153
ubound = lineno + 1 + context
157
rep = re.compile('coding[=:]\s*([-\w.]+)')
158
for linestr in lines[0], lines[1]:
159
match = rep.search(linestr)
161
charset = match.group(1)
164
before = [to_unicode(l.rstrip('\n'), charset)
165
for l in lines[lbound:lineno]]
166
line = to_unicode(lines[lineno].rstrip('\n'), charset)
167
after = [to_unicode(l.rstrip('\n'), charset) \
168
for l in lines[lineno + 1:ubound]]
170
return before, line, after
166
175
def safe__import__(module_name):
168
177
Safe imports: rollback after a failed import.
181
190
del(sys.modules[modname])
193
# -- setuptools utils
195
def get_module_path(module):
196
# Determine the plugin that this component belongs to
197
path = module.__file__
198
module_name = module.__name__
199
if path.endswith('.pyc') or path.endswith('.pyo'):
201
if os.path.basename(path) == '__init__.py':
202
path = os.path.dirname(path)
203
base_path = os.path.splitext(path)[0]
204
while base_path.replace(os.sep, '.').endswith(module_name):
205
base_path = os.path.dirname(base_path)
206
module_name = '.'.join(module_name.split('.')[:-1])
211
def get_pkginfo(dist):
212
"""Get a dictionary containing package information for a package
214
`dist` can be either a Distribution instance or, as a shortcut,
215
directly the module instance, if one can safely infer a Distribution
218
Always returns a dictionary but it will be empty if no Distribution
219
instance can be created for the given module.
222
if isinstance(dist, types.ModuleType):
224
from pkg_resources import find_distributions
226
module_path = get_module_path(module)
227
for dist in find_distributions(module_path, only=True):
228
if os.path.isfile(module_path) or \
229
dist.key == module.__name__.lower():
236
attrs = ('author', 'author-email', 'license', 'home-page', 'summary',
237
'description', 'version')
240
return attr.lower().replace('-', '_')
242
pkginfo = email.message_from_string(dist.get_metadata('PKG-INFO'))
243
for attr in [key for key in attrs if key in pkginfo]:
244
info[normalize(attr)] = pkginfo[attr]
246
err = 'Failed to read PKG-INFO file for %s: %s' % (dist, e)
248
info[normalize(attr)] = err
249
except email.Errors.MessageError, e:
250
err = 'Failed to parse PKG-INFO file for %s: %s' % (dist, e)
252
info[normalize(attr)] = err
185
255
# -- crypto utils
187
257
def hex_entropy(bytes=32):
190
return md5.md5(str(random.random())).hexdigest()[:bytes]
260
return sha.new(str(random.random())).hexdigest()[:bytes]
193
263
# Original license for md5crypt:
257
327
rearranged += itoa64[v & 0x3f]; v >>= 6
259
329
return magic + salt + '$' + rearranged
334
class Ranges(object):
336
Holds information about ranges parsed from a string
338
>>> x = Ranges("1,2,9-15")
347
>>> [i for i in range(20) if i in x]
348
[1, 2, 9, 10, 11, 12, 13, 14, 15]
350
Also supports iteration, which makes that last example a bit simpler:
353
[1, 2, 9, 10, 11, 12, 13, 14, 15]
355
Note that it automatically reduces the list and short-circuits when the
356
desired ranges are a relatively small portion of the entire set:
359
>>> 1 in x # really fast
361
>>> x = Ranges("1, 2, 1-2, 2") # reduces this to 1-2
364
>>> x = Ranges("1-9,2-4") # handle ranges that completely overlap
366
[1, 2, 3, 4, 5, 6, 7, 8, 9]
368
The members 'a' and 'b' refer to the min and max value of the range, and
369
are None if the range is empty:
379
Empty ranges are ok, and ranges can be constructed in pieces, if you
383
>>> x.appendrange("1, 2, 3")
384
>>> x.appendrange("5-9")
385
>>> x.appendrange("2-3") # reduce'd away
387
[1, 2, 3, 5, 6, 7, 8, 9]
389
''Code contributed by Tim Hatch''
392
RE_STR = r"""\d+(?:[-:]\d+)?(?:,\d+(?:[-:]\d+)?)*"""
394
def __init__(self, r=None):
396
self.a = self.b = None
399
def appendrange(self, r):
400
"""Add a range (from a string or None) to the current one"""
404
for x in r.split(","):
406
a, b = map(int, x.split('-', 1))
408
a, b = int(x), int(x)
414
"""Come up with the minimal representation of the ranges"""
418
while i + 1 < len(p):
419
if p[i+1][0]-1 <= p[i][1]: # this item overlaps with the next
420
# make the first include the second
421
p[i] = (p[i][0], max(p[i][1], p[i+1][1]))
422
del p[i+1] # delete the second, after adjusting my endpoint
426
self.a = p[0][0] # min value
427
self.b = p[-1][1] # max value
429
self.a = self.b = None
433
This is another way I came up with to do it. Is it faster?
435
from itertools import chain
436
return chain(*[xrange(a, b+1) for a, b in self.pairs])
438
for a, b in self.pairs:
439
for i in range(a, b+1):
442
def __contains__(self, x):
447
# short-circuit if outside the possible range
448
if self.a is not None and self.a <= x <= self.b:
449
for a, b in self.pairs:
452
if b > x: # short-circuit if we've gone too far
457
"""Provide a compact string representation of the range.
459
>>> (str(Ranges("1,2,3,5")), str(Ranges()), str(Ranges('2')))
461
>>> str(Ranges('99-1')) # only nondecreasing ranges allowed
465
for a, b in self.pairs:
469
r.append("%d-%d" % (a, b))
473
"""The length of the entire span, ignoring holes.
475
>>> (len(Ranges('99')), len(Ranges('1-2')), len(Ranges('')))
478
if self.a is not None and self.b is not None:
479
return self.b - self.a + 1
483
def content_disposition(type, filename=None):
484
"""Generate a properly escaped Content-Disposition header"""
485
if isinstance(filename, unicode):
486
filename = filename.encode('utf-8')
487
return type + '; filename=' + quote(filename, safe='')
489
def pairwise(iterable):
490
"""s -> (s0,s1), (s1,s2), (s2, s3), ...
492
:deprecated: since 0.11 (if this really needs to be used, rewrite it
498
except StopIteration:
502
def partition(iterable, order=None):
504
if order is not None:
507
for item, category in iterable:
508
result.setdefault(category, []).append(item)
511
return [result[key] for key in order]