Utilities for with-statement contexts
  __all__ = ["contextmanager", "closing", "AbstractContextManager",
               "ContextDecorator", "ExitStack", "redirect_stdout",
               "redirect_stderr", "suppress"]

AbstractContextManager(abc.ABC)

  上下文管理抽象类,子类必须实现__enter__(self)、__exit__(self)

  1. class AbstractContextManager(abc.ABC):
  2.  
  3. """An abstract base class for context managers."""
  4.  
  5. def __enter__(self):
  6. """Return `self` upon entering the runtime context."""
  7. return self
  8.  
  9. @abc.abstractmethod
  10. def __exit__(self, exc_type, exc_value, traceback):
  11. """Raise any exception triggered within the runtime context."""
  12. return None
  13.  
  14. @classmethod
  15. def __subclasshook__(cls, C):
  16. if cls is AbstractContextManager:
  17. if (any("__enter__" in B.__dict__ for B in C.__mro__) and
  18. any("__exit__" in B.__dict__ for B in C.__mro__)):
  19. return True
  20. return NotImplemented

ContextDecorator(object)

  上下文管理基类或mixin类,该类可以像装饰器一样工作,提供你需要实现的任何辅助功能

  1. class ContextDecorator(object):
  2. "A base class or mixin that enables context managers to work as decorators."
  3.  
  4. def _recreate_cm(self):
  5. """Return a recreated instance of self.
  6.  
  7. Allows an otherwise one-shot context manager like
  8. _GeneratorContextManager to support use as
  9. a decorator via implicit recreation.
  10.  
  11. This is a private interface just for _GeneratorContextManager.
  12. See issue #11647 for details.
  13. """
  14. return self
  15.  
  16. def __call__(self, func):
  17. @wraps(func)
  18. def inner(*args, **kwds):
  19. with self._recreate_cm():
  20. return func(*args, **kwds)
  21. return inner

_GeneratorContextManager(ContextDecorator, AbstractContextManager)

  contextmanager装饰器的包装函数提供以下方法:

  _recreate_cm(重新生成新对像),self.__class__(*args, **kwds)

  __enter__上下文管理进入函数

  __exit__上下文管理退出函数

  可以根据ContextDecorator实现任何想实现的辅助功能

  1. class _GeneratorContextManager(ContextDecorator, AbstractContextManager):
  2. """Helper for @contextmanager decorator."""
  3.  
  4. def __init__(self, func, args, kwds):
  5. self.gen = func(*args, **kwds)
  6. logger.info("get generator by with:{}".format(self.gen))
  7. self.func, self.args, self.kwds = func, args, kwds
  8. # Issue 19330: ensure context manager instances have good docstrings
  9. doc = getattr(func, "__doc__", None) #得到函数文档,第三个参数为默认参数
  10. logger.info("doc:{}".format(doc))
  11. if doc is None:
  12. doc = type(self).__doc__
  13. self.__doc__ = doc
  14. # Unfortunately, this still doesn't provide good help output when
  15. # inspecting the created context manager instances, since pydoc
  16. # currently bypasses the instance docstring and shows the docstring
  17. # for the class instead.
  18. # See http://bugs.python.org/issue19404 for more details.
  19.  
  20. def _recreate_cm(self):
  21. # _GCM instances are one-shot context managers, so the
  22. # CM must be recreated each time a decorated function is
  23. # called
  24. return self.__class__(self.func, self.args, self.kwds)
  25.  
  26. def __enter__(self):
  27. logger.info("__enter__:you can add you method")
  28.  
  29. @ContextDecorator()
  30. def testfun(*args, **kwds):
  31. logger.info("@ContextDecorator():testfun test success")
  32. testfun("hello")
  33.  
  34. try:
  35. return next(self.gen)
  36. except StopIteration:
  37. raise RuntimeError("generator didn't yield") from None
  38.  
  39. def __exit__(self, type, value, traceback):
  40. logger.info("__exit__")
  41. logger.info("type:{}".format(type))
  42. if type is None:
  43. try:
  44. next(self.gen)
  45. except StopIteration:
  46. return
  47. else:
  48. raise RuntimeError("generator didn't stop")
  49. else:
  50. if value is None:
  51. # Need to force instantiation so we can reliably
  52. # tell if we get the same exception back
  53. value = type()
  54. try:
  55. self.gen.throw(type, value, traceback)
  56. raise RuntimeError("generator didn't stop after throw()")
  57. except StopIteration as exc:
  58. # Suppress StopIteration *unless* it's the same exception that
  59. # was passed to throw(). This prevents a StopIteration
  60. # raised inside the "with" statement from being suppressed.
  61. return exc is not value
  62. except RuntimeError as exc:
  63. # Don't re-raise the passed in exception. (issue27112)
  64. if exc is value:
  65. return False
  66. # Likewise, avoid suppressing if a StopIteration exception
  67. # was passed to throw() and later wrapped into a RuntimeError
  68. # (see PEP 479).
  69. if exc.__cause__ is value:
  70. return False
  71. raise
  72. except:
  73. # only re-raise if it's *not* the exception that was
  74. # passed to throw(), because __exit__() must not raise
  75. # an exception unless __exit__() itself failed. But throw()
  76. # has to raise the exception to signal propagation, so this
  77. # fixes the impedance mismatch between the throw() protocol
  78. # and the __exit__() protocol.
  79. #
  80. if sys.exc_info()[1] is not value:
  81. raise

装饰器contextmanager

  1. def contextmanager(func):
  2. """@contextmanager decorator.
  3.  
  4. Typical usage:
  5.  
  6. @contextmanager
  7. def some_generator(<arguments>):
  8. <setup>
  9. try:
  10. yield <value>
  11. finally:
  12. <cleanup>
  13.  
  14. This makes this:
  15.  
  16. with some_generator(<arguments>) as <variable>:
  17. <body>
  18.  
  19. equivalent to this:
  20.  
  21. <setup>
  22. try:
  23. <variable> = <value>
  24. <body>
  25. finally:
  26. <cleanup>
  27.  
  28. """
  29. @wraps(func)
  30. def helper(*args, **kwds):
  31. return _GeneratorContextManager(func, args, kwds)
  32. return helper

用例:

  1. #coding = utf-8
  2.  
  3. import abc
  4. from functools import wraps
  5.  
  6. import logging
  7. logging.basicConfig(level=logging.INFO, filename="logging.txt", filemode="w+", \
  8. format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
  9. logger = logging.getLogger(__name__)
  10.  
  11. class AbstractContextManager(abc.ABC):
  12.  
  13. """An abstract base class for context managers."""
  14.  
  15. def __enter__(self):
  16. """Return `self` upon entering the runtime context."""
  17. return self
  18.  
  19. @abc.abstractmethod
  20. def __exit__(self, exc_type, exc_value, traceback):
  21. """Raise any exception triggered within the runtime context."""
  22. return None
  23.  
  24. @classmethod
  25. def __subclasshook__(cls, C):
  26. if cls is AbstractContextManager:
  27. if (any("__enter__" in B.__dict__ for B in C.__mro__) and
  28. any("__exit__" in B.__dict__ for B in C.__mro__)):
  29. return True
  30. return NotImplemented
  31.  
  32. class ContextDecorator(object):
  33. "A base class or mixin that enables context managers to work as decorators."
  34.  
  35. def _recreate_cm(self):
  36. """Return a recreated instance of self.
  37.  
  38. Allows an otherwise one-shot context manager like
  39. _GeneratorContextManager to support use as
  40. a decorator via implicit recreation.
  41.  
  42. This is a private interface just for _GeneratorContextManager.
  43. See issue #11647 for details.
  44. """
  45. return self
  46.  
  47. def __call__(self, func):
  48. #logger.info("ContextDecorator func:{}".format(func))
  49. @wraps(func)
  50. def inner(*args, **kwds):
  51. #with self._recreate_cm():
  52. logger.info("you can do something in decorator")
  53. return func(*args, **kwds)
  54. return inner
  55.  
  56. class _GeneratorContextManager(ContextDecorator, AbstractContextManager):
  57. """Helper for @contextmanager decorator."""
  58.  
  59. def __init__(self, func, args, kwds):
  60. self.gen = func(*args, **kwds)
  61. logger.info("get generator by with:{}".format(self.gen))
  62. self.func, self.args, self.kwds = func, args, kwds
  63. # Issue 19330: ensure context manager instances have good docstrings
  64. doc = getattr(func, "__doc__", None) #得到函数文档,第三个参数为默认参数
  65. logger.info("doc:{}".format(doc))
  66. if doc is None:
  67. doc = type(self).__doc__
  68. self.__doc__ = doc
  69. # Unfortunately, this still doesn't provide good help output when
  70. # inspecting the created context manager instances, since pydoc
  71. # currently bypasses the instance docstring and shows the docstring
  72. # for the class instead.
  73. # See http://bugs.python.org/issue19404 for more details.
  74.  
  75. def _recreate_cm(self):
  76. # _GCM instances are one-shot context managers, so the
  77. # CM must be recreated each time a decorated function is
  78. # called
  79. return self.__class__(self.func, self.args, self.kwds)
  80.  
  81. def __enter__(self):
  82. logger.info("__enter__:you can add you method")
  83.  
  84. @ContextDecorator()
  85. def testfun(*args, **kwds):
  86. logger.info("@ContextDecorator():testfun test success")
  87. testfun("hello")
  88.  
  89. try:
  90. return next(self.gen)
  91. except StopIteration:
  92. raise RuntimeError("generator didn't yield") from None
  93.  
  94. def __exit__(self, type, value, traceback):
  95. logger.info("__exit__")
  96. logger.info("type:{}".format(type))
  97. if type is None:
  98. try:
  99. next(self.gen)
  100. except StopIteration:
  101. return
  102. else:
  103. raise RuntimeError("generator didn't stop")
  104. else:
  105. if value is None:
  106. # Need to force instantiation so we can reliably
  107. # tell if we get the same exception back
  108. value = type()
  109. try:
  110. self.gen.throw(type, value, traceback)
  111. raise RuntimeError("generator didn't stop after throw()")
  112. except StopIteration as exc:
  113. # Suppress StopIteration *unless* it's the same exception that
  114. # was passed to throw(). This prevents a StopIteration
  115. # raised inside the "with" statement from being suppressed.
  116. return exc is not value
  117. except RuntimeError as exc:
  118. # Don't re-raise the passed in exception. (issue27112)
  119. if exc is value:
  120. return False
  121. # Likewise, avoid suppressing if a StopIteration exception
  122. # was passed to throw() and later wrapped into a RuntimeError
  123. # (see PEP 479).
  124. if exc.__cause__ is value:
  125. return False
  126. raise
  127. except:
  128. # only re-raise if it's *not* the exception that was
  129. # passed to throw(), because __exit__() must not raise
  130. # an exception unless __exit__() itself failed. But throw()
  131. # has to raise the exception to signal propagation, so this
  132. # fixes the impedance mismatch between the throw() protocol
  133. # and the __exit__() protocol.
  134. #
  135. if sys.exc_info()[1] is not value:
  136. raise
  137.  
  138. def contextmanager(func):
  139. """@contextmanager decorator.
  140.  
  141. Typical usage:
  142.  
  143. @contextmanager
  144. def some_generator(<arguments>):
  145. <setup>
  146. try:
  147. yield <value>
  148. finally:
  149. <cleanup>
  150.  
  151. This makes this:
  152.  
  153. with some_generator(<arguments>) as <variable>:
  154. <body>
  155.  
  156. equivalent to this:
  157.  
  158. <setup>
  159. try:
  160. <variable> = <value>
  161. <body>
  162. finally:
  163. <cleanup>
  164.  
  165. """
  166. @wraps(func)
  167. def helper(*args, **kwds):
  168. return _GeneratorContextManager(func, args, kwds)
  169. return helper
  170.  
  171. @contextmanager
  172. def file_open(path):
  173. ''' file open test'''
  174. try:
  175. f_obj = open(path,"w")
  176. yield f_obj
  177. except OSError:
  178. print("We had an error!")
  179. finally:
  180. print("Closing file")
  181. f_obj.close()
  182.  
  183. if __name__ == "__main__":
  184. with file_open("contextlibtest.txt") as fobj:
  185. fobj.write("Testing context managers")
  186. logger.info("write file success")

关键输出:

  1. 2018-03-22 15:36:43,249 - __main__ - INFO - get generator by with:<generator object file_open at 0x01DE4870>
  2. 2018-03-22 15:36:43,249 - __main__ - INFO - doc: file open test
  3. 2018-03-22 15:36:43,249 - __main__ - INFO - __enter__:you can add you method
  4. 2018-03-22 15:36:43,249 - __main__ - INFO - you can do something in decorator
  5. 2018-03-22 15:36:43,249 - __main__ - INFO - @ContextDecorator():testfun test success
  6. 2018-03-22 15:36:43,249 - __main__ - INFO - write file success
  7. 2018-03-22 15:36:43,249 - __main__ - INFO - __exit__
  8. 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. c9.io

    老常时间没写了,这次是真碰到心动的东西了,赶快给大家奉献上来. (先上图!) (Cloud9 IDE,云端IDE,简单一点就是运行在浏览器中的IDE,你不需要安装任何东西, 只要打开任何一个浏览器,甚 ...

  2. grid布局笔记

    1.应用 display: grid 的元素.这是所有网格项(Grid Items)的直接父级元素.即容器 2.网格容器(Grid Container)的子元素(直接子元素). 3.注意:在 网格容器 ...

  3. 链接指示:extern "C"

    C++程序有时需要调用其他语言编写的函数,最常见的是调用C语言编写的函数.像所有其他名字一样,其他语言中的函数名字也必须在C++中进行声明,并且该声明必须指定返回类型和形参列表.对于其他语言编写的函数 ...

  4. udev和mdev hotplug事件

    关于udev和mdev之间的区别与联系我发现自己现在还没有把它完整的给区分开来和联系起来. 设备文件系统有devfs,mdev,udev mdev是udev的简化版本,是busybox中所带的程序,最 ...

  5. Environment类包含的几个有用的方法

    1.获取操作系统版本(PC,PDA均支持) Environment.OSVersion 2.获取应用程序当前目录(PC支持) Environment.CurrentDirectory 3.列举本地硬盘 ...

  6. IO流参考

    1 import java.io.File; import java.io.FileInputStream; /** * 读取一个字符 */ public class MyReadChar { pub ...

  7. 20162305李昱兴 2016-2017-2 《Java程序设计》第2周学习总结

    20162305 2016-2017-2 <Java程序设计>第2周学习总结 教材学习内容总结 教材的第二章以数据和表达式为主.在第二章的学习中,我了解了print以及println的用法 ...

  8. [洛谷3041]视频游戏的连击Video Game Combos

    题目描述 Bessie is playing a video game! In the game, the three letters 'A', 'B', and 'C' are the only v ...

  9. PHP7的五大新特性

    如果你使用的是基于 composer 和 PSR-4 的框架,这种写法是否能成功的加载类文件?其实是可以的,composer 注册的自动加载方法是在类被调用的时候根据类的命名空间去查找位置,这种写法对 ...

  10. Mybatis中接口和对应的mapper文件位置配置深入剖析

    首先要说明的问题是,Mybatis中接口和对应的mapper文件不一定要放在同一个包下,放在一起的目的是为了Mybatis进行自动扫描,并且要注意此时java接口的名称和mapper文件的名称要相同, ...