Python之contextlib库及源码分析
Utilities for
with
-statement contexts
__all__ = ["contextmanager", "closing", "AbstractContextManager",
"ContextDecorator", "ExitStack", "redirect_stdout",
"redirect_stderr", "suppress"]
AbstractContextManager(abc.ABC)
上下文管理抽象类,子类必须实现__enter__(self)、__exit__(self)
class AbstractContextManager(abc.ABC): """An abstract base class for context managers.""" def __enter__(self):
"""Return `self` upon entering the runtime context."""
return self @abc.abstractmethod
def __exit__(self, exc_type, exc_value, traceback):
"""Raise any exception triggered within the runtime context."""
return None @classmethod
def __subclasshook__(cls, C):
if cls is AbstractContextManager:
if (any("__enter__" in B.__dict__ for B in C.__mro__) and
any("__exit__" in B.__dict__ for B in C.__mro__)):
return True
return NotImplemented
ContextDecorator(object)
上下文管理基类或mixin类,该类可以像装饰器一样工作,提供你需要实现的任何辅助功能
class ContextDecorator(object):
"A base class or mixin that enables context managers to work as decorators." def _recreate_cm(self):
"""Return a recreated instance of self. Allows an otherwise one-shot context manager like
_GeneratorContextManager to support use as
a decorator via implicit recreation. This is a private interface just for _GeneratorContextManager.
See issue #11647 for details.
"""
return self def __call__(self, func):
@wraps(func)
def inner(*args, **kwds):
with self._recreate_cm():
return func(*args, **kwds)
return inner
_GeneratorContextManager(ContextDecorator, AbstractContextManager)
contextmanager装饰器的包装函数提供以下方法:
_recreate_cm(重新生成新对像),self.__class__(*args, **kwds)
__enter__上下文管理进入函数
__exit__上下文管理退出函数
可以根据ContextDecorator实现任何想实现的辅助功能
class _GeneratorContextManager(ContextDecorator, AbstractContextManager):
"""Helper for @contextmanager decorator.""" def __init__(self, func, args, kwds):
self.gen = func(*args, **kwds)
logger.info("get generator by with:{}".format(self.gen))
self.func, self.args, self.kwds = func, args, kwds
# Issue 19330: ensure context manager instances have good docstrings
doc = getattr(func, "__doc__", None) #得到函数文档,第三个参数为默认参数
logger.info("doc:{}".format(doc))
if doc is None:
doc = type(self).__doc__
self.__doc__ = doc
# Unfortunately, this still doesn't provide good help output when
# inspecting the created context manager instances, since pydoc
# currently bypasses the instance docstring and shows the docstring
# for the class instead.
# See http://bugs.python.org/issue19404 for more details. def _recreate_cm(self):
# _GCM instances are one-shot context managers, so the
# CM must be recreated each time a decorated function is
# called
return self.__class__(self.func, self.args, self.kwds) def __enter__(self):
logger.info("__enter__:you can add you method") @ContextDecorator()
def testfun(*args, **kwds):
logger.info("@ContextDecorator():testfun test success")
testfun("hello") try:
return next(self.gen)
except StopIteration:
raise RuntimeError("generator didn't yield") from None def __exit__(self, type, value, traceback):
logger.info("__exit__")
logger.info("type:{}".format(type))
if type is None:
try:
next(self.gen)
except StopIteration:
return
else:
raise RuntimeError("generator didn't stop")
else:
if value is None:
# Need to force instantiation so we can reliably
# tell if we get the same exception back
value = type()
try:
self.gen.throw(type, value, traceback)
raise RuntimeError("generator didn't stop after throw()")
except StopIteration as exc:
# Suppress StopIteration *unless* it's the same exception that
# was passed to throw(). This prevents a StopIteration
# raised inside the "with" statement from being suppressed.
return exc is not value
except RuntimeError as exc:
# Don't re-raise the passed in exception. (issue27112)
if exc is value:
return False
# Likewise, avoid suppressing if a StopIteration exception
# was passed to throw() and later wrapped into a RuntimeError
# (see PEP 479).
if exc.__cause__ is value:
return False
raise
except:
# only re-raise if it's *not* the exception that was
# passed to throw(), because __exit__() must not raise
# an exception unless __exit__() itself failed. But throw()
# has to raise the exception to signal propagation, so this
# fixes the impedance mismatch between the throw() protocol
# and the __exit__() protocol.
#
if sys.exc_info()[1] is not value:
raise
装饰器contextmanager
def contextmanager(func):
"""@contextmanager decorator. Typical usage: @contextmanager
def some_generator(<arguments>):
<setup>
try:
yield <value>
finally:
<cleanup> This makes this: with some_generator(<arguments>) as <variable>:
<body> equivalent to this: <setup>
try:
<variable> = <value>
<body>
finally:
<cleanup> """
@wraps(func)
def helper(*args, **kwds):
return _GeneratorContextManager(func, args, kwds)
return helper
用例:
#coding = utf-8 import abc
from functools import wraps import logging
logging.basicConfig(level=logging.INFO, filename="logging.txt", filemode="w+", \
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__) class AbstractContextManager(abc.ABC): """An abstract base class for context managers.""" def __enter__(self):
"""Return `self` upon entering the runtime context."""
return self @abc.abstractmethod
def __exit__(self, exc_type, exc_value, traceback):
"""Raise any exception triggered within the runtime context."""
return None @classmethod
def __subclasshook__(cls, C):
if cls is AbstractContextManager:
if (any("__enter__" in B.__dict__ for B in C.__mro__) and
any("__exit__" in B.__dict__ for B in C.__mro__)):
return True
return NotImplemented class ContextDecorator(object):
"A base class or mixin that enables context managers to work as decorators." def _recreate_cm(self):
"""Return a recreated instance of self. Allows an otherwise one-shot context manager like
_GeneratorContextManager to support use as
a decorator via implicit recreation. This is a private interface just for _GeneratorContextManager.
See issue #11647 for details.
"""
return self def __call__(self, func):
#logger.info("ContextDecorator func:{}".format(func))
@wraps(func)
def inner(*args, **kwds):
#with self._recreate_cm():
logger.info("you can do something in decorator")
return func(*args, **kwds)
return inner class _GeneratorContextManager(ContextDecorator, AbstractContextManager):
"""Helper for @contextmanager decorator.""" def __init__(self, func, args, kwds):
self.gen = func(*args, **kwds)
logger.info("get generator by with:{}".format(self.gen))
self.func, self.args, self.kwds = func, args, kwds
# Issue 19330: ensure context manager instances have good docstrings
doc = getattr(func, "__doc__", None) #得到函数文档,第三个参数为默认参数
logger.info("doc:{}".format(doc))
if doc is None:
doc = type(self).__doc__
self.__doc__ = doc
# Unfortunately, this still doesn't provide good help output when
# inspecting the created context manager instances, since pydoc
# currently bypasses the instance docstring and shows the docstring
# for the class instead.
# See http://bugs.python.org/issue19404 for more details. def _recreate_cm(self):
# _GCM instances are one-shot context managers, so the
# CM must be recreated each time a decorated function is
# called
return self.__class__(self.func, self.args, self.kwds) def __enter__(self):
logger.info("__enter__:you can add you method") @ContextDecorator()
def testfun(*args, **kwds):
logger.info("@ContextDecorator():testfun test success")
testfun("hello") try:
return next(self.gen)
except StopIteration:
raise RuntimeError("generator didn't yield") from None def __exit__(self, type, value, traceback):
logger.info("__exit__")
logger.info("type:{}".format(type))
if type is None:
try:
next(self.gen)
except StopIteration:
return
else:
raise RuntimeError("generator didn't stop")
else:
if value is None:
# Need to force instantiation so we can reliably
# tell if we get the same exception back
value = type()
try:
self.gen.throw(type, value, traceback)
raise RuntimeError("generator didn't stop after throw()")
except StopIteration as exc:
# Suppress StopIteration *unless* it's the same exception that
# was passed to throw(). This prevents a StopIteration
# raised inside the "with" statement from being suppressed.
return exc is not value
except RuntimeError as exc:
# Don't re-raise the passed in exception. (issue27112)
if exc is value:
return False
# Likewise, avoid suppressing if a StopIteration exception
# was passed to throw() and later wrapped into a RuntimeError
# (see PEP 479).
if exc.__cause__ is value:
return False
raise
except:
# only re-raise if it's *not* the exception that was
# passed to throw(), because __exit__() must not raise
# an exception unless __exit__() itself failed. But throw()
# has to raise the exception to signal propagation, so this
# fixes the impedance mismatch between the throw() protocol
# and the __exit__() protocol.
#
if sys.exc_info()[1] is not value:
raise def contextmanager(func):
"""@contextmanager decorator. Typical usage: @contextmanager
def some_generator(<arguments>):
<setup>
try:
yield <value>
finally:
<cleanup> This makes this: with some_generator(<arguments>) as <variable>:
<body> equivalent to this: <setup>
try:
<variable> = <value>
<body>
finally:
<cleanup> """
@wraps(func)
def helper(*args, **kwds):
return _GeneratorContextManager(func, args, kwds)
return helper @contextmanager
def file_open(path):
''' file open test'''
try:
f_obj = open(path,"w")
yield f_obj
except OSError:
print("We had an error!")
finally:
print("Closing file")
f_obj.close() if __name__ == "__main__":
with file_open("contextlibtest.txt") as fobj:
fobj.write("Testing context managers")
logger.info("write file success")
关键输出:
2018-03-22 15:36:43,249 - __main__ - INFO - get generator by with:<generator object file_open at 0x01DE4870>
2018-03-22 15:36:43,249 - __main__ - INFO - doc: file open test
2018-03-22 15:36:43,249 - __main__ - INFO - __enter__:you can add you method
2018-03-22 15:36:43,249 - __main__ - INFO - you can do something in decorator
2018-03-22 15:36:43,249 - __main__ - INFO - @ContextDecorator():testfun test success
2018-03-22 15:36:43,249 - __main__ - INFO - write file success
2018-03-22 15:36:43,249 - __main__ - INFO - __exit__
2018-03-22 15:36:43,249 - __main__ - INFO - type:None
Python之contextlib库及源码分析的更多相关文章
- python 从SocketServer到 WSGIServer 源码分析、
python 下有个wsgi的封装库.wsgiref. WSGI 指的是 Web服务器网关接口(Python Web Server Gateway Interface) django的runserve ...
- python apschedule安装使用与源码分析
我们的项目中用apschedule作为核心定时调度模块.所以对apschedule进行了一些调查和源码级的分析. 1.为什么选择apschedule? 听信了一句话,apschedule之于pytho ...
- Python之Django rest_Framework框架源码分析
#!/usr/bin/env python # -*- coding:utf-8 -*- from rest_framework.views import APIView from rest_fram ...
- 云风协程库coroutine源码分析
前言 前段时间研读云风的coroutine库,为了加深印象,做个简单的笔记.不愧是大神,云风只用200行的C代码就实现了一个最简单的协程,代码风格精简,非常适合用来理解协程和用来提升编码能力. 协程简 ...
- 文件解析库doctotext源码分析
doctotext中没有make install选项,make后生成可执行文件 在buile目录下面有.so动态库和头文件,需要的可以从这里面拷贝 build/doctotext就是可执行程序. ...
- Python yield与实现(源码分析 转)
转自:https://www.cnblogs.com/coder2012/p/4990834.html
- Duilib源码分析(一)整体框架
Duilib界面库是一款由杭州月牙儿网络技术有限公司开发的界面开源库,以viksoe项目下的UiLib库的基础上开发(此后也将对UiLib库进行源码分析):通过XML布局界面,将用户界面和处理逻辑彻底 ...
- 【源码分析】cJSON库学习
cJSON库是什么? cJSON是一个轻量级的json解析库.使用起来非常简单,整个库非常地简洁,核心功能的实现都在cJSON.c文件,非常适合阅读源代码来学习C语言.最近读完这个库的源码,分享自己收 ...
- [python] 基于词云的关键词提取:wordcloud的使用、源码分析、中文词云生成和代码重写
1. 词云简介 词云,又称文字云.标签云,是对文本数据中出现频率较高的“关键词”在视觉上的突出呈现,形成关键词的渲染形成类似云一样的彩色图片,从而一眼就可以领略文本数据的主要表达意思.常见于博客.微博 ...
随机推荐
- poj2996
/*排序函数要写对,优先级:K,Q,R,B,N,P 白色的:如果优先级一样,那么按照行数大的优先,如果行数一样,那么列数小的优先 黑色的:如果优先级一样,那么按照行数小的优先,如果行数一样,那么列数小 ...
- [Android]自定义控件LoadMoreRecyclerView
RecyclerView是加强版的ListView,用于在有限的窗口中展示大量的数据,而LoadMoreRecyclerView则是为RecyclerView增加了加载更多的功能,先来看效果: 三种加 ...
- PAT1077. Kuchiguse (20)
#include <iostream> #include <vector> #include <sstream> using namespace std; int ...
- Elasticsearch 配置优化
cluster.routing.allocation.same_shard.host:true 这会防止同一个shard的主副本存在同一个物理机上(因为如果存在一个机器上,副本的高可用性就没有了). ...
- 理解Java中字符流与字节流的区别(转)
1. 什么是流 Java中的流是对字节序列的抽象,我们可以想象有一个水管,只不过现在流动在水管中的不再是水,而是字节序列.和水流一样,Java中的流也具有一个“流动的方向”,通常可以从中读入一个字节序 ...
- 指定library路径
1.执行 ?.jar文件: 1.1.“java -jar ?.jar” 1.2.如果 ?.jar里面使用了JNI调用了 ?.dll/?.so 等文件,可能会报错 找不到相关的 库文件,如果这样的话,可 ...
- JNI_Z_06_方法的操作(没有String类型的参数)_父类的同名方法
1.关键在于: 使用的 method id 是 子类的 还是 父类的,而 父类methodID的获取 必须使用 父类的class对象. 2.VC6(CPP)的DLL代码: #include<st ...
- Python jieba 分词
环境 Anaconda3 Python 3.6, Window 64bit 目的 利用 jieba 进行分词,关键词提取 代码 # -*- coding: utf-8 -*- import jieba ...
- easyui datagrid 行编辑功能
datagrid现在具有行编辑能力了,使用时只须在columns中为需要编辑的列添加一个editor属性,编辑保存时同时具有数据校验能力. 看一个例子效果图: 代码如下: $('#tt').datag ...
- 四十二 Python分布式爬虫打造搜索引擎Scrapy精讲—elasticsearch(搜索引擎)的mget和bulk批量操作
注意:前面讲到的各种操作都是一次http请求操作一条数据,如果想要操作多条数据就会产生多次请求,所以就有了mget和bulk批量操作,mget和bulk批量操作是一次请求可以操作多条数据 1.mget ...