On 23Jun2017 11:48, Nick Coghlan <ncogh...@gmail.com> wrote:
On 23 June 2017 at 09:29, Cameron Simpson <c...@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 <c...@zip.com.au>
_______________________________________________
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to