3 from __future__ import with_statement
19 def printfmt(template):
21 This hides having to create the Template object and call substitute/safe_substitute on it. For example:
25 >>> printfmt("I would like to order $num units of $word, please") #doctest: +SKIP
26 I would like to order 10 units of spam, please
28 frame = inspect.stack()[-1][0]
30 print string.Template(template).safe_substitute(frame.f_locals)
36 return name.startswith("__") and name.endswith("__")
40 return name.startswith("_") and not is_special(name)
43 def privatize(clsName, attributeName):
45 At runtime, make an attributeName private
48 >>> class Test(object):
52 ... dir(Test).index("_Test__me")
57 >>> setattr(Test, privatize(Test.__name__, "me"), "Hello World")
59 ... dir(Test).index("_Test__me")
65 >>> print getattr(Test, obfuscate(Test.__name__, "__me"))
68 >>> is_private(privatize(Test.__name__, "me"))
70 >>> is_special(privatize(Test.__name__, "me"))
73 return "".join(["_", clsName, "__", attributeName])
76 def obfuscate(clsName, attributeName):
78 At runtime, turn a private name into the obfuscated form
81 >>> class Test(object):
82 ... __me = "Hello World"
85 ... dir(Test).index("_Test__me")
91 >>> print getattr(Test, obfuscate(Test.__name__, "__me"))
93 >>> is_private(obfuscate(Test.__name__, "__me"))
95 >>> is_special(obfuscate(Test.__name__, "__me"))
98 return "".join(["_", clsName, attributeName])
101 class PAOptionParser(optparse.OptionParser, object):
103 >>> if __name__ == '__main__':
104 ... #parser = PAOptionParser("My usage str")
105 ... parser = PAOptionParser()
106 ... parser.add_posarg("Foo", help="Foo usage")
107 ... parser.add_posarg("Bar", dest="bar_dest")
108 ... parser.add_posarg("Language", dest='tr_type', type="choice", choices=("Python", "Other"))
109 ... parser.add_option('--stocksym', dest='symbol')
110 ... values, args = parser.parse_args()
111 ... print values, args
117 python mycp.py foo bar
119 python mycp.py foo bar lava
120 Usage: pa.py <Foo> <Bar> <Language> [options]
122 Positional Arguments:
127 pa.py: error: option --Language: invalid choice: 'lava' (choose from 'Python', 'Other'
130 def __init__(self, *args, **kw):
132 super(PAOptionParser, self).__init__(*args, **kw)
134 def add_posarg(self, *args, **kw):
135 pa_help = kw.get("help", "")
136 kw["help"] = optparse.SUPPRESS_HELP
137 o = self.add_option("--%s" % args[0], *args[1:], **kw)
138 self.posargs.append((args[0], pa_help))
140 def get_usage(self, *args, **kwargs):
141 params = (' '.join(["<%s>" % arg[0] for arg in self.posargs]), '\n '.join(["%s: %s" % (arg) for arg in self.posargs]))
142 self.usage = "%%prog %s [options]\n\nPositional Arguments:\n %s" % params
143 return super(PAOptionParser, self).get_usage(*args, **kwargs)
145 def parse_args(self, *args, **kwargs):
148 for p, v in zip(self.posargs, args):
149 args0.append("--%s" % p[0])
152 options, args = super(PAOptionParser, self).parse_args(args, **kwargs)
153 if len(args) < len(self.posargs):
154 msg = 'Missing value(s) for "%s"\n' % ", ".join([arg[0] for arg in self.posargs][len(args):])
159 def explicitly(name, stackadd=0):
161 This is an alias for adding to '__all__'. Less error-prone than using
162 __all__ itself, since setting __all__ directly is prone to stomping on
163 things implicitly exported via L{alias}.
165 @note Taken from PyExport (which could turn out pretty cool):
166 @li @a http://codebrowse.launchpad.net/~glyph/
167 @li @a http://glyf.livejournal.com/74356.html
169 packageVars = sys._getframe(1+stackadd).f_locals
170 globalAll = packageVars.setdefault('__all__', [])
171 globalAll.append(name)
176 This is a decorator, for convenience. Rather than typing the name of your
177 function twice, you can decorate a function with this.
179 To be real, @public would need to work on methods as well, which gets into
182 @note Taken from PyExport (which could turn out pretty cool):
183 @li @a http://codebrowse.launchpad.net/~glyph/
184 @li @a http://glyf.livejournal.com/74356.html
186 explicitly(thunk.__name__, 1)
190 def _append_docstring(obj, message):
191 if obj.__doc__ is None:
192 obj.__doc__ = message
194 obj.__doc__ += message
197 def validate_decorator(decorator):
205 f.__dict__["member"] = True
209 if f.__name__ != g.__name__:
210 print f.__name__, "!=", g.__name__
212 if g.__doc__ is None:
213 print decorator.__name__, "has no doc string"
214 elif not g.__doc__.startswith(f.__doc__):
215 print g.__doc__, "didn't start with", f.__doc__
217 if not ("member" in g.__dict__ and g.__dict__["member"]):
218 print "'member' not in ", g.__dict__
221 def deprecated_api(func):
223 This is a decorator which can be used to mark functions
224 as deprecated. It will result in a warning being emitted
225 when the function is used.
227 >>> validate_decorator(deprecated_api)
230 @functools.wraps(func)
231 def newFunc(*args, **kwargs):
232 warnings.warn("Call to deprecated function %s." % func.__name__, category=DeprecationWarning)
233 return func(*args, **kwargs)
235 _append_docstring(newFunc, "\n@deprecated")
239 def unstable_api(func):
241 This is a decorator which can be used to mark functions
242 as deprecated. It will result in a warning being emitted
243 when the function is used.
245 >>> validate_decorator(unstable_api)
248 @functools.wraps(func)
249 def newFunc(*args, **kwargs):
250 warnings.warn("Call to unstable API function %s." % func.__name__, category=FutureWarning)
251 return func(*args, **kwargs)
252 _append_docstring(newFunc, "\n@unstable")
258 This decorator doesn't add any behavior
260 >>> validate_decorator(enabled)
267 This decorator disables the provided function, and does nothing
269 >>> validate_decorator(disabled)
272 @functools.wraps(func)
273 def emptyFunc(*args, **kargs):
275 _append_docstring(emptyFunc, "\n@note Temporarily Disabled")
279 def metadata(document=True, **kwds):
281 >>> validate_decorator(metadata(author="Ed"))
285 for k, v in kwds.iteritems():
288 _append_docstring(func, "\n@"+k+" "+v)
294 """Function decorator for defining property attributes
296 The decorated function is expected to return a dictionary
297 containing one or more of the following pairs:
298 fget - function for getting attribute value
299 fset - function for setting attribute value
300 fdel - function for deleting attribute
301 This can be conveniently constructed by the locals() builtin
303 http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/205183
304 @author http://kbyanc.blogspot.com/2007/06/python-property-attribute-tricks.html
307 >>> #Due to transformation from function to property, does not need to be validated
308 >>> #validate_decorator(prop)
309 >>> class MyExampleClass(object):
312 ... "The foo property attribute's doc-string"
316 ... def fset(self, value):
318 ... self._foo = value
321 >>> me = MyExampleClass()
328 return property(doc=func.__doc__, **func())
331 def print_handler(e):
335 print "%s: %s" % (type(e).__name__, e)
342 print 'Ignoring %s exception: %s' % (type(e).__name__, e)
345 def print_traceback(e):
349 #print sys.exc_info()
350 traceback.print_exc(file=sys.stdout)
353 def ExpHandler(handler = print_handler, *exceptions):
355 An exception handling idiom using decorators
357 Specify exceptions in order, first one is handled first
360 >>> validate_decorator(ExpHandler())
361 >>> @ExpHandler(print_ignore, ZeroDivisionError)
362 ... @ExpHandler(None, AttributeError, ValueError)
365 >>> @ExpHandler(print_traceback, ZeroDivisionError)
372 >>> @ExpHandler(print_traceback, ZeroDivisionError)
378 Ignoring ZeroDivisionError exception: integer division or modulo by zero
379 >>> f2() # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
380 Traceback (most recent call last):
382 ZeroDivisionError: integer division or modulo by zero
384 IndexError: tuple index out of range
390 localExceptions = exceptions
391 if not localExceptions:
392 localExceptions = [Exception]
393 t = [(ex, handler) for ex in localExceptions]
396 def newfunc(t, *args, **kwargs):
400 return f(*args, **kwargs)
402 #Recurse for embedded try/excepts
403 dec_func = functools.partial(newfunc, t[1:])
404 dec_func = functools.update_wrapper(dec_func, f)
405 return dec_func(*args, **kwargs)
409 dec_func = functools.partial(newfunc, t)
410 dec_func = functools.update_wrapper(dec_func, f)
415 def into_debugger(func):
417 >>> validate_decorator(into_debugger)
420 @functools.wraps(func)
421 def newFunc(*args, **kwargs):
423 return func(*args, **kwargs)
431 class bindclass(object):
433 >>> validate_decorator(bindclass)
434 >>> class Foo(BoundObject):
436 ... def foo(this_class, self):
437 ... return this_class, self
441 ... def bar(this_class, self):
442 ... return this_class, self
447 >>> f.foo() # doctest: +ELLIPSIS
448 (<class '...Foo'>, <...Foo object at ...>)
449 >>> b.foo() # doctest: +ELLIPSIS
450 (<class '...Foo'>, <...Bar object at ...>)
451 >>> b.bar() # doctest: +ELLIPSIS
452 (<class '...Bar'>, <...Bar object at ...>)
455 def __init__(self, f):
457 self.__name__ = f.__name__
458 self.__doc__ = f.__doc__
459 self.__dict__.update(f.__dict__)
462 def bind(self, cls, attr):
464 def bound_m(*args, **kwargs):
465 return self.f(cls, *args, **kwargs)
466 bound_m.__name__ = attr
469 def __get__(self, obj, objtype=None):
470 return self.m.__get__(obj, objtype)
473 class ClassBindingSupport(type):
476 def __init__(mcs, name, bases, attrs):
477 type.__init__(mcs, name, bases, attrs)
478 for attr, val in attrs.iteritems():
479 if isinstance(val, bindclass):
483 class BoundObject(object):
485 __metaclass__ = ClassBindingSupport
490 >>> validate_decorator(bindfunction)
492 ... def factorial(thisfunction, n):
493 ... # Within this function the name 'thisfunction' refers to the factorial
494 ... # function(with only one argument), even after 'factorial' is bound
495 ... # to another object
497 ... return n * thisfunction(n - 1)
506 def bound_f(*args, **kwargs):
507 return f(bound_f, *args, **kwargs)
511 class Memoize(object):
513 Memoize(fn) - an instance which acts like fn but memoizes its arguments
514 Will only work on functions with non-mutable arguments
515 @note Source: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52201
517 >>> validate_decorator(Memoize)
520 def __init__(self, fn):
522 self.__name__ = fn.__name__
523 self.__doc__ = fn.__doc__
524 self.__dict__.update(fn.__dict__)
527 def __call__(self, *args):
528 if args not in self.memo:
529 self.memo[args] = self.fn(*args)
530 return self.memo[args]
533 class MemoizeMutable(object):
534 """Memoize(fn) - an instance which acts like fn but memoizes its arguments
535 Will work on functions with mutable arguments(slower than Memoize)
536 @note Source: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52201
538 >>> validate_decorator(MemoizeMutable)
541 def __init__(self, fn):
543 self.__name__ = fn.__name__
544 self.__doc__ = fn.__doc__
545 self.__dict__.update(fn.__dict__)
548 def __call__(self, *args, **kw):
549 text = cPickle.dumps((args, kw))
550 if text not in self.memo:
551 self.memo[text] = self.fn(*args, **kw)
552 return self.memo[text]
555 callTraceIndentationLevel = 0
560 Synchronization decorator.
562 >>> validate_decorator(call_trace)
567 Entering a((1, 2), {'c': 3})
568 Exiting a((1, 2), {'c': 3})
572 def verboseTrace(*args, **kw):
573 global callTraceIndentationLevel
575 print "%sEntering %s(%s, %s)" % ("\t"*callTraceIndentationLevel, f.__name__, args, kw)
576 callTraceIndentationLevel += 1
578 result = f(*args, **kw)
580 callTraceIndentationLevel -= 1
581 print "%sException %s(%s, %s)" % ("\t"*callTraceIndentationLevel, f.__name__, args, kw)
583 callTraceIndentationLevel -= 1
584 print "%sExiting %s(%s, %s)" % ("\t"*callTraceIndentationLevel, f.__name__, args, kw)
588 def smallTrace(*args, **kw):
589 global callTraceIndentationLevel
591 print "%sEntering %s" % ("\t"*callTraceIndentationLevel, f.__name__)
592 callTraceIndentationLevel += 1
594 result = f(*args, **kw)
596 callTraceIndentationLevel -= 1
597 print "%sException %s" % ("\t"*callTraceIndentationLevel, f.__name__)
599 callTraceIndentationLevel -= 1
600 print "%sExiting %s" % ("\t"*callTraceIndentationLevel, f.__name__)
607 @contextlib.contextmanager
608 def lexical_scope(*args):
610 @note Source: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/520586
613 >>> with lexical_scope(1) as (a):
617 >>> with lexical_scope(1,2,3) as (a,b,c):
621 >>> with lexical_scope():
630 frame = inspect.currentframe().f_back.f_back
631 saved = frame.f_locals.keys()
640 f_locals = frame.f_locals
641 for key in (x for x in f_locals.keys() if x not in saved):
646 def normalize_number(prettynumber):
648 function to take a phone number and strip out all non-numeric
651 >>> normalize_number("+012-(345)-678-90")
653 >>> normalize_number("1-(345)-678-9000")
655 >>> normalize_number("+1-(345)-678-9000")
658 uglynumber = re.sub('[^0-9+]', '', prettynumber)
659 if uglynumber.startswith("+"):
661 elif uglynumber.startswith("1") and len(uglynumber) == 11:
662 uglynumber = "+"+uglynumber
663 elif len(uglynumber) == 10:
664 uglynumber = "+1"+uglynumber
668 #validateRe = re.compile("^\+?[0-9]{10,}$")
669 #assert validateRe.match(uglynumber) is not None
674 _VALIDATE_RE = re.compile("^\+?[0-9]{10,}$")
677 def is_valid_number(number):
679 @returns If This number be called ( syntax validation only )
681 return _VALIDATE_RE.match(number) is not None