在python装饰器中引发异常是一种很好的模式吗?

时间:2015-01-28 19:46:27

标签: python exception error-handling flask

上下文:我为不同的API端点定义了Flask路由,每个端点使用某些参数(uid,project_id等)调用控制器类。

@app.route('/sample/route', methods=['POST'])
@require_json_payload
@require_fields({
    'pid',
    'params'
})
def route_handler(arg1, arg2):
    #input filtering
    ...

    try:
        proj_cntr.sample_method(
            pid         = pid,
            ...         = ...
        )
    except ProjCntrException:
        #handle error

    #response generation
    ...

控制器(proj_cntr)负责确定给定的PID是否有效,是否允许给定用户执行操作,以及其他业务逻辑验证。

我注意到我在不同的控制器中粘贴了很多这样的代码:

if not project_object:
    sys_logger.info('...')
    raise ProjCntrException('PID %d does not exist' % pid)

在装饰器中进行这些检查(验证)似乎是最好的事情。但是,如果验证未通过,我不确定哪种错误处理模式是最佳实践。

1)我应该为每个要装饰的装饰器创建特定的自定义异常(InvalidProjException,PermissionsException等)吗?

关注:调用方法的catch块看起来很臃肿。另外,假设调用者知道被调用者的装饰器会引发什么异常,这是不是很好?

2)装饰器将额外的错误参数传递给方法,该方法决定引发什么异常。通过这种方式,调用方法可以识别预期和处理的异常类型。

关注:方法似乎有点过于设计和混乱。

抱歉这个冗长的问题。非常感谢任何想法/想法。

2 个答案:

答案 0 :(得分:3)

我最终使用装饰器并在其中抛出特定的异常。例如:

@validate_pid装饰器raises InvalidPidException(),它们在任何调用装饰方法的使用者的except块中捕获。

目前的优势:

  • 控制器更清晰,代码复制更少。
  • 相当多功能的解决方案,因为我在我的代码中使用这些“业务逻辑验证”装饰器。

到目前为止的缺点:

  • 装饰器依赖于在调用修饰方法时传递的某些keyname参数,但这些参数中的某些参数不在方法本身中使用。这可能会导致方法签名中出现一些奇怪的“悬空”参数。
  • 我有一些轻微的性能问题。例如:项目验证装饰器初始化会话并加载一个对象(Project),仅在装饰方法本身中再次加载。看起来有点笨拙。

答案 1 :(得分:0)

选项1 :为装饰器创建特定的Exception似乎合乎逻辑,因为这有助于用户知道引发了哪个异常,以及为什么。当然,异常的类型应出现在装饰器文档字符串中,以便调用者能够知道期望什么。这样你就不会做出假设,用户知道引发了什么异常但是他已经阅读了文档 - 这更可能是假设。但是,如果经常调用该方法,则调用者每次都必须处理它。使用库,API或其他人的代码时,这是交易的一部分。

选项2 :它不属于用户以确定将引发哪个异常。例如,用户应该通过文档了解它。在API设计方面,它很混乱,反直觉。

考虑到你的问题,这让我想到了Java的工作方式,特别是如果你使用IDE,在调用方法时你会被IDE和打字机制警告你应该try/catch给定类型的异常。

但是在你的情况下,用特定的信息提出一个合适的HTTPException是不可能的?