commit 2cf46cd3a267a6829ff8ad562be5a7ecc2dc89ca Author: Arturo Filastò arturo@filasto.net Date: Wed Nov 7 20:10:46 2012 +0100
Move peculiar unused file to wtf directory. --- ooni/utils/assertions.py | 180 ---------------------------------- ooni/utils/meta.py | 241 ---------------------------------------------- ooni/utils/timer.py | 107 -------------------- wtf/assertions.py | 180 ++++++++++++++++++++++++++++++++++ wtf/meta.py | 241 ++++++++++++++++++++++++++++++++++++++++++++++ wtf/timer.py | 107 ++++++++++++++++++++ 6 files changed, 528 insertions(+), 528 deletions(-)
diff --git a/ooni/utils/assertions.py b/ooni/utils/assertions.py deleted file mode 100644 index 875dcf5..0000000 --- a/ooni/utils/assertions.py +++ /dev/null @@ -1,180 +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. -# - -class ValueChecker(object): - """ - A class for general purpose value checks on commandline parameters - passed to subclasses of :class:`twisted.python.usage.Options`. - """ - default_doc = "fix me" - - def __init__(self, coerce_doc=None): - if not coerce_doc: - self.coerce_doc = default_doc - else: - self.coerce_doc = coerce_doc - - @classmethod - def port_check(cls, port, - range_min=1024, range_max=65535, - coerce_doc=None): - """ - Check that given ports are in the allowed range for an unprivileged - user. - - :param port: - The port to check. - :param range_min: - The minimum allowable port number. - :param range_max: - The minimum allowable port number. - :param coerce_doc: - The documentation string to show in the optParameters menu, see - :class:`twisted.python.usage.Options`. - """ - if not coerce_doc: - coerceDoc = cls.default_doc - - assert isinstance(port, int) - if port not in range(range_min, range_max): - raise ValueError("Port out of range") - log.err() - - @staticmethod - def uid_check(error_message): - """ - Check that we're not root. If we are, setuid(1000) to normal user if - we're running on a posix-based system, and if we're on Windows just - tell the user that we can't be run as root with the specified options - and then exit. - - :param error_message: - The string to log as an error message when the uid check fails. - """ - uid, gid = os.getuid(), os.getgid() - if uid == 0 and gid == 0: - log.msg(error_message) - if os.name == 'posix': - log.msg("Dropping privileges to normal user...") - os.setgid(1000) - os.setuid(1000) - else: - sys.exit(0) - - @staticmethod - def dir_check(d): - """Check that the given directory exists.""" - if not os.path.isdir(d): - raise ValueError("%s doesn't exist, or has wrong permissions" % d) - - @staticmethod - def file_check(f): - """Check that the given file exists.""" - if not os.path.isfile(f): - raise ValueError("%s does not exist, or has wrong permissions" % f) - -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/ooni/utils/meta.py b/ooni/utils/meta.py deleted file mode 100644 index 0b810f7..0000000 --- a/ooni/utils/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/ooni/utils/timer.py b/ooni/utils/timer.py deleted file mode 100644 index e03fd74..0000000 --- a/ooni/utils/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 - diff --git a/wtf/assertions.py b/wtf/assertions.py new file mode 100644 index 0000000..875dcf5 --- /dev/null +++ b/wtf/assertions.py @@ -0,0 +1,180 @@ +#-*- 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. +# + +class ValueChecker(object): + """ + A class for general purpose value checks on commandline parameters + passed to subclasses of :class:`twisted.python.usage.Options`. + """ + default_doc = "fix me" + + def __init__(self, coerce_doc=None): + if not coerce_doc: + self.coerce_doc = default_doc + else: + self.coerce_doc = coerce_doc + + @classmethod + def port_check(cls, port, + range_min=1024, range_max=65535, + coerce_doc=None): + """ + Check that given ports are in the allowed range for an unprivileged + user. + + :param port: + The port to check. + :param range_min: + The minimum allowable port number. + :param range_max: + The minimum allowable port number. + :param coerce_doc: + The documentation string to show in the optParameters menu, see + :class:`twisted.python.usage.Options`. + """ + if not coerce_doc: + coerceDoc = cls.default_doc + + assert isinstance(port, int) + if port not in range(range_min, range_max): + raise ValueError("Port out of range") + log.err() + + @staticmethod + def uid_check(error_message): + """ + Check that we're not root. If we are, setuid(1000) to normal user if + we're running on a posix-based system, and if we're on Windows just + tell the user that we can't be run as root with the specified options + and then exit. + + :param error_message: + The string to log as an error message when the uid check fails. + """ + uid, gid = os.getuid(), os.getgid() + if uid == 0 and gid == 0: + log.msg(error_message) + if os.name == 'posix': + log.msg("Dropping privileges to normal user...") + os.setgid(1000) + os.setuid(1000) + else: + sys.exit(0) + + @staticmethod + def dir_check(d): + """Check that the given directory exists.""" + if not os.path.isdir(d): + raise ValueError("%s doesn't exist, or has wrong permissions" % d) + + @staticmethod + def file_check(f): + """Check that the given file exists.""" + if not os.path.isfile(f): + raise ValueError("%s does not exist, or has wrong permissions" % f) + +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 new file mode 100644 index 0000000..0b810f7 --- /dev/null +++ b/wtf/meta.py @@ -0,0 +1,241 @@ +#-*- 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 new file mode 100644 index 0000000..e03fd74 --- /dev/null +++ b/wtf/timer.py @@ -0,0 +1,107 @@ +# +# 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 +
tor-commits@lists.torproject.org