问题导读
1、如何学习OpenStack的with语法?
2、当执行到With其后的对象时,就返回该对象的什么?
在OpenStack的源码中经常会看到一个语法:with,如下面的函数实现的是释放已经分配给实例的网络端口,其中就用到了with。
- def _try_deallocate_network(self, context, instance,
- requested_networks=None):
- try:
- # tear down allocated network structure
- self._deallocate_network(context, instance, requested_networks)
- except Exception:
- with excutils.save_and_reraise_exception():
- LOG.error(_('Failed to deallocate network for instance.'),
- instance=instance)
- self._set_instance_error_state(context, instance['uuid'])
复制代码
在Exception下面有with excutils.save_and_reraise_exception(),从字面意思来看excutils.save_and_reraise_exception()很容易理解,
就是保存并重新抛出一个异常,这个功能的作用是,当有异常发生时,先针对这个异常进行一些必要的处理,而不是马上
向上抛出异常,即先保存异常,执行完下面的语句后,再抛出exception。
- LOG.error(_('Failed to deallocate network for instance.'),
- instance=instance)
- self._set_instance_error_state(context, instance['uuid'])
- span style="white-space:pre"> </span>#在日志中记录一个错误信息:Failed to deallocate network for instance;设置实例的状态为error
复制代码
那么这个功能是怎么实现的呢? 我们可以在nova/openstack/common/excutils.py中找到实现它的源码,注释也很清楚的描述了
这个类的功能。
- class save_and_reraise_exception(object):
- """Save current exception, run some code and then re-raise.
-
- In some cases the exception context can be cleared, resulting in None
- being attempted to be re-raised after an exception handler is run. This
- can happen when eventlet switches greenthreads or when running an
- exception handler, code raises and catches an exception. In both
- cases the exception context will be cleared.
-
- To work around this, we save the exception state, run handler code, and
- then re-raise the original exception. If another exception occurs, the
- saved exception is logged and the new exception is re-raised.
-
- In some cases the caller may not want to re-raise the exception, and
- for those circumstances this context provides a reraise flag that
- can be used to suppress the exception. For example:
-
- except Exception:
- with save_and_reraise_exception() as ctxt:
- decide_if_need_reraise()
- if not should_be_reraised:
- ctxt.reraise = False
- """
- def .__init__(self):
- self.reraise = True
-
- def __enter__(self):
- # 进入这个类做的处理,exc_info()获取当前正在处理的异常类的信息,是一个tuple: (type, value/message, traceback),参见[1]
- self.type_, self.value, self.tb, = sys.exc_info()
- return self
-
- def __exit__(self, exc_type, exc_val, exc_tb):
- #从这个类出去时的动作,如果发生异常,则输出异常并返回False,
- #如果reraise为True, 则重新抛出异常
- if exc_type is not None:
- logging.error(_('Original exception being dropped: %s'),
- traceback.format_exception(self.type_,
- self.value,
- self.tb))
- return False
- if self.reraise:
- #six是一个为python2和python3提供兼容性的库,six.reraise参见[2]
- six.reraise(self.type_, self.value, self.tb)
复制代码
那么with在上述例子中扮演了一个什么角色呢?原来当执行到With其后的对象时,就返回该对象的__enter__(),当with后面的代码执行完毕后,
再返回该对象的__exit__(),参见[3],从而实现了先保存异常,做清理工作,再抛出异常这样的功能。
|