Can you wrap an entire view in a Python Try/Except?

This is probably going to b a “no” since I have no recollection of seeing this concept anywhere before.

Consider that I have something like:

  • A re-usable view that contains various elements in a hierarchy.
  • And there are scripts attached to events etc at different levels in that hierarchy
  • And some of those scripts don’t have their own Try/Except blocks
  • And one of these lower scripts causes an exception

Is it possible to wrap the entire view at a top level with code such that the exception bubbles up and is caught there, rather than having the exception be sent straight to the wrapper log? EG I’m thinking about something like having an “onException” event on each object that could include a hierarchy of objects below it.

I’m not sure if this is possible, or even if it is a good idea, but it would allow you to do things like summarize at a higher level that you have an issue in a reusable object, rather than having to decipher details from the stack trace in the wrapper log.

I think try/excepts should be around units of business logic.

I thnk generally speaking its bad practice/frowned upon to do something like

try:
    doesALotOfStuff()
except:
    pass

or even with a logger, this most likely frowned upon (for me at least). It would make parsing/recovering from errors a mess. Imagine you had multiple parts of your view that used dictionaries and could potentially throw a KeyError. Now you would need your own internal mapping system to know oh this key name could corresponds to the dictionary on this button, and this key name corresponds to the dictionary on that property change event, and god help you if you reuse a key name.

AFAIK I don’t think it’s possible to wrap every single script inside a try/except using a built in method.

You could of course make your own wrapper function

def tryAndLog(func, *args, **kwargs):
    try:
        func(*args, **kwargs)
    except Exception, e:
        logger.error(str(e))

or something, and then call every you do via a function through this (think you could use @ wrapper syntax to make it a bit neater as well) but I don’t see the benefit of this over hardcoding the specific errors that any business logic unit is capable of doing. This amount of generalization/abstraction I think actually would make troubleshooting harder. Just my opinion.

1 Like

I would counter that exceptions should be caught at the appropriate level. That could be at a business logic level, or it could be at a component level. It all depends on what you are trying to protect against.

I am looking at this from a programing point of view as if the view hierarchy could be considered like a function call hierarchy with each level able to capture the exceptions thrown by the objects below it.

This doesn't mean you have to capture those events, just that being able to capture them could deliver more flexibility.

I believe that currently all uncaught exceptions go straight to the wrapper log - which in itself can lead to debugging chaos as you can't partition where in your application your uncaught exception is originating from.

I was no way implying I wanted to use "pass" to swallow exptions.

Oooohh, oooh .. I think you just committed a straw man fallacy! :crazy_face:

I agree that there are situations where what I am proposing may not be appropriate, but that doesn't mean there aren't situations that would benefit from it.

I think you may be missing what I am trying to suggest. I am not saying that there should be explicit wrapping of every function, just that having hooks that have the potential to catch exceptions at a higher level would be beneficial rather than having them go straight to the wrapper log.

In addition this would effectively apply to uncaught exceptions. IE things you have totally missed or screwed up. Dealing with specific business errors is the polar opposite of what I am suggesting.

Isn't this counter to the idea in original post of putting the whole view inside a try/except? I agree. I do put try/excepts in property changes when appropriate. I always put as low level as makes sense. This just seems counter to what you were trying to do in your original post unless I misinterpreted it?

I was no way implying I wanted to use “pass” to swallow exptions.

I just wrote that as an example, I did say or log the error immediately afterwards. My major point was that recovering from errors, if you only had one try except on the entire view that caught everything, would be a major headache - not worth the effort.

Oooohh, oooh … I think you just committed a [straw man fallacy]

I am just giving you a real example of a headache you could easily run into with this method. Look here
image

This is the sort of error you would get. Now you have to know in your code what keys belong to what dictionary in order to do any sort of error recovery. I was just giving a single instance of how a specific error thrown that in a singular context (like on button press or property change) inside a try/except that would be easy to handle there (because you would know exactly what caused it - would become much harder to recover from because all you know is that it's from somewhere on your view.

I am not saying that there should be explicit wrapping of every function, just that having hooks that have the potential to catch exceptions at a higher level would be beneficial rather than having them go straight to the wrapper log.

I think under the hood this would just be wrapping of some sort fwiw.

I still think it makes more sense to catch uncaught exceptions on the level they are on because you may also want to handle them differently in different scenarios. Surely you do want to log them all, but in certain scenarios an uncaught exception might want you to also abort the current process you are in the middle of for instance.

I think you should write for expected and unexpected errors in the same code block, not just code for the expected type and then have the unexpected bubble up to the thing you are suggesting caught all unexpected errors.

The way I tend to code my business logic is like

# I throw this from validation functions when examining if a data is ok
class FormError(Exception)
    pass

unexpectedError=False
expectedErrror=False
try:
    createRecord(data)
except FormError, e:
    # show user nicely what they did wrong
    expectedErrror=True
except java.lang.Exception, e:
    if "Duplicate record" in str(e.cause): 
        # Tell user unique key violated
        expectedErrror=True
    elif 'cannot be null' in str(e.cause):
        # tell user column cannot be blank
        expectedErrror=True
    else:
        # Unexpected db error - log and set to True
        unexpectedError = True
except Exception, e:
    # Unexpected python error, log and set to True
    unexpectedError= True
finally:
    if unexpectedError:
       # Might need to short circuit or reset something etc.
    elif expectedError:
          # All went well or was recoverable, do something here if needed
    else
         # Everything went ok without expected or unexpected errors - success

So to me then it seems like you just want

except Exception, e:
    # Unexpected python error
    logger.info()

This part as a catch all to happen on any part of your view.

I think part of the issue with this too can be observed from my java.lang.Exception part - there are java.lang.Exceptions that I do expect and can handle, and there are ones I do not and can't handle.

The same is probably true of most Error types you encounter like IOError could just be a folder can't be found and I can handle that by prompting the user, or it could be something else that is unexpected and I don't have a way to handle.

I think you might just want to change how you write your code a bit. A big part of writing in python is it is better to try and ask for forgiveness.

With this in mind, the way I write code is to write if for instance it was to check some data and run an insert query into multiple places, is to write one function that does everything linearly in the perfect case scenario, and then another function that does the try and except.

def createRecord(data):
    if data['moneyAmount'] < 0:
        raise FormError("Money amount can't be less than zero")
    if data['someOtherKey'] == 1:
        data['modifiedKey'] = 'newValue'
    system.db.runNamedQuery("someForm", data)

def callingFunction(data):
    try:
        createRecord(data)
    except FormError, e:
       expectedError=True
        # do something with this
    except KeyError, e: 
       # Do something else with this
    except java.Lang.Exception, e:
       # do something iwth this
    except Exception, e:
        # log and abort
    finally:
        # Clean up /reset if neede based on if you got a expected or unexpected error

Yep, I think you are totally missing my use case . While yes I am trying to put an entire view inside a try/except, the use case is that views themselves are embeddable in other views. EG right now I am working on a system that has about 6 different cononical views that encapsulate a specific behavior in each view type - and each of these views is comprised of several different objects, and have various scripts scattered around them.

But the overall system is multiple high level views where I have embedded several hundred instances of each canonical view type, and the behavior is controlled by parameters on the embedded views. What I want to do is to contain the uncaught exception reporting to within each canonical view and stop it from spreading to a higher higher level.

By containing the reporting at the embedded view level, I can now report "Canonical view type X, embedded in View Y had an uncaught exception Z", rather than a load of "uncaught exception Z" messages in the wrapper log.

1 Like

Fundamentally, views are not code. They are configurations in a domain-specific language, a DSL that isn’t jython. (And I would guess not Turing complete, either.)

I applaud thinking outside the box like this, but I can’t imagine how your desire can be satisfied.

3 Likes

Ok I do understand your use case a bit more. I get the intuition as to why you would want this now and I think it’s a fair thing to want.

I think the only way you are able to get this

“Canonical view type X, embedded in View Y had an uncaught exception Z”

is to in your embedded views, do try/except and inside your except raise your own error with more specific information. Normally I am against doing a raise inside an except but given that you are getting unhelpful error messages currently I get why you want this.

I know it’s not exactly what you want but you could do

# These would really be from parameters/props on the view
canoncial = 'Type X'
view= 'View Y'

try:
    something()
except Exception, e:
    errMsg = '%s %s %s'%(canoncial, view, str(e))
    raise Exception(errMsg)

inside your embedded views.

Probably a better way to do this but I think its a fair first draft so at least when you see the error message in your logs, you know what embedded view is causing it and where.

Sorry if I sounded lecturer-y before, I was having a argument with a coworker over try/excepts earlier and I was running hot lol.

I know that views are not code. On the other hand views do have event hooks where code attaches. I suppose I am proposing a modification to the exception handling in Jython whereby a exception doesn't get posted to the wrapper log until you have traversed up the object tree and not encountered a non-empty Except hook

No need to apologize about being lecture-y, sometimes it can be hard to get complex ideas across in forums. I mean, I know exactly in my head what I am thinking about - so you should too based on whatever minimal information I posted!!! :rofl:

What I am also trying to avoid is to force having try/except at every location I have a custom jython script, and having those except blocks each independently fill in the summary information. That smells like an anti-pattern to me (at the very least it’s YAGNI). Plus it defats the purpose of having a top level except block that can catch my stupidities without me having to consider the entire world.

1 Like

If you formalize exactly how you want this to work you can make a feature request. Just show how it works now and what you would like to work instead so the good folks at IA don’t get confused like I did lol.

1 Like

I can see the impression of that as an end user, but from a backend perspective, there's no relationship between events/event handlers in this way. I also don't see any backwards compatible way to do this...

It would have to be something like a new event handler type (onException(error)) that we'd go through, finding and calling handlers backwards appropriately. While it's not my area (and you're certainly welcome to make the feature, it's a neat idea) I personally don't think it would be broadly useful enough to justify the added complexity (both for us and for you as developers).

I only see the complexity on your side :smiley: Like all hooks on the user side, we don’t need to define them until we use them :wink: