commit 16d270968cd1d52a081b3c653312b1e4b029dd9f Author: Isis Lovecruft isis@torproject.org Date: Thu Nov 8 08:32:43 2012 +0000
* Moved metaclass code to separate branch 'meta'. --- wtf/README | 9 -- wtf/assertions.py | 108 ------------------------ wtf/meta.py | 241 ----------------------------------------------------- wtf/timer.py | 107 ----------------------- 4 files changed, 0 insertions(+), 465 deletions(-)
diff --git a/wtf/README b/wtf/README deleted file mode 100644 index 7696130..0000000 --- a/wtf/README +++ /dev/null @@ -1,9 +0,0 @@ -I have moved here all the pieces of code that are either not being used -anywhere or that should probably not be part of OONIProbe. - -They are very neat pieces of code, though I have not tester their -functionality, since there are no examples of how they should be called or -used in the repo. - -I believe they should go on a separate branch or on another repo called -something like "python class kung foo". diff --git a/wtf/assertions.py b/wtf/assertions.py deleted file mode 100644 index 60d3c50..0000000 --- a/wtf/assertions.py +++ /dev/null @@ -1,108 +0,0 @@ -#-*- coding: utf-8 -*- -# -# assertions.py -# ------------- -# Collection of utilies for checks and assertions. -# -# :authors: Isis Lovecruft -# :version: 0.1.0-alpha -# :license: see included LICENSE file -# :copyright: 2012 Isis Lovecruft, The Tor Project Inc. -# - -def isNewStyleClass(obj): - """ - Check if :param:`obj` is a new-style class, which is any class - derived by either - - NewStyleClass = type('NewStyleClass') - or - class NewStyleClass(object): - pass - - whereas old-style classes are (only in Python 2.x) derived by: - - class OldStyleClass: - pass - - There are recently implemented classes in many Python libraries, - including a few in Twisted, which are derived from old-style classes. - Thus, calling super() or attempting to retrieve the MRO of any - subclass of old-style classes can cause issues such as: - - o Calling Subclass.mro() goes through the class hierarchy until it - reaches OldStyleClass, which has no mro(), and raises an - AttributeError. - - o Calling super(Subclass, subcls) produces an ambiguous, rather than - algorithmic, class hierarchy, which (especially in Twisted's case) - breaks multiple inheritance. - - o Expected Subclass instance attributes, in particular __bases__ and - __class__, can be missing, which in turn leads to problems with - a whole bunch of builtin methods and modules. - - For more information, see: - http://www.python.org/download/releases/2.3/mro/ - http://www.cafepy.com/article/python_attributes_and_methods/ - - :return: - True if :param:`obj` is a new-style class derived from object; - False if :param:`obj` is an old-style class (or derived from - one). - """ - from types import ClassType - return not isinstance(type(object), ClassType) - -def isOldStyleClass(obj): - """ - Check if :param:`obj` is an old-style class, which is any class - derived in Python 2.x with: - - class OldStyleClass: - pass - - There are recently implemented classes in many Python libraries, - including a few in Twisted, which are derived from old-style classes, - and thus their types, bases, and attributes are generally just messed - up. - - :return: - True if :param:`obj` is a new-style class derived from object; - False if :param:`obj` is an old-style class (or derived from - one). - """ - from types import ClassType - return not isinstance(type(object), ClassType) - -def isClass(obj): - """ - Check if an object is *a* class (not a specific class), without trying - to call the obj.__class__ attribute, as that is not available in some - cases. This function will return True for both old- and new-style - classes, however, it will return False for class instances of either - style. An alternate way to do this (although it presumes that - :param:`obj` is a class instance) would be: - - from types import TypeType - return isinstance(object.__class__, TypeType) - - It turns out that objects with <type 'type'>, i.e. classes, don't - actually have the __class__ attribute...go figure. Instead, class - objects are only required to have the __doc__ and __module__ - attributes. - - :param obj: - Any object. - :return: - True if :param:`obj` is a class, False if otherwise (even if - :param:`obj` is an instance of a class). - """ - from types import ClassType - return isinstance(object, (type, ClassType)) - -def isNotClass(object): - """ - See :func:`isClass`. - """ - return True if not isClass(object) else False diff --git a/wtf/meta.py b/wtf/meta.py deleted file mode 100644 index 0b810f7..0000000 --- a/wtf/meta.py +++ /dev/null @@ -1,241 +0,0 @@ -#-*- coding: utf-8 -*- -# -# meta.py -# ------- -# Meta classes for coercing subclasses which don't exist yet. -# -# :authors: Isis Lovecruft -# :version: 0.1.0-alpha -# :copyright: 2012 Isis Lovecruft -# :license: see attached LICENSE file -# - -class MetaDescriptor(type): - """ - bug: "Attribute error: class <> has no attribute __bases__" - - There are only objects. However, there *are* two kinds of objects: - type-objects and non-type-objects. - - There are only two objects which do not have an attribute named - "__bases__": - - 1) Instances of the builtin object ``object`` itself (i.e. the - superclass of any top-level class in python), whose __class__ is - itself, and whose type is ``type(type)``: - - >>> o = object() - >>> type(o) - <type 'type'> - >>> o.__class__ - <type 'object'> - - The o.__class__ part seems to imply that the __bases__ of - ``object`` should be itself. - - 2) Old Style Classes. Which are despicable demons, deserving to be - sent straight back to hell. No offense to the good Satanas. The - only thing these have by default is __doc__ and __module__. Also, - and this is importants too: Old Style Classes do not have an - attribute named __class__, because they do not derive from - anything. - - Incidentally, the type of ``type(type)`` is itself, and the "__bases__" of - ``type(type)`` is... - - >>> t = type(type) - >>> t.__name__ - 'type' - >>> type(t) - <type 'type'> - >>> t.__class__ - <type 'type'> - >>> t.__bases__ - (<type 'object'>,) - - ``type(object)``. WTF. This is to say that the "__bases__" of ``object`` - is the ``type`` of itself. This strange loop is where all black magic - enters into Python. - - If we do "class Metaclass(type): pass", we can then call - ``super(Metaclass, mcls)``, and the resulting ``super`` object is actually - just ``type``: - - o Its type is ``type(type)``. - o Its __bases__ ``type(object)``. - - For example, ``super(Metaclass, mcls).__new__(mcls, *a, *kw)`` is the same - as saying ``type(mcls, "Metaclass", (type, ), {} )``, except that super - does some namespace munging with calling "self.__super" on its own type, - which is probably equivalent to the strange loops with type(type) and - type(object), but my brain is already flipping out (and I keep a string - cosmology textbook on my nightstand!). - - However, we should not ever be able to call - - >>> super(type, type(mcls)).__new__(type(mcls), 'type', (type(type),) {} ) - TypeError: object.__new__(type) is not safe, use type.__new__() - - Q: Why all this fuss? - - A: We need to force future class-level attributes of subclasses of - NetTestCase to be accessible (also at the class-level, without - instatiations) by NetTestCase. I.e.: - 1) class SubNetTestCase has class attribute optParameters, but no - class for doing anything with them, and they shouldn't have to. - They should just be able to define the options. - 2) Therefore, NetTestCase needs to have data descriptors, which get - inherited. - 3) We need to be able to do this without dangerous namespace - munging, because we cannot control the namespace of future - tests. Therefore, we cannot use hacks like "self.__super". - - We need a Metaclass, which creates a Metafactory for classes which are - dynamic test descriptors. If this is confusing, leave it alone, there's - witches is these woods. - - http://stackoverflow.com/a/10707719 - http://docs.python.org/2/howto/descriptor.html - http://www.no-ack.org/2011/03/strange-behavior-with-properties-on.html - http://www.cafepy.com/article/python_types_and_objects/ - http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python - - """ - ## DO NOT GIVE A METACLASS ATTRIBUTES HERE. - #descriptors = { } - - from byteplay import Code, opmap - from functools import partial - - def __new__(im_so_meta_even_this_acronym, name, base, dict): - - def hofstaeder_decorator(meta_decorator): - def meta_decorator_factory(*args, **kwargs): - def meta_decorator_wrapper(func): - return meta_decorator(func, *args, **kwargs) - return meta_decorator_wrapper - return meta_decorator_factory - - def _transmute(opcode, arg): - if ((opcode == opmap['LOAD_GLOBAL']) and - (arg == 'self')): - return opmap['LOAD_FAST'], arg - return opcode, arg - - def selfless(child): - code = Code.from_code(child.func_code) - code.args = tuple(['self'] + list(code.args)) - code.code = [_transmute(op, arg) for op, arg in code.code] - function.func_code = code.to_code() - return function - - acronym = ( ( k,v ) for k,v in dict.items( ) - if not k.startswith('__') ) - metanym, polymorph = ( (k for ( k,v ) in acronym), - (v for ( k,v ) in acronym) ) - morphonemes = (("get%s"%n, "set%s"%n, "del%s"%n) for n in metanym ) - - oracles = [] - for getter, setter, deleter in morphonemes: - childnym = getter[3:] - - @hofstaeder_decorator - def meta_decorator(func, *args, **kwargs): - def decorator_wrapper(first, last): - return func(first, last) - return decorator_wrapper - - @meta_decorator(getter, setter, deleter) - def decorated_property(first, last): - childnym = getter[3:] - class DataDescriptor(object): - @selfless - def __init__(childnym=None, polymorph): - setattr(self, childnym, polymorph) - - @property - @selfless - def getter(): - return self.childnym - return DataDescriptor(first, last) - - oracles.append(decorated_property(childnym, polymorph)) - - return super( - MetaDescriptor, im_so_meta_even_this_acronym).__new__( - im_so_meta_even_this_acronym, - metanym, - polymorph, - dict(oracles) ) -''' - @property - def x(self): ## or getx - return self._x - @x.setter - def x(self, value): ## or setx - self._x = value - @x.deleter - def x(self): ## or delx - del self._x - ## or 'x = property(getx, setx, delx, "documentation")' - ## ^ ^ ^ ^ - ## just need @property's name, initial value can be None - -Metaclass - Creates Metaclasses for each data descriptor in each SubNetTestCase - so, per SubNetTestCase, we get (usually two) descriptors: - optParameters and input - -''' - -def applyClassAttribute(obj, cls, get='optParameters'): - """ - I get attributes from an outside instances' dictionary and attempt to - apply them to a class. I require that the attributes I am trying to set be - data descriptors which is just Python name munging trick that is mild and - harmless enough to have it's own builtin decorator helper: - - class Foo(object): - def __init__(self, *a, **kw): - if 'thing' in kw: - self._thing = thing - @property - def thing(self): - return self._thing - @property.setter - def thing(self, value): - self._thing = value - @property.delter - def thing(self): - return del(self) - """ - from ooni.utils.assertions import isClass, isNotClass - - try: - assert isNotClass(obj), "must be an instance" - assert isClass(cls), "not a class" - ## obj is probably an instance - C = obj.__class__ ## of a subclass of nettest.NetTestCase - - assert issubclass(C, cls), "not a subclass of %s" % cls - assert C.__dict__.__contains__('optParameters'), \ - "%s in %s.__dict__ not found" % (get, C) - except AssertionError, ae: - log.debug(ae) - else: - attributes = classify_class_attrs(C) - ## uncomment this to have class attributes spewn everywhere: - #log.debug("Found class attributes:\n%s" % pprint(attributes)) - for attr in attributes: - if attr.name == str(get): - setattr(obj, str(get), attr.object) - if not hasattr(obj, str(get)): - log.debug("Unable to find class attribute %s" % get) - else: - log.debug("Applying %s.%s = %s to descriptor..." - % (C.name, attr.name, attr.object)) - ## This was an unfinished attempt at fixing a class' __bases__, I do - ## not know if it was heading in the right direction. It can be - ## removed it is still crufting up the space. --isis - if '__bases__' or '_parents' in C.__dict__: - pass diff --git a/wtf/timer.py b/wtf/timer.py deleted file mode 100644 index e03fd74..0000000 --- a/wtf/timer.py +++ /dev/null @@ -1,107 +0,0 @@ -# -# timer.py -# ---------- -# OONI utilities for adding timeouts to functions and to Deferreds. -# -# :author: Isis Lovecruft -# :version: 0.1.0-pre-alpha -# :license: see include LICENSE file -# :copyright: copyright (c) 2012, Isis Lovecruft, The Tor Project Inc. -# - -class TimeoutError(Exception): - """Raised when a timer runs out.""" - pass - -def timeout(seconds, e=None): - """ - A decorator for blocking methods to cause them to timeout. Can be used - like this: - - @timeout(30) - def foo(arg1, kwarg="baz"): - for x in xrange(1000000000): - print "%s %s" % (arg1, kwarg) - print x - - or like this: - - ridiculous = timeout(30)(foo("bar")) - - :param seconds: - Number of seconds to wait before raising :class:`TimeoutError`. - :param e: - Error message to pass to :class:`TimeoutError`. Default None. - :return: - The result of the original function, or else an instance of - :class:`TimeoutError`. - """ - from signal import alarm, signal, SIGALRM - from functools import wraps - - def decorator(func): - def _timeout(signum, frame): - raise TimeoutError, e - def wrapper(*args, **kwargs): - signal(SIGALRM, _timeout) - alarm(seconds) - try: - res = func(*args, **kwargs) - finally: - alarm(0) - return res - return wraps(func)(wrapper) - return decorator - -def deferred_timeout(seconds, e=None): - """ - Decorator for adding a timeout to an instance of a - :class:`twisted.internet.defer.Deferred`. Can be used like this: - - @deferred_timeout(30) - def foo(arg1, kwarg="baz"): - for x in xrange(1000000000): - print "%s %s" % (arg1, kwarg) - print x - - or like this: - - ridiculous = deferred_timeout(30)(foo("bar")) - - :param seconds: - Number of seconds to wait before raising :class:`TimeoutError`. - :param e: - Error message to pass to :class:`TimeoutError`. Default None. - :return: - The result of the orginal :class:`twisted.internet.defer.Deferred` - or else a :class:`TimeoutError`. - """ - from twisted.internet import defer, reactor - - def wrapper(func): - @defer.inlineCallbacks - def _timeout(*args, **kwargs): - d_original = func(*args, **kwargs) - if not isinstance(d_original, defer.Deferred): - defer.returnValue(d_original) ## fail gracefully - d_timeout = defer.Deferred() - timeup = reactor.callLater(seconds, d_timeout.callback, None) - try: - original_result, timeout_result = \ - yield defer.DeferredList([d_original, d_timeout], - fireOnOneCallback=True, - fireOnOneErrback=True, - consumeErrors=True) - except defer.FirstError, dfe: - assert dfe.index == 0 ## error in original - timeup.cancel() - dfe.subFailure.raiseException() - else: - if d_timeout.called: ## timeout - d_original.cancel() - raise TimeoutError, e - timeup.cancel() ## no timeout - defer.returnValue(d_original) - return _timeout - return wrapper -