On 01/03/2011 10:16 PM, Eduardo Habkost wrote:
> On Mon, Jan 03, 2011 at 09:48:02PM +0200, Michael Goldish wrote:
>> On 01/03/2011 09:26 PM, Eduardo Habkost wrote:
>>> On Mon, Jan 03, 2011 at 08:34:07PM +0200, Michael Goldish wrote:
>>>> +# Exception context information:
>>>> +# ------------------------------
>>>> +# Every function can have some context string associated with it.
>>>> +# The context string can be changed by calling context(str) and cleared by
>>>> +# calling context() with no parameters.
>>>> +# get_context() joins the current context strings of all functions in the
>>>> +# provided traceback.  The result is a brief description of what the test 
>>>> was
>>>> +# doing in the provided traceback (which should be the traceback of a 
>>>> caught
>>>> +# exception).
>>>
>>> I am sure people will eventually forget to call context() to clear
>>> previous context calls, and people won't notice until an actual error is
>>> raised on another section.
>>>
>>> What about a decorator like:
>>>
>>> @context("hello")
>>> def a()
>>>     ...
>>>
>>> That would set/clear the context automatically when the function is
>>> called/returns?
>>
>> - In most cases it isn't necessary. The context dict maps whole stack
>> traces to context strings, which means that if a function is called
>> twice from different places in the code, it won't have the same context
>> string.  The only problematic case is functions that are called in loops
>> and declare context strings.  I suppose those should be rare, and all
>> they need to do to prevent the problem is call error.context() at the top.
>> - A function can (and should) change its context string at runtime (for
>> example: before reboot, after reboot).  If each function could only
>> declare a single context, we could just use the function's name and we
>> wouldn't need a context.
> 
> OK, I am convinced about the general features of the API (one context
> per function call).
> 
> Now, about the internal implementation: what about something to avoid
> using the stack trace tricks on context()? Basically what you need is
> something that adds a new "context entry" when a function is called and
> clear it once the function returns.
> 
> What about a decorator tyat indicates "this function will push/pop a context
> when it is called/returns"? It looks much simpler than doing the tricks
> involving the stack on every context() call.
> 
> 
> e.g. I would find something like the following much easier to understand:
> 
> CTX = threading.local()
> CTX.contexts = []
> 
> def context(s):
>     """Change current context"""
>     CTX.contexts[-1] = s
> 
> def new_context(s):
>     """Push new context in the stack"""
>     CTX.contexts.append(s)
> 
> def clear_context():
>     """Remove current context from the stack"""
>     CTX.contexts.pop()
> 
> def get_context():
>     return " --> ".join(CTX.contexts)
> 
> def context_aware(fn):
>     # quick solution. using decorator util functions to keep function 
> metadata would be better
>     def docall(*args, **kwargs):
>         new_context("[%s function begin]" % (fn.__name__))
>         try:
>             return fn(*args, **kwargs)
>         finally:
>             clear_context()
>     return docall
> 
> 
> ### sample usage:
> @context_aware
> def a():
>     context("hello")
>     b()
>     context("world")
>     print 'b:',get_context() # ----> 'world'
> 
> @context_aware
> def b():
>     context("foo")
>     c()
> 
> @context_aware
> def c():
>     context("bar")
>     print 'c:',get_context() # ----> 'hello --> foo --> bar'
> 

If I understand your suggestion correctly, then:

- The purpose of contexts is to add information to exceptions.  If an
exception is raised and you call clear_context() in a finally clause,
you remove the context of the current function.  If the function itself
calls get_context() it'll see the current context, but as soon as an
exception is raised the context disappears all the way up to the point
where the exception is caught.

- A possible solution would be not to use finally and only call
clear_context() if the function terminates successfully.  Then if a
function raises an exception, the context remains for some handler to
catch and print.  However, if we catch the exception sometime later and
decide not to fail the test (for example: login failed and we don't
care) then we have to pop some context items, or the context stack will
be corrupted.

- Additionally, if some function raises an exception and we perform some
cleanup in a finally clause, and during that cleanup we call another
function that calls context(), then our context stack will be modified
by the other function, and the context handler (which calls
get_context()) will not see the original context but rather a modified one.

Let me know if I misunderstood something.

Thanks,
Michael
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to