Bubbling up warnings

I’ve asked this on SO and reddit with no real elegant solutions. Have you guys run into a similar problem? What was your solutiuon?

Say you have functions A, B, and C.
A will be called and call B Then B calls C C loops on a bunch of stuff
C determines there is a warning that the user should at least be made aware of but it doesn’t block the process. How would you communicate these warnings to A?
If i pass a list to use for warnings from A to B, then its passed straight through to C as B has no use for it. Exceptions would halt C from continuing its loop.



def A():
    items = [
        [1,2,3],
        [4,5,6],
        [7,8,9]
    ]
    try:
        B(items)
    except Exception, e:
        print e


def B(items):
    for num_list in items:
        if len(num_list) == 0:
            raise ValueError('A number list cannot be empty')


        C(num_list)


def C(numbers):
    for num in numbers:
        if num > 9:
            ## -- Warning???
            print '%i is greater than 9, this might break things'


        print num


not sure if this is helpful, but an approach I have used in the past for a similar situation was to group the functions together into a class. the class had a list attribute that would accumulate warnings generated by its individual methods. the individual methods are wrappers that interpret the return value of functions do the heavy lifting.

for example:


def validateCompleteUVSet(mesh, uvset):
    # return a list of unmapped faces or []

class MeshValidator(object):
    def __init__(self, mesh):
        self.mesh = mesh
        self.warnings = []

    def validateUVs(self):
        unmappedFaces = validateCompleteUVSet(self.mesh, 'map1')
        if unmappedFaces :
            self.warnings.append('Found incomplete UV Set {0} on Mesh {1}'.format(self.mesh, 'map1'))
            # do something with unmappedFaces, i send the list to a custom widget so they can be selected by the user.

    def runAllTests(self):
        self.validateUVs()


mv = MeshValidator('pSphere1')
mv.runAllTests()
print mv.warnings # returns all the warnings

I like to create a type to handle return values, and then pass results and warnings (and even errors, if appropriate) up the chain, allowing higher level code to decide what to do. In this pattern the low level code should probably be exception safe and the higher level code intervenes if something goes wrong. This is a nice match for a generator where you can intervene right after a bad thing happens.


import sys

class SafeResult(object):
    def __init__(self, result, warn = None, err = None):
        self.result = result 
        self.warning = warn
        self.error = err
    
    def valid(self):
        return self.error is None
        
    def __repr__(self):
        if self.valid():
            return "< %s %s>" % (self.result, self.warning or "")
        return "<invalid result>"
           
    
    
def low_level():
    for item in range(20):
        
        try:
            if item == 15:
                raise ValueError("15 forbidden")
            res = item / (item % 4)
            yield SafeResult(res, None, None)
        except ZeroDivisionError:
            yield SafeResult (0, 'zero dvision')
        except:
            yield SafeResult(0, err=sys.exc_info())

for item in low_level(): print item
< 0 zero dvision>
< 1 >
< 1 >
< 1 >
< 0 zero dvision>
< 5 >
< 3 >
< 2 >
< 0 zero dvision>
< 9 >
< 5 >
< 3 >
< 0 zero dvision>
< 13 >
< 7 >
<invalid result>
< 0 zero dvision>
< 17 >
< 9 >
< 6 >

The generator makes it easy to abort out:


for item in low_level(): 
    if item.valid():
        print item
    else:
        break
< 0 zero dvision>
< 1 >
< 1 >
< 1 >
< 0 zero dvision>
< 5 >
< 3 >
< 2 >
< 0 zero dvision>
< 9 >
< 5 >
< 3 >
< 0 zero dvision>
< 13 >
< 7 >

You could of course do this with a dictionary or tuple of results instead of a class:


def low_level_simple():
    for item in range(20):
        
        try:
            yield ( (item / (item % 4), ()  ))
        except:
            yield (None, sys.exc_info())

for item in low_level_simple(): 
    print item

(None, (<type 'exceptions.ZeroDivisionError'>, ZeroDivisionError('integer division or modulo by zero',), <traceback object at 0x0000000044597188>))
(1, ())
(1, ())
(1, ())
(None, (<type 'exceptions.ZeroDivisionError'>, ZeroDivisionError('integer division or modulo by zero',), <traceback object at 0x0000000044597C08>))
(5, ())
(3, ())
(2, ())
(None, (<type 'exceptions.ZeroDivisionError'>, ZeroDivisionError('integer division or modulo by zero',), <traceback object at 0x0000000044597188>))
(9, ())
(5, ())
(3, ())
(None, (<type 'exceptions.ZeroDivisionError'>, ZeroDivisionError('integer division or modulo by zero',), <traceback object at 0x0000000044597C08>))
(13, ())
(7, ())
(5, ())
(None, (<type 'exceptions.ZeroDivisionError'>, ZeroDivisionError('integer division or modulo by zero',), <traceback object at 0x0000000044597188>))
(17, ())
(9, ())
(6, ())

wouldn’t B then have to be a generator too, simply yielding the result of C? My question revolved around a function in between that has no use for the warnings, how would you communicate it back to A from C.

Someone suggested using a logger to add a handler for the duration and just let C log warnings. You could also use a global :confused:

In the specific example yes, B would be a generator too. At that point it’s really a chain-of-responsibility pattern, where each successive level has the opportunity to inspect and possibly alter the result item as it passes up.

This can be very useful if you are applying a lot of rules (say, processing a piece of geometry and checking lots of things like normals, minimum sizes, manifoldness etc. It’s kind of a pain in the ass if you are just processing something simple like the example.

The generator is only important if you want to change the process or possibly bail when something happens. You could replace the generator with returns at each level and do the same processing in loops, it’ll just be wordier and more memory intensive.

Another way to think of it is ‘where does the fault tolerance need to live’. In 99% of cases you want it in the outermost procedures, since they are closer to the user and more likely to know what to do; sometimes, however, they are just ‘noise’ in the process in which case you suppress them at the bottom but record them so higher code can do something smart if smart things become necessary.

I fought this beast not too long ago, with deeply nested widgets communicating with the top-most one for user feedback.

I tried a number of solutions, documented here, but in a nutshell there where three variations

  • Globals (outer-most object passing directly to inner-most)
  • Signals (a compromise)
  • Chain-of-responsibility (every object passing messages along)

Globals

Globals was the most successful attempt initially, as all problems I was having just magically disappeared and the world seemed at peace. However once the infatuation faded, a number of other issues surfaced; mainly about understanding how things worked after some time has passed and I not longer had intricately detailed information about what I did in the past. I think it goes without saying, globals isn’t fit for this problem.

With PubSub implements this solution, and though pubsub seems to be working for many and is a replica of typical message-passing which also has promise, I came to the conclusion that it created more problems that it solved.

Justin Israel contributed this other solution which works in a similar fashion, With Event Filter

Signals

This isn’t to say that signals is always a compromise of globals and locals. In fact, I first used signals as a means of passing signals from one child to its parent, much like chain-of-responsibility. This even worked great at first, but ultimately became quite rigid. The problem occurred whenever a new signal was introduced, at which point I had to return to objects already implemented to also forward this new signal.

A better way was to utilise MVC and build a flat hierarchy of widgets on one side (the view), a central location in which they all communicated with (model), and finally a place where they were handled (controller).

With MVC

Chain-of-responsibility

This made a lot of sense to me and I made two separate implementations of it.

Qt’s Event system works with chain-of-responsibility. All of its events are passed from widget to widget up the parent hierarchy, much more simply than you might expect. So the first example, custom cascading, implements this behaviour into a custom QWidget. The end result is custom events being passed up the chain and finally delivered to the top-most widget.

This did however pose a problem when implementing widgets that relied on subclasses of QWidget, such as QLineEdit, as you would then have to implement the method there too. So the second approach is subclassing QApplication, which seemed the recommended approach. Now, you might think, “why not just use the QApplication directly?” Well, the reason is that custom QEvents simply aren’t propagated. The source code confirms this.

With a subclassed QApplication, QEvents and custom QEvents now acted accordingly and allows for the elegant and implicit chain-of-responsibility normally used in Qt, meaning the inner-most widgets may pass an event to its nearest parent and trust it to pass it along to the grand-parent and so forth, finally catching it within an “event()” method.

This final solution was the one that stuck and I’m now treating all these scenarious this way, subclassing QEvent for every unique event that needs to touch more than one object up a chain and it works quite well.

A more full-blown example has been documented here:

With the custom QApplication here: labs/python/mvc/editable.py at master · abstractfactory/labs · GitHub
One of the custom events here: labs/python/mvc/editable.py at master · abstractfactory/labs · GitHub
And a replacement of a click-event for a custom QEvent here: labs/python/mvc/editable.py at master · abstractfactory/labs · GitHub

Hope it helps.

Another data point to consider: As Mancuso’s post points out, you can create a system which passes non-fatal messages outside the return value stream using an event model. In this version you make everything exception-safe (at least, for well-known and predictable issues) and pass warnings and errors along using event callbacks. There’s some extended discussion and examples here, and the Event class itself is in mGui.

The event based paradigm works best for things like GUIs where there is a hierarchy of long-lived objects. You can manhandle it into a function-oriented system (like the original example), usually be passing callback objects along with other objects. However that tends to increase coupling across the system, so if the use case is more like the example I’d go for complex return types.