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
     42 -class GeneratorContextManager(object):
     43      """Helper for @contextmanager decorator."""
     44
     45 -    def __init__(self, gen):
     46          self.gen = gen
     47
     48 -    def __enter__(self):
     49          try:
     50              return self.gen.next()
     51          except StopIteration:
     52              raise RuntimeError("generator didn't yield")
     53
     54 -    def __exit__(self, type, value, tb):
     55          if type is None:
     56              try:
     57                  self.gen.next()
     58              except StopIteration:
     59                  return
     60              else:
     61                  raise RuntimeError("generator didn't stop")
     62          else:
     63              if value is None:
     64                  # Need to force instantiation so we can reliably
     65                  # tell if we get the same exception back
     66                  value = type()
     67              try:
     68                  try:
     69                      self.gen.next()
     70                  except StopIteration:
     71                      import traceback
     72                      traceback.print_exception(type, value, tb)
     73                      raise value
     74              except StopIteration, exc:
     75                  # Suppress the exception *unless* it's the same exception that
     76                  # was passed to throw().  This prevents a StopIteration
     77                  # raised inside the "with" statement from being suppressed
     78                  return exc is not value
     79
     80
     81 -def contextmanager(func):
     82      """@contextmanager decorator.
     83
     84      Typical usage:
     85
     86          @contextmanager
     87          def some_generator(<arguments>):
     88              <setup>
     89              try:
     90                  yield <value>
     91              finally:
     92                  <cleanup>
     93
     94      This makes this:
     95
     96          with some_generator(<arguments>) as <variable>:
     97              <body>
     98
     99      equivalent to this:
    100
    101          <setup>
    102          try:
    103              <variable> = <value>
    104              <body>
    105          finally:
    106              <cleanup>
    107
    108      """
    109
    110      def helper(*args, **kwds):
    111          return GeneratorContextManager(func(*args, **kwds))
    112      try:
    113          helper.__name__ = func.__name__
    114          helper.__doc__ = func.__doc__
    115          helper.__dict__ = func.__dict__
    116      except:
    117          pass
    118      return helper
    119
    120
    121  @contextmanager
    122 -def nested(*managers):
    123      """Support multiple context managers in a single with-statement.
    124
    125      Code like this:
    126
    127          with nested(A, B, C) as (X, Y, Z):
    128              <body>
    129
    130      is equivalent to this:
    131
    132          with A as X:
    133              with B as Y:
    134                  with C as Z:
    135                      <body>
    136
    137      """
    138      exits = []
    139      vars = []
    140      exc = (None, None, None)
    141      # Lambdas are an easy way to create unique objects. We don't want
    142      # this to be None, since our answer might actually be None
    143      undefined = lambda: 42
    144      result = undefined
    145
    146      try:
    147          for mgr in managers:
    148              exit = mgr.__exit__
    149              enter = mgr.__enter__
    150              vars.append(enter())
    151              exits.append(exit)
    152          result = vars
    153      except:
    154          exc = sys.exc_info()
    155
    156      # If nothing has gone wrong, then result contains our return value
    157      # and thus it is not equal to 'undefined'. Thus, yield the value.
    158      if result != undefined:
    159          yield result
    160
    161      while exits:
    162          exit = exits.pop()
    163          try:
    164              if exit(*exc):
    165                  exc = (None, None, None)
    166          except:
    167              exc = sys.exc_info()
    168      if exc != (None, None, None):
    169          # Don't rely on sys.exc_info() still containing
    170          # the right information. Another exception may
    171          # have been raised and caught by an exit method
    172          raise exc[0], exc[1], exc[2]
    173
    174
    175 -class closing(object):
    176      """Context to automatically close something at the end of a block.
    177
    178      Code like this:
    179
    180          with closing(<module>.open(<arguments>)) as f:
    181              <block>
    182
    183      is equivalent to this:
    184
    185          f = <module>.open(<arguments>)
    186          try:
    187              <block>
    188          finally:
    189              f.close()
    190
    191      """
    192
    193 -    def __init__(self, thing):
    194          self.thing = thing
    195
    196 -    def __enter__(self):
    197          return self.thing
    198
    199 -    def __exit__(self, *exc_info):
    200          self.thing.close()
    201