The principal problem with your code is the overuse of the double underscore namespace conflict prevention in a class that isn't intended to be subclassed at all.
In general, self.__foo
is a code smell that should be accompanied by a comment along the lines of # This is a mixin and we don't want arbitrary subclasses to have a namespace conflict
.
Further the client API of this method would look like this:
def mymethod(): pass
mymethod = add_timeout(mymethod, 15)
# start the processing
timeout_obj = mymethod()
try:
# access the property, which is really a function call
ret = timeout_obj.value
except TimeoutError:
# handle a timeout here
ret = None
This is not very pythonic at all and a better client api would be:
@timeout(15)
def mymethod(): pass
try:
my_method()
except TimeoutError:
pass
You are using @property in your class for something that is a state mutating accessor, this is not a good idea. For instance, what would happen when .value is accessed twice? It looks like it would fail because queue.get() would return trash because the queue is already empty.
Remove @property entirely. Don't use it in this context, it's not suitable for your use-case. Make call block when called and return the value or raise the exception itself. If you really must have value accessed later, make it a method like .get() or .value().
This code for the _target should be rewritten a little:
def _target(queue, function, *args, **kwargs):
try:
queue.put((True, function(*args, **kwargs)))
except:
queue.put((False, exc_info())) # get *all* the exec info, don't do exc_info[1]
# then later:
raise exc_info[0], exc_info[1], exc_info[2]
That way the stack trace will be preserved correctly and visible to the programmer.
I think you've made a reasonable first crack at writing a useful library, I like the usage of the processing module to achieve the goals.