参考:

https://www.youtube.com/watch?v=8kTlzR4HhWo

https://github.com/miguelgrinberg/merry

背景

本文实际就是把 doc 翻译了下, 笔记用, 建议直接到 github 看源码, 并不复杂

撸码的时候就发现, 异常处理会很大的影响程序的观感, 本不复杂的业务逻辑嵌套在异常处理中, 不够优美, 就想到把异常处理的逻辑搁到装饰器中

用装饰器很爽, 但是又发现, 几乎每个IO函数都顶着一个装饰器, 确实不雅观, 想了想觉得可以让异常向上传递, 只在较高的几个函数上使用装饰器

然后程序就简洁多了, 不过内心还有疑虑, 这是正确的方向么? 是不是走的太过了. YouTube 上狂搜 python error handing, 有所得.

演讲者 miguelgrin berg, flask 入门教程的作者, 很惊喜的发现, 自己居然跟他的想法差不多, 更重要的是, berg 提供了 merry 这个工具来实践它.

是不是有点太过了?

不过是异常处理而已, 好好的 try except 就够了吧? 其他人不也没这么写么? 偏偏就要用装饰器?

在 youtube 的评论里,作者提到 merry.logger is a Python logging object. It comes standard with Python. See https://docs.python.org/3/library/logging.html.

看来是个大腿, 可抱!

开始

merry 的目的是将异常处理与业务逻辑分离, 举个栗子

def write_to_file(filename, data):
try:
with open(filename, 'w') as f:
f.write(data)
except IOError:
print('Error: can't write to file')
except Exception as e:
print('Unexpected error: ' + str(e)) write_to_file('some_file', 'some_data')

很简单, 我们只想写个文件而已, 然后在 try except 的层峦叠嶂中, 整个代码成了一坨

merry 通过将异常处理拆分到其他函数中, 实现了异常处理和业务逻辑的分离

from merry import Merry

merry = Merry()

@merry._try
def write_to_file(filename, data):
with open(filename, 'w') as f:
f.write(data) @merry._except(IOError)
def ioerror():
print('Error: can't write to file') @merry._except(Exception)
def catch_all(e):
print('Unexpected error: ' + str(e) write_to_file('some_file', 'some_data')

现在看看新的 write_to_file 是多么清爽, 不过没有银弹, 代价是这个版本确实多写了很多代码.

然后就要问了, 如果我还有个读文件的函数, 那么一个 merry 够用么? 显然不够用, 然后要创建多个 Merry() 实例, 写更多的代码.

访问异常对象 Exception Object

被装饰的异常处理函数可选接受一个参数, 这个参数就是 exception object

@merry._except(Exception)
def catch_all(e):
print('Unexpected error: ' + str(e)

Else 和 Finally

@merry._else
def else_clause():
print('No exceptions where raised!') @merry._finally
def finally_clause():
print('Clean up time!')

Return Values 装饰后的返回值

Returning values from functions protected with the try decorator or the corresponding exceptelse and finally handlers follow certain rules that try to implement a behavior similar to a Python try/except:

  • The value returned by a function decorated with the try decorator is normally returned to the caller.
  • If there is an exception, then the value returned by the except handler that matches the exception is returned to the caller.
  • The else handler only runs if the try function does not raise an exception and returns None
  • If the try function returns None and there is an else handler, then its return value is given to the caller.
  • If there is a finally handler and it returns a value that is not None, then this value takes the place of any other returned value.

函数被 merry 提供的装饰器装饰之后的返回值, 会尽量的模仿正常的 try except.

  • merry._try 装饰的函数, 返回值会正常返回给调用者
  • 如果发生异常, 捕获到异常的处理函数的返回值会返回给调用者
  • merry._else 装饰的函数, 当且仅当 merr._try 装饰的函数没有产生异常且返回值为 None 才会执行 (注意! 与 try -except - else - finally 的定义不同了这里)
  • 如果  merry._finally 装饰的函数返回值不为 None, 那么这个值会替代其他的返回值返回

Passing context to error handlers 向异常处理函数传递上下文

有时异常处理函数需要访问 try 中的变量, 但是由于已经拆分成不同的函数, 这时候就需要 merry.g 来存储这些变量. 对 Flask 是真爱啊.

@merry._try
def app_logic():
db = open_database()
merry.g.database = db # save it in the error context just in case
# do database stuff here @merry._except(Exception)
def catch_all():
db = getattr(merry.g, 'database', None)
if db is not None and is_database_open(db):
close_database(db)
print('Unexpected error, quitting')
sys.exit(1)

Debug 模式

可以将异常信息全部向上传递到顶层, 也可以对某个异常梳理函数设置不继续传递, 举个栗子

import logging
from merry import Merry logger = logging.getLogger(__name__)
logger.addHandler(logging.StreamHandler()) merry = Merry(logger_name=__name__, debug=True) @merry._try
def foo():
logger.error("error information in foo")
raise IOError("IOError occurs in foo") @merry._except(IOError)
def ioerror():
# will nerver run in debug mode
logger.error("IOError has occured in foo") @merry._except(Exception)
def catch_all(e):
logger.exception("Unexcepted Error: {}".format(e)) foo() Output: # 正常输出
error information in foo # merry 自带的异常捕获
[merry] Exception caught
Traceback (most recent call last):
File "/home/work/warthog/venv/lib/python2.7/site-packages/merry.py", line 26, in wrapper
ret = f(*args, **kwargs)
File "opoos.py", line 12, in foo
raise IOError("IOError occurs in foo")
IOError: IOError occurs in foo # 在 ioerror 处再次抛出异常, 注意到 ioerror 中的代码在 debug 模式下是不会执行的
Traceback (most recent call last):
File "opoos.py", line 22, in <module>
foo()
File "/home/work/warthog/venv/lib/python2.7/site-packages/merry.py", line 55, in wrapper
raise e
IOError: IOError occurs in foo

Logging

merry 中自带一个 logger, 默认名字叫 "merry", Hello, merry.

使用方式为 merry.logger. 初始化时参数 logger_name 可以指定 logger 名称 Merry(logger_name="foo").

merry.logger 默认不带 handlers, 可以按需自行添加.

python merry -- error handling in the real world的更多相关文章

  1. python urllib2 error handling

    python 2 里面的下载实现. https://stackoverflow.com/questions/666022/what-errors-exceptions-do-i-need-to-han ...

  2. beam 的异常处理 Error Handling Elements in Apache Beam Pipelines

    Error Handling Elements in Apache Beam Pipelines Vallery LanceyFollow Mar 15 I have noticed a defici ...

  3. Erlang error handling

    Erlang error handling Contents Preface try-catch Process link Erlang-way error handling OTP supervis ...

  4. MySQL Error Handling in Stored Procedures 2

    Summary: this tutorial shows you how to use MySQL handler to handle exceptions or errors encountered ...

  5. setjmp()、longjmp() Linux Exception Handling/Error Handling、no-local goto

    目录 . 应用场景 . Use Case Code Analysis . 和setjmp.longjmp有关的glibc and eglibc 2.5, 2.7, 2.13 - Buffer Over ...

  6. Error Handling

    Use Exceptions Rather Than Return Codes Back in the distant past there were many languages that didn ...

  7. Python pip – error: invalid command ‘bdist_wheel’

    原文@http://software-engineer.gatsbylee.com/python-pip-error-invalid-command-bdist_wheel/ Python pip – ...

  8. Error Handling and Exception

    The default error handling in PHP is very simple.An error message with filename, line number and a m ...

  9. Clean Code–Chapter 7 Error Handling

    Error handling is important, but if it obscures logic, it's wrong. Use Exceptions Rather Than Return ...

随机推荐

  1. Android开发之XUtils框架使用和报错处理

    一.XUtils  lib的的添加: 1.点击+,选择第一个Library dependency 2.输入XUtils 按enter键,搜索: 3.然后就是选择XUtils,选择哪个版本就看个人了,接 ...

  2. WebView·开发指南

    WebView·开车指南 作者:凌俊琦链接:https://zhuanlan.zhihu.com/p/22247021来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处. T ...

  3. 别再抱怨了,国内这么多优秀的Android资源你都知道吗?

    因为一些大家都知道的原因,android很多官方出品的优秀开发资源在国内无法访问. 国内的同行们对此也做出了很多努力,有很多朋友通过各种手段把很多优秀的资源搬运到了国内,为国内android开发者提供 ...

  4. 【Alpha版本】十天冲刺——日志集合贴

    No Bug 031402401鲍亮 031402402曹鑫杰 031402403常松 031402412林淋 031402418汪培侨 031402426许秋鑫 Day1 Day2 Day3 Day ...

  5. poj 1390 Blocks

    poj 1390 Blocks 题意 一排带有颜色的砖块,每一个可以消除相同颜色的砖块,,每一次可以到块数k的平方分数.问怎么消能使分数最大.. 题解 此题在徐源盛<对一类动态规划问题的研究&g ...

  6. 静态关键字static(2)

    static关键字主要有两种作用: 第一,为某特定数据类型或对象分配单一的存储空间,而与创建对象的个数无关. 第二,实现某个方法或属性与类而不是对象关联在一起 具体而言,在Java语言中,static ...

  7. 创建16x16二级hash目录

    Hash目录是一种优化文件存储性能的方法.无论是Windows还是Linux,无论是NTFS还是ext4,每个目录下所能容纳的项目数是有限的.并不是不能保存,而是当项目数量过大的时候,会降低文件索引速 ...

  8. SDL简介(网络汇总)

    摄像头视频播放采用sdl,下面简单介绍下.不保证正确及网址永远有效.后面文章采用tao框架http://sourceforge.net/projects/taoframework/      SDL. ...

  9. C# 中的委托和事件(转)

    引言 委托 和 事件在 .Net Framework中的应用非常广泛,然而,较好地理解委托和事件对很多接触C#时间不长的人来说并不容易.它们就像是一道槛儿,过了这个槛的人,觉得真是太容易了,而没有过去 ...

  10. 循环中的continue功能

    在oracle存储过程中,有时我们希望在循环中为某种情况时不做任何处理,类似于c语言中的continue,跳过本次循环:在oracle 11g中好像增加了这个关键字,在之前版本中我们可以通过如下方式来 ...