【妙用协程】 – 可resume的异常处理
admin
2023-07-31 01:53:11
0

标准的异常处理是这样的

try:
    print(\'hello\')
    raise Exception()
    print(\'!!!\')
except:
    print(\'world\')
    print(\'???\')

这段代码会打印出???而不会打印出!!!,因为异常会中断当前流程,跳转到except部分去继续执行。但是有的时候我们希望的这样的行为:

try:
    print(\'hello\')
    print(Scheduler.interrupt())
    print(\'!!!\')
except ProcessInterrupt as pi:
    pi.resume(\'world\')
    print(\'???\')

这段代码打印出!!!而不是???,因为resume的时候把执行重新跳转回interrupt的地方了。这种行为类似vba里的on error resume next(https://msdn.microsoft.com/en-us/library/5hsw66as.aspx)。

如何实现的?其实原理上很简单。interrupt的时候把当前协程的状态保存起来(pickle.dumps),如果决定要resume,就把协程interrupt的时刻的状态重新恢复(pickle.loads)然后从那个点继续执行。

完整的代码(需要pypy或者stackless python):

import greenlet
import cPickle as pickle
import traceback
import threading
import functools


class ProcessInterrupt(Exception):
    def __init__(self, interruption_point, pi_args):
        self.interruption_point = interruption_point
        self.stacktrace = traceback.extract_stack()
        self.pi_args = pi_args

    def resume(self, resume_with=None):
        Scheduler.resume(self.interruption_point, resume_with)

    def __repr__(self):
        return \'>>>ProcessInterrupt>>>%s\' % repr(self.stacktrace)

    def __str__(self):
        return repr(self)

    def __unicode__(self):
        return repr(self)


class Scheduler(object):
    current = threading.local()

    def __init__(self):

        if getattr(self.current, \'instance\', None):
            raise Exception(\'can not have two scheduler in one thread\')
        self.scheduler_greenlet = greenlet.getcurrent()
        self.current.instance = self

    def __call__(self, action, action_args):
        next = action, action_args
        while next:
            action, action_args = next
            if \'init\' == action:
                next = action_args[\'init_greenlet\'].switch()
            elif \'interrupt\' == action:
                interruption_point = pickle.dumps(action_args[\'switched_from\'])
                should_resume, resume_with = False, None
                next = action_args[\'switched_from\'].switch(
                    should_resume, resume_with, interruption_point)
            elif \'resume\' == action:
                should_resume, resume_with, interruption_point = True, action_args[\'resume_with\'], action_args[
                    \'interruption_point\']
                next = pickle.loads(action_args[\'interruption_point\']).switch(
                    should_resume, resume_with, interruption_point)
            else:
                raise NotImplementedError(\'unknown action: %s\' % action)

    @classmethod
    def start(cls, init_func, *args, **kwargs):
        scheduler = Scheduler()
        init_greenlet = greenlet.greenlet(functools.partial(init_func, *args, **kwargs))
        scheduler(\'init\', {
            \'init_greenlet\': init_greenlet,
        })

    @classmethod
    def interrupt(cls, pi_args=None):
        should_resume, resume_with, interruption_point = cls.switch_to_scheduler(\'interrupt\', {
            \'switched_from\': greenlet.getcurrent()
        })
        if should_resume:
            return resume_with
        else:
            pi = ProcessInterrupt(interruption_point, pi_args)
            raise pi

    @classmethod
    def resume(cls, interruption_point, resume_with=None):
        cls.switch_to_scheduler(\'resume\', {
            \'interruption_point\': interruption_point,
            \'resume_with\': resume_with
        })

    @classmethod
    def switch_to_scheduler(cls, *args, **kwargs):
        return cls.current.instance.scheduler_greenlet.switch(*args, **kwargs)


if \'__main__\' == __name__:
    def init():
        try:
            print(\'hello\')
            print(Scheduler.interrupt())
            print(\'!!!\')
        except ProcessInterrupt as pi:
            pi.resume(\'world\')
            print(\'???\')

        try:
            print(\'hello\')
            raise Exception()
            print(\'!!!\')
        except:
            print(\'world\')
            print(\'???\')

    Scheduler.start(init)

上一篇:WSGI简介

下一篇:Python 多进程实践

相关内容

热门资讯

500 行 Python 代码... 语法分析器描述了一个句子的语法结构,用来帮助其他的应用进行推理。自然语言引入了很多意外的歧义,以我们...
定时清理删除C:\Progra... C:\Program Files (x86)下面很多scoped_dir开头的文件夹 写个批处理 定...
Mobi、epub格式电子书如... 在wps里全局设置里有一个文件关联,打开,勾选电子书文件选项就可以了。
65536是2的几次方 计算2... 65536是2的16次方:65536=2⁶ 65536是256的2次方:65536=256 6553...
scoped_dir32_70... 一台虚拟机C盘总是莫名奇妙的空间用完,导致很多软件没法再运行。经过仔细检查发现是C:\Program...
小程序支付时提示:appid和... [Q]小程序支付时提示:appid和mch_id不匹配 [A]小程序和微信支付没有进行关联,访问“小...
pycparser 是一个用... `pycparser` 是一个用 Python 编写的 C 语言解析器。它可以用来解析 C 代码并构...
微信小程序使用slider实现... 众所周知哈,微信小程序里面的音频播放是没有进度条的,但最近有个项目呢,客户要求音频要有进度条控制,所...
python查找阿姆斯特朗数 题目解释 如果一个n位正整数等于其各位数字的n次方之和,则称该数为阿姆斯特朗数。 例如1^3 + 5...
Apache Doris 2.... 亲爱的社区小伙伴们,我们很高兴地向大家宣布,Apache Doris 2.0.0 版本已于...