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库及源码分析的更多相关文章

  1. python 从SocketServer到 WSGIServer 源码分析、

    python 下有个wsgi的封装库.wsgiref. WSGI 指的是 Web服务器网关接口(Python Web Server Gateway Interface) django的runserve ...

  2. python apschedule安装使用与源码分析

    我们的项目中用apschedule作为核心定时调度模块.所以对apschedule进行了一些调查和源码级的分析. 1.为什么选择apschedule? 听信了一句话,apschedule之于pytho ...

  3. Python之Django rest_Framework框架源码分析

    #!/usr/bin/env python # -*- coding:utf-8 -*- from rest_framework.views import APIView from rest_fram ...

  4. 云风协程库coroutine源码分析

    前言 前段时间研读云风的coroutine库,为了加深印象,做个简单的笔记.不愧是大神,云风只用200行的C代码就实现了一个最简单的协程,代码风格精简,非常适合用来理解协程和用来提升编码能力. 协程简 ...

  5. 文件解析库doctotext源码分析

    doctotext中没有make install选项,make后生成可执行文件 在buile目录下面有.so动态库和头文件,需要的可以从这里面拷贝 build/doctotext就是可执行程序.   ...

  6. Python yield与实现(源码分析 转)

    转自:https://www.cnblogs.com/coder2012/p/4990834.html

  7. Duilib源码分析(一)整体框架

    Duilib界面库是一款由杭州月牙儿网络技术有限公司开发的界面开源库,以viksoe项目下的UiLib库的基础上开发(此后也将对UiLib库进行源码分析):通过XML布局界面,将用户界面和处理逻辑彻底 ...

  8. 【源码分析】cJSON库学习

    cJSON库是什么? cJSON是一个轻量级的json解析库.使用起来非常简单,整个库非常地简洁,核心功能的实现都在cJSON.c文件,非常适合阅读源代码来学习C语言.最近读完这个库的源码,分享自己收 ...

  9. [python] 基于词云的关键词提取:wordcloud的使用、源码分析、中文词云生成和代码重写

    1. 词云简介 词云,又称文字云.标签云,是对文本数据中出现频率较高的“关键词”在视觉上的突出呈现,形成关键词的渲染形成类似云一样的彩色图片,从而一眼就可以领略文本数据的主要表达意思.常见于博客.微博 ...

随机推荐

  1. https过程

    HTTPS在传输数据之前需要客户端(浏览器)与服务端(网站)之间进行一次握手,在握手过程中将确立双方加密传输数据的密码信息.TLS/SSL协议不仅仅是一套加密传输的协议,更是一件经过艺术家精心设计的艺 ...

  2. 【Thinking in java, 4e】复用类

    mark一篇09年的<Thinking in Java>笔记:here --> https://lawrence-zxc.github.io/2009/11/07/thinking- ...

  3. Linux防火墙--iptables学习

    iptables是Linux系统提供的一个强大的防火墙工具,可以实现包过滤.包重定向.NAT转换等功能.iptables是免费的,iptables是一个工具,实际的功能是通过netfilter模块来实 ...

  4. InterruptedException异常

    本文总结自:https://blog.csdn.net/asdfsadfasdfsa/article/details/78808131 什么样的方法会抛出InterruptedException异常? ...

  5. Linux 下的 core dump

    core dump 的基本概念      当一个进程要异常终止时 ,可以选择把进程的用户空间内存数据全部保存到磁盘上 ,文件名通常是 core, 这叫做 Core Dump.通常情况下,core文件会 ...

  6. Win32 API编程:网络编程在设置WSAAsyncSelect模型后connect的返回值问题

    通过WSAAsyncSelect()可以设置非阻塞异步套接字 ::WSAAsyncSelect(s, hDlg, WM_SOCKET, FD_CONNECT | FD_CLOSE | FD_WRITE ...

  7. Linux下挂载windows的共享文件夹

    环境说明: 由于领导要求:现需要将某Linux服务器下的一个文件移动到某windows服务器下(服务器均在机房托管,要远程操作) 由于操作为一次性,则决定在windows下建立一个共享文件夹,linu ...

  8. 《Pro Git》第1章 起步

    关于版本控制 什么是版本控制:记录文件内容变化,将来可查阅特定版本修订情况的系统. 版本控制演进 1)本地版本控制系统 2)集中化的版本控制系统(Centralized Version Control ...

  9. AtCoder Regular Contest 094

    AtCoder Regular Contest 094 C - Same Integers 题意: 给定\(a,b,c\)三个数,可以进行两个操作:1.把一个数+2:2.把任意两个数+1.求最少需要几 ...

  10. 谷歌SEO老域名注册完全攻略

    老域名有优势,不管在百度和谷歌都是一样的. 我们查看搜索结果就能发现,谷歌里面很多排前十的网站都是N年前的,一零年以后的算是比较新的,很多产品网站域名是零几年,甚至很多排名更好的域名是九几年的. 谷歌 ...