Django调试

django默认情况会将所有的错误信息以HTML形式返回给前端,这样导致在运行nose的单元测试用例出现错误时,无法看到详细的错误栈帧信息,给程序的debug带来一定的困扰。

如何debug django异常栈帧?

方法一:

配置settings.py,将django的所有的debug信息,错误栈帧信息输出到终端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console': {
'class': 'logging.StreamHandler',
},
},
'loggers': {
'django': {
'handlers': ['console'],
'level': os.getenv('DJANGO_LOG_LEVEL', 'INFO'),
},
},
}

连接信号got_request_exception

1
2
3
4
5
6
7
from django.core.signals import got_request_exception

def exc_cb(sender, **kwargs):
import traceback
traceback.print_exc()

got_request_exception.connect(exc_cb)

方法二: 通过middleware,在process_exception中增加exception的调试信息,并将此middleware增加到settings.pyMIDDLEWARE_CLASSES中。

1
2
3
4
5
import traceback

class LogMiddleware(object):
def process_exception(self, request, exception):
traceback.print_exc()

注: 在中间件process_exception方法中打印异常栈帧,只能debug view函数的异常。 如果异常发生在中间件中,无法打印异常信息。

参考

Django中间件

中间件是django处理request/response钩子的框架。它是一个用来修改输入、输出的轻量级的插件系统。 从另外角度上讲,中间件也是一种特殊的“装饰器”,装饰所有的视图函数。

版本说明

django中间件新版本与旧版本不兼容, 本文档是基于django 1.10编写。

新版本中间件

1
2
3
4
5
6
7
class NewMiddlewareCls(object):
def __init__(self, get_response):
self.get_response = get_response

def __call__(self, request):
response = self.get_response(request)
return response

旧版本中间件

1
2
class OldMiddlewarCls(object):
def __init__(self): pass

框架

中间件示例

1
2
3
4
5
6
7
8
9
10
11
from django.utils.depreaction import MiddlewareMixin

class ExampleMiddleware(MiddlewareMixin):
def process_request(self, request):
return None

def process_exceptions(self, request, exception):
return None # or return HttpResponse()

def process_response(self, request, response):
return response

加载中间件

WSGIHandler在初始化时加载中配置文件中的中间件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class BaseHandler(object):
...
def load_middleware(self):
if settings.MIDDLEWARE is None:
...
else:
handler = convert_exception_to_response(self._get_response)
for middleware_path in reversed(settings.MIDDLEWARE):
middleware = import_string(middleware_path)
try:
mw_instance = middleware(handler)
except MiddlewareNotUsed as exc:
if settings.DEBUG:
if six.text_type(exc):
logger.debug('MiddlewareNotUsed(%r): %s', middleware_path, exc)
else:
logger.debug('MiddlewareNotUsed: %r', middleware_path)
continue

if mw_instance is None:
raise ImproperlyConfigured(
'Middleware factory %s returned None.' % middleware_path
)

if hasattr(mw_instance, 'process_view'):
self._view_middleware.insert(0, mw_instance.process_view)
if hasattr(mw_instance, 'process_template_response'):
self._template_response_middleware.append(mw_instance.process_template_response)
if hasattr(mw_instance, 'process_exception'):
self._exception_middleware.append(mw_instance.process_exception)

handler = convert_exception_to_response(mw_instance)
class WSGIHandler(base.BaseHandler):
request_class = WSGIRequest

def __init__(self, *args, **kwargs):
super(WSGIHandler, self).__init__(*args, **kwargs)
self.load_middleware()

运行中间件

收到客户端发起的一个请求,调用所有注册的中间件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
class WSGIHandler(base.BaseHandler):
...
def __call__(self, environ, start_response):
...
response = self.get_response(request)
...
...


class BaseHandler(object):
...
def get_response(self, request):
"""Return an HttpResponse object for the given HttpRequest."""
# Setup default url resolver for this thread
set_urlconf(settings.ROOT_URLCONF)

response = self._middleware_chain(request)

# This block is only needed for legacy MIDDLEWARE_CLASSES; if
# MIDDLEWARE is used, self._response_middleware will be empty.
try:
# Apply response middleware, regardless of the response
for middleware_method in self._response_middleware:
response = middleware_method(request, response)
# Complain if the response middleware returned None (a common error).
if response is None:
raise ValueError(
"%s.process_response didn't return an "
"HttpResponse object. It returned None instead."
% (middleware_method.__self__.__class__.__name__))
except Exception: # Any exception should be gathered and handled
signals.got_request_exception.send(sender=self.__class__, request=request)
response = self.handle_uncaught_exception(request, get_resolver(get_urlconf()), sys.exc_info())

response._closable_objects.append(request)

# If the exception handler returns a TemplateResponse that has not
# been rendered, force it to be rendered.
if not getattr(response, 'is_rendered', True) and callable(getattr(response, 'render', None)):
response = response.render()

if response.status_code == 404:
logger.warning(
'Not Found: %s', request.path,
extra={'status_code': 404, 'request': request},
)

return response

中间件chain

所有的中间件通过MiddlewareMixin链接起来,形成middleware_chain,参考设计模式 chain of responsibility职责链

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class MiddlewareMixin(object):
def __init__(self, get_response=None):
self.get_response = get_response
super(MiddlewareMixin, self).__init__()

def __call__(self, request):
response = None
if hasattr(self, 'process_request'):
response = self.process_request(request)
if not response:
response = self.get_response(request)
if hasattr(self, 'process_response'):
response = self.process_response(request, response)
return response

新版本middleware处理时序:

img

旧版版本middleware处理时序:
img

其他

如果process_request返回结果为不None, 则不再迭代调用下一个层中间件(或视图函数)。

中间件处理分为几个阶段:

  • process_request
  • process_view
  • process_exception
  • process_template_response
  • process_exception
  • process_response

参考

Python Functools

functools.wraps()

wraps(wrapped[,assigned][,updated])
常用装饰器函数中返回的wrapper()函数,解决了被装饰函数namedoc等签名丢失问题。

1
2
3
4
5
6
>>> import functools
>>> def decorator(f):
@functools.wrap(f)
def wrapper(*args, **kwargs):
return f(*args, **kwargs)
return wrapper

functools.partial()

partial(func[,*args][,**kwargs])
是一个装饰器函数,也是一个闭包,返回一个可调用对象,freeze一些参数。

实现原理

1
2
3
4
5
6
7
8
9
def partial(func, *args, **keywords):
def newfunc(*fargs, **fkeywords):
newkeywords = keywords.copy()
newkeywords.update(fkeywords)
return func(*(args + fargs), **newkeywords)
newfunc.func = func
newfunc.args = args
newfunc.keywords = keywords
return newfunc

References

Python装饰器

装饰器是一种设计模型(结构型模式),可以动态给一个对象添加一些额外的职责,而不用修改该对象的任何code。

比如,我们要给一个API增加权限认证,可以通过auth_decorator()装饰这个API,而不必修改每个API的代码;debug一个函数的耗时,可以实现一个time_decorator()装饰要这些函数,而不用修改这些函数的内部实现;给个TextView()增加滚动条的装饰器ScrollDecorator();Flask使用route()装饰器进行路由的注册等等。

装饰器优点:

  • 比静态继承更灵活。与静态继承相比,装饰器可以灵活向对象添加额外的责任
  • 避免在层次结构高层的类有特多的特征。 可以定义一个简单的类,通过装饰器给他逐渐添加功能。

基本装饰器

Python从语法本身就支持装饰器,Python装饰器是一个可调用对象(比如,函数、类),接受一个函数对象作为输入,返回另外一个函数对象。

最简单的Python函数装饰器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
>>> def decorator(f):
def wrapper(*args, **kwargs):
print('before call %s' % f.__name__)
result = f(*args, **kwargs)
print('after call %s' % f.__name__)
return result
return wrapper

>>> @decorator
def func(*args, **kwargs):
print('call func')


>>> func()
before call func
call func
after call func

函数装饰器

函数装饰器,接受一个函数f作为输入,返回另外一个函数对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
'''
function decorator
'''
def time_cost_decorator(f):
def wrapper(*args, **kwargs):
start_time = time.time()
rv = f(*args, **kwargs)
end_time = time.time()
delta = end_time - start_time
print('time cost: %ds' % delta)
return rv
return wrapper

@time_cost_decorator
def sleep_10s_func(*args, **kwargs):
time.sleep(10)

>>> sleep_10s_func()
time cost: 10s

使用类实现函数装饰器,在对象初始化时接受一个函数f作为输入,在模拟调用__call__特殊方法中调用f函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# class decorator
class TimeCostDecorator(object):
def __init__(self, f):
self._f = f

def __call__(self, *args, **kwargs):
start_time = time.time()
ret = self._f(*args, **kwargs)
end_time = time.time()
delta = end_time - start_time
print('time cost %ds' % delta)
return ret


@TimeCostDecorator
def sleep_5s_func():
time.sleep(5)

类装饰器

类装饰器的与函数装饰器语法非常类似。类装饰器接受cls作为输入,返回另外一个cls。

类装饰器可以用来管理管理类,类实例的创建。

函数实现类装饰器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
>>> def class_decorator(cls):
class ClassWrapper(object):
def __init__(self, *args, **kwargs):
self._ins = cls(*args, **kwargs)
def __getattr__(self, name):
print('call ClassWapper.__getattr__ func')
return getattr(self._ins, name)
return ClassWrapper

>>> @class_decorator
class Foo(object):
def func(self):
print('call Foo.func')


>>> Foo().func()
call ClassWapper.__getattr__ func
call Foo.func

类实现类装饰器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class ClassDecorator(object):
def __init__(self, cls):
self._cls = cls
def __call__(self, *args, **kwargs):
self._ins = self._cls(*args, **kwargs)
return self
def __getattr__(self, name):
print('call ClassDecorator.__getattr__')
return getattr(self._ins, name)


>>> @ClassDecorator
class Foo(object):
def func(self):
print('call Foo.func func')


>>> Foo().func()
call ClassDecorator.__getattr__
call Foo.func func
>>>

装饰器进阶

带参数装饰器

不仅被装饰的函数可以携带参数,装饰器函数也可以携带参数。

Decorator params imply three levels of callables: a callable to accept decorator arguments, which return a callable to serve a callable to serve as decorator, which return a callbale to handle calls to the origin function or class. Each of the three levels may be a function or class and may retain state in the form of scopes or class attributes.

函数实现的带参数的函数装饰器:

以最近写的权限验证为例,描述带参数的装饰器实现

1
2
3
4
5
6
>>> def perm_required(perm, **options):
def decorator(f):
def wrapper(*args, **kwargs):
return f(*args, **kwargs)
return wrapper
return decoraor

类实现的带参数的装饰器:

TODO

函数实现的带参数类装饰器:

TODO

类实现的带参数类装饰器:

TODO

函数签名

functools.wraps() 保留被封装函数的签名,如__module__, __name__, __doc__

以下函数装饰器,被装饰的函数的签名被覆盖

1
2
3
4
5
6
7
8
9
10
11
>>> def decorator(f):
def wrapper(*args, **kwargs):
return f(*args, **kwargs)
return wrapper

>>> @decorator
def func(): pass

>>> func.__name__
'wrapper'
>>>

调用functools.wraps()之后

1
2
3
4
5
6
7
8
9
10
11
12
>>> def decorator(f):
@functools.wraps(f)
def wrapper(*args, **kwargs):
return f(*args, **kwargs)
return wrapper

>>>
>>> @decorator
def func(): pass

>>> func.__name__
'func'

wraps()函数的定义

1
2
3
WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__doc__')
WRAPPER_UPDATES = ('__dict__')
def wraps(wrapped, assign=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES):pass

嵌套装饰器

有时一个装饰器不能满足需求,这时,我们可以添加多个装饰器(decorator nesting)。

以下两个装饰器函数分别实现如下功能,scroll_decorator()添加滚动条,border_decorator()添加边框

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
>>> def border_decorator(f):
def wrapper(*args, **kwargs):
print('border wrapper')
return f(*args, **kwargs)
return wrapper

>>> def scroll_decorator(f):
def wrapper(*args, **kwargs):
print('scroll decorator')
return f(*args, **kwargs)
return wrapper

>>>
>>> @border_decorator
@scroll_decorator
def decorated_func(*args, **kwargs):
print('call decorated func')


>>>
>>> decorated_func()
border wrapper
scroll decorator
call decorated func
>>>

以上装饰器从语法与以下等价

1
2
3
4
5
6
7
8
>>> def decorated_func(*args, **kwargs):
print('decorated func')

>>> f = border_decorator(scroll_decorator(decorated_func))
>>> f()
border wrapper
scroll decorator
decorated func

装饰器的其他特性

装饰器与闭包

TODO

装饰器与描述符

TODO

装饰器使用场景

统计API的调用时间

TODO

单例设计模式

TODO

跟踪方法调用

TODO

其他用例

  • Flask 路由管理
  • Django model 事务管理
  • Tornado 异步框架

参考

读书与不读书的区别

没有养成读书习惯的人,以时间和空间而言,是受他眼前的世界所禁锢的。

他的生活是机械化的,刻板的。他只是跟几个朋友和相识者接触谈话,他只看到周遭发生的事情。他在这个监狱里是逃不出去的。

可是当他拿起一本书的时候,他立刻走进一个不同的世界。如果是一本好书,他便立刻接触到世界上一个最健谈的人。

这个谈话者引导他前进,带他到一个不同的国度或不同的时代,或者对他发泄一些私人的悔恨,或者跟他讨论一些他从来不知道的学问或生活问题。

摘自:《林语堂: 读书人和不读书的人,最大的区别是什么》

Python PEP

PEP是Python Enehencement Proposal的缩写.每篇PEP都是一个设计文档,或者描述Python社区信息,或者描述Python的新特性等.PEP提供了特性的技术规范和这个特性的来源.

Python常见的PEP,比如PEP8 Python代码风格指南、PEP333 Python Web服务网关接口(WSGI)、PEP20 Python之禅、PEP248 Python 数据库接口规范等。

八达岭长城

骑行百里以前是躺在愿望单里了,现在已经成为一个现实,一段难忘的经历。

两个快奔三的人还跟个愣头青一样骑着自行车去长城,是逃离城市放松一下,抑或是证明自己还年轻,抑或是其他,答案未知。只知道去时,充满激情,一路狂奔(山路阶段艰难骑行);回来时,身体很疲惫,但心灵得到了某种不知名的慰藉。

马八条

马化腾的8条论纲,也称“马八条”.

一、互联网即将走出其历史的“三峡时刻”,激情会更多,力量会更大。
任何一个新鲜的工具出现的时候总会引起社会的惊讶,以及很多关注,并且风靡一时。这个过程就好像长江三峡一样一路险滩,在这个阶段过去之后,新鲜感逐渐消失。但是,这推动了社会结构的重塑,创新力量将会排山倒海般到来。这个转折的标志就是每一个公民都能够熟练使用互联网这个工具。

二、客户端将不再重要,产业上游的价值将重新崛起。
回顾过去,很多人认为腾讯获取成功就是因为有了一个QQ客户端软件。我们能够非常便捷的接触到用户,手中有很多用户推什么产品都可以成功,这实际上是一个渠道,我们能够轻易的通过这个渠道去接触到用户。但是在未来我们感觉这个趋势,或者说这种故事将不再存在。简单来说,价值链在互联网产业链中正在往上游转移。也就是说,如果未来人们只依靠你的客户端,那这个企业将会步入一个重大危机。

三、“垄断”是一个令人烦恼的罪名,但有的时候确实是一个假象的罪名。
很多所谓的垄断公司,实际上在产业不断变革的时候,仍然面临着很大的危机。也就是说,在价值变迁迅速的产业里,没有一个公司是可以高枕无忧的。所以说,挑战阿里巴巴、百度和腾讯,有人说是三座大山,有效的方法不是建立一个类似的平台,形成一个垄断,而是顺应而上形成一个好的产业链,这才是一个好的方法。

四、截杀渠道者仅仅是“刺客”,占据源头者才是“革命者”。
互联网将不再作为一个独立的产业而存在,它将融入传统的产业之中。在互联网的作用下,产业链的上游将会变得越来越重要。也就是说,你拥有什么样的产品和服务是最重要的,而不是你拥有一个什么的渠道。外界一直对腾讯有一个误解,说我们的核心价值就是QQ,有渠道。其实,我们在很早之前就意识到这个是不可持续的。所以,我们就权利打造产业链的源头,也就是说你要有很好的优秀产品和服务,以及应用。

五、广告模式是“产品经济”的产物,知识产权模式是“体验经济”的宠儿。
过去的产品经济时代,产品和注意力是分离的,也就是说销售产品时为了获取知名度和名誉度不得不到媒体那购买注意力,这个就是广告的本质。我们现在看到产品经济逐渐演化的体验经济的时候,独特的体验将成为所有产业的一个价格源头,这也为产业增值打开了一个无穷空间。在产品经济时代,媒体内容是一个独立产业,也就是为广告提供一个载体。那么,在体验经济时代,媒体内容将会全方位的融入到其他产业中,成为一个价值的源头。

六、不要被“免费”吓到。拥有“稀缺性”,就拥有了破解免费魔咒的武器。
制造稀缺性的方法有三个。第一,要有一个长期的大量品牌投资。第二,要营造一个独特的体验,比如苹果的iPhone,通过整合的方式把很多技术整合在一起创造出一个非常好的独特体验。其中它的每个一个技术在其他的厂商看来都不是什么高端的技术,关键是把它整合成一个体验,这就是稀缺性。第三,塑造明星。

七、产品经济束缚人,互联网经济将解放人。
互联网的使命之一就是改造传统的物本经济,把人从组织束缚中解救出来。也就是说,在互联网未来的世界力,拥有独特魅力和独立的人会成为最终源头,会成为最终赢家。聚合更多的个人价值,为更多人的自我实现提供平台,把个性魅力和创新的潜力凝聚成巨大的商业价值,是未来互联网的用武之地,也是腾讯公司的愿景之一。只有把人的价值释放出来,产业才会产生,稳定的社会结构才会出现,这也是互联网应该能做出的贡献。

八、在“云组织”时代,“伟公司”不见得是“大公司”。
“云”是未来社会的形态,是社会资源的一种聚合方式,也就是说平时一水分子形态存在的,需要的整合的时候,一旦时机成熟就会形成“云”,任务完成之后又四散而去。这样的组织形式也许是未来互联网的一种常态。腾讯公司眼里的开放和共享,简单来说就是以释放人的价值为着眼点,以个人资源为立足点,以云组织来凝聚,以云创新来推动。

此文章摘自王晓波的《腾讯传》