Package translate :: Package misc :: Module contextlib

[hide private]
[frames] | no frames]

# Source Code for Module translate.misc.contextlib

      1  #!/usr/bin/env python
      2  # -*- coding: utf-8 -*-
      3  #
      4  # Copyright 2002-2006 Zuza Software Foundation
      5  #
      6  # This file is part of translate.
      7  # The file was copied from the Python 2.5 source.
      8  #
      9  # translate is free software; you can redistribute it and/or modify
     10  # it under the terms of the GNU General Public License as published by
     11  # the Free Software Foundation; either version 2 of the License, or
     12  # (at your option) any later version.
     13  #
     14  # translate is distributed in the hope that it will be useful,
     15  # but WITHOUT ANY WARRANTY; without even the implied warranty of
     16  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17  # GNU General Public License for more details.
     18  #
     19  # You should have received a copy of the GNU General Public License
     20  # along with translate; if not, write to the Free Software
     21  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
     22  #
     23
     24  # NB! IMPORTANT SEMANTIC DIFFERENCE WITH THE OFFICIAL contextlib.
     25  # In Python 2.5+, if an exception is thrown in a 'with' statement
     26  # which uses a generator-based context manager (that is, a
     27  # context manager created by decorating a generator with
     28  # @contextmanager), the exception will be propagated to the
     29  # generator via the .throw method of the generator.
     30  #
     31  # This does not exist in Python 2.4. Thus, we just naively finish
     32  # off the context manager. This also means that generator-based
     33  # context managers can't deal with exceptions, so be warned.
     34
     35  """Utilities for with-statement contexts.  See PEP 343."""
     36
     37  import sys
     38
     39  __all__ = ["contextmanager", "nested", "closing"]
     40
     41 -class GeneratorContextManager(object):
     42      """Helper for @contextmanager decorator."""
     43
     44 -    def __init__(self, gen):
     45          self.gen = gen
     46
     47 -    def __enter__(self):
     48          try:
     49              return self.gen.next()
     50          except StopIteration:
     51              raise RuntimeError("generator didn't yield")
     52
     53 -    def __exit__(self, type, value, tb):
     54          if type is None:
     55              try:
     56                  self.gen.next()
     57              except StopIteration:
     58                  return
     59              else:
     60                  raise RuntimeError("generator didn't stop")
     61          else:
     62              if value is None:
     63                  # Need to force instantiation so we can reliably
     64                  # tell if we get the same exception back
     65                  value = type()
     66              try:
     67                  try:
     68                      self.gen.next()
     69                  except StopIteration:
     70                      import traceback
     71                      traceback.print_exception(type, value, tb)
     72                      raise value
     73              except StopIteration, exc:
     74                  # Suppress the exception *unless* it's the same exception that
     75                  # was passed to throw().  This prevents a StopIteration
     76                  # raised inside the "with" statement from being suppressed
     77                  return exc is not value
     78
     79 -def contextmanager(func):
     80      """@contextmanager decorator.
     81
     82      Typical usage:
     83
     84          @contextmanager
     85          def some_generator(<arguments>):
     86              <setup>
     87              try:
     88                  yield <value>
     89              finally:
     90                  <cleanup>
     91
     92      This makes this:
     93
     94          with some_generator(<arguments>) as <variable>:
     95              <body>
     96
     97      equivalent to this:
     98
     99          <setup>
    100          try:
    101              <variable> = <value>
    102              <body>
    103          finally:
    104              <cleanup>
    105
    106      """
    107      def helper(*args, **kwds):
    108          return GeneratorContextManager(func(*args, **kwds))
    109      try:
    110          helper.__name__ = func.__name__
    111          helper.__doc__ = func.__doc__
    112          helper.__dict__ = func.__dict__
    113      except:
    114          pass
    115      return helper
    116
    117
    118  @contextmanager
    119 -def nested(*managers):
    120      """Support multiple context managers in a single with-statement.
    121
    122      Code like this:
    123
    124          with nested(A, B, C) as (X, Y, Z):
    125              <body>
    126
    127      is equivalent to this:
    128
    129          with A as X:
    130              with B as Y:
    131                  with C as Z:
    132                      <body>
    133
    134      """
    135      exits = []
    136      vars = []
    137      exc = (None, None, None)
    138      # Lambdas are an easy way to create unique objects. We don't want
    139      # this to be None, since our answer might actually be None
    140      undefined = lambda: 42
    141      result = undefined
    142
    143      try:
    144          for mgr in managers:
    145              exit = mgr.__exit__
    146              enter = mgr.__enter__
    147              vars.append(enter())
    148              exits.append(exit)
    149          result = vars
    150      except:
    151          exc = sys.exc_info()
    152
    153      # If nothing has gone wrong, then result contains our return value
    154      # and thus it is not equal to 'undefined'. Thus, yield the value.
    155      if result != undefined:
    156          yield result
    157
    158      while exits:
    159          exit = exits.pop()
    160          try:
    161              if exit(*exc):
    162                  exc = (None, None, None)
    163          except:
    164              exc = sys.exc_info()
    165      if exc != (None, None, None):
    166          # Don't rely on sys.exc_info() still containing
    167          # the right information. Another exception may
    168          # have been raised and caught by an exit method
    169          raise exc[0], exc[1], exc[2]
    170
    171 -class closing(object):
    172      """Context to automatically close something at the end of a block.
    173
    174      Code like this:
    175
    176          with closing(<module>.open(<arguments>)) as f:
    177              <block>
    178
    179      is equivalent to this:
    180
    181          f = <module>.open(<arguments>)
    182          try:
    183              <block>
    184          finally:
    185              f.close()
    186
    187      """
    188 -    def __init__(self, thing):
    189          self.thing = thing
    190 -    def __enter__(self):
    191          return self.thing
    192 -    def __exit__(self, *exc_info):
    193          self.thing.close()
    194