
On 23Jun2017 11:48, Nick Coghlan <ncoghlan@gmail.com> wrote:
On 23 June 2017 at 09:29, Cameron Simpson <cs@zip.com.au> wrote:
This is so common that I actually keep around a special hack:
def prop(func): ''' The builtin @property decorator lets internal AttributeErrors escape. While that can support properties that appear to exist conditionally, in practice this is almost never what I want, and it masks deeper errors. Hence this wrapper for @property that transmutes internal AttributeErrors into RuntimeErrors. ''' def wrapper(*a, **kw): try: return func(*a, **kw) except AttributeError as e: e2 = RuntimeError("inner function %s raised %s" % (func, e)) if sys.version_info[0] >= 3: try: eval('raise e2 from e', globals(), locals()) except: # FIXME: why does this raise a SyntaxError? raise e else: raise e2 return property(wrapper)
Slight tangent, but I do sometimes wonder if adding a decorator factory like the following to functools might be useful:
def raise_when_returned(return_exc): def decorator(f): @wraps(f) def wrapper(*args, **kwds): try: result = f(*args, **kwds) except selective_exc as unexpected_exc: msg = "inner function {} raised {}".format(f, unexpected_exc) raise RuntimeError(msg) from unexpected_exc if isinstance(result, return_exc): raise result return result
It's essentially a generalisation of PEP 479 to arbitrary exception types, since it lets you mark a particular exception type as being communicated back to the wrapper via the return channel rather than as a regular exception:
def with_traceback(exc): try: raise exc except BaseException as caught_exc: return caught_exc
@property @raise_when_returned(AttributeError) def target(self): if len(self.targets) == 1: return self.targets[0] return with_traceback(AttributeError('only exists when this has exactly one target'))
Funnily enough I have an @transmute decorator which serves just this purpose. It doesn't see as much use as I might imagine, but that is partially because my function predates "raise ... from", which meant that it loses the stack trace from the transmuted exception, impeding debugging. I need to revisit it with that in mind. So yes, your proposed decorator has supporting real world use cases in my world. Cheers, Cameron Simpson <cs@zip.com.au>