Python装饰器针对avatar部分解析

Python装饰器

  装饰器太难了,实在是看不懂,所以要写一篇对装饰器的详解,直接上Halucinator和Avatar的装饰器部分的代码来进行解释,如果需要知道装饰器的基础的话建议直接百度装饰器,本篇主要用于理解Halucinator和Avatar2。

# Halucinator——stm32f4_uart.py
@bp_handler(['HAL_UART_Transmit', 'HAL_UART_Transmit_IT', 'HAL_UART_Transmit_DMA'])
def handle_tx(self, qemu, bp_addr):
    '''
        Reads the frame out of the emulated device, returns it and an 
        id for the interface(id used if there are multiple ethernet devices)
    '''
    huart = qemu.get_arg(0)
    hw_addr = qemu.read_memory(huart, 4, 1)
    buf_addr = qemu.get_arg(1)
    buf_len = qemu.get_arg(2)
    data = qemu.read_memory(buf_addr, 1, buf_len, raw=True)
    hal_log.info("UART %i TX:%s" % (hw_addr, data))
    self.model.write(hw_addr, data)
    return True, 0

在这个函数里他调用了get_arg函数好几次,查看它的定义会找到代码是这么写的


def get_arg(self, idx):
    '''
        Gets the value for a function argument (zero indexed)
        :param idx  The argument index to return
        :returns    Argument value
    '''
    if idx >= 0 and idx < 4:
        return self.read_register("r%i" % idx)
    elif idx >= 4:
        sp = self.read_register("sp")
        stack_addr = sp + (idx-4) * 4
        return self.read_memory(stack_addr, 4, 1)
    else:
        raise ValueError("Invalid arg index")

  所以通过这个函数就能知道,最关键的函数在read_register部分。但要是想找read_register的话,其定义就只在avatar2中了,现在去看看avatar2里搜一下

  所有定义过read_register的都是在一些不是直接挂钩的py文件里,Halucinator直接调用的QemuTarget里却并没有写入这个函数的内容,可是直接看QemuTarget类的声明方式的话会发现其最根本的在这,类继承的问题。


class QemuTarget(Target):
    """"""
    def __init__(
        self,
        avatar,
        ...

  我们首先需要知道类的概念,对于类,只要学过C++都能知道它是用来干什么的,而在python中,类也有一样的功能,Python的新建类如果继承了之前的类的话,那么它就可以调用上一个类中的函数,在QemuTarget类中,其继承了Target类,所以他能够调用Target中的所有已定义过的函数,这也就是为什么Halucinator能够直接使用read_register的原因。同时可以再看一下Target中定义的read_register本身,可以看见它对函数的定义:

@watch('TargetRegisterRead')
#@action_valid_decorator_factory(TargetStates.STOPPED, 'registers')
def read_register(self, register):
    """
    Reading a register from the target

    :param register:     The name of the register
    :return:             The actual value read from the register
    """
    return self.protocols.registers.read_register(register)

在这我们要开始讲一下watch函数!(终于要开始讲装饰器了嘛呜呜呜)
我们看一下watch函数中的内容(watchmen.py)对于该函数的解释大部分已经在注释中了

def watch(watched_type):
    """
    Decorator for the watchmen system
    """

    def decorator(func):
        @wraps(func)
        def watchtrigger(self, *args, **kwargs):
            # To avoid circular dependencies, we import here ...
            from .avatar2 import Avatar
            from .targets.target import Target

            cb_kwargs = dict(kwargs) #将多个参数转换成字典格式
            if isinstance(self, Avatar): #检查调用该函数的类是否是Avatar类
                avatar = self
            elif isinstance(self, Target): #如果是Target类则赋一个值
                avatar = self.avatar
                cb_kwargs['watched_target'] = self #朝字典中添加一个新的键值,{'watched_target': 'Target'}

            avatar.watchmen.t(watched_type, BEFORE, *args, **cb_kwargs) #Trigger函数部分,后面会讲
            ret = func(self, *args, **kwargs) #运行函数本身
            cb_kwargs.update({'watched_return': ret}) #给字典添加键值对
            cb_ret = avatar.watchmen.t(watched_type, AFTER, *args, **cb_kwargs) #Trigger函数部分

            return ret if cb_ret is None else cb_ret

        return watchtrigger

    return decorator

watchmen是一定要讲的,因为在Avatar中它放在了根目录就说明这个函数是由自己的用处的,并不是一点作用没有,当然不可否认这个主要功能还是做其他函数的装饰器的功能,可是因为本文是对装饰器的详解(笑),那么就要深层挖掘装饰器内容。
那么我们就看watchmen类中对Trigger函数的描述吧

def trigger(self, watch_type, when, *args, **kwargs):
    ret = None
    for watchman in self._watched_events[watch_type]:
        if watchman.when == when:
            if watchman.overwrite_return:
                ret = watchman.react(self._avatar, *args, **kwargs)
                kwargs.update({'watched_return': ret})
            else:
                watchman.react(self._avatar, *args, **kwargs)
    return ret

该函数最主要的功能就是查看在运行函数之前/之后是否需要执行前置步骤,如果不需要的话那就直接返回None,在read_register中是不需要额外的步骤的,所以就没有Trigger的使用必要,那么在这个里面是否是全部都不需要Trigger呢?当然不是,有一些函数是需要运行Trigger的,下面这个就是一个需要Trigger的例子。

#target_wait.py
a.watchmen.add('TargetWait', when='before', callback=delay)

尽管目前我不知道这些有什么用,但是在这三四个月总会知道的诶嘿~☆
所以它最后read_register就会到相关协议的定义函数上,下一篇就会对每一个寄存器、断点等具体函数做详解。

posted @ 2021-09-22 16:36  喵内桑  阅读(219)  评论(0)    收藏  举报