Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

"""Utilities for with-statement contexts.  See PEP 343.""" 

 

import sys 

from functools import wraps 

from warnings import warn 

 

__all__ = ["contextmanager", "nested", "closing"] 

 

class GeneratorContextManager(object): 

    """Helper for @contextmanager decorator.""" 

 

    def __init__(self, gen): 

        self.gen = gen 

 

    def __enter__(self): 

        try: 

            return self.gen.next() 

        except StopIteration: 

            raise RuntimeError("generator didn't yield") 

 

    def __exit__(self, type, value, traceback): 

        if type is None: 

            try: 

                self.gen.next() 

            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, exc: 

                # Suppress the exception *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: 

                # 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 nested(*managers): 

    """Combine multiple context managers into a single nested context manager. 

 

   This function has been deprecated in favour of the multiple manager form 

   of the with statement. 

 

   The one advantage of this function over the multiple manager form of the 

   with statement is that argument unpacking allows it to be 

   used with a variable number of context managers as follows: 

 

      with nested(*managers): 

          do_something() 

 

    """ 

    warn("With-statements now directly support multiple context managers", 

         DeprecationWarning, 3) 

    exits = [] 

    vars = [] 

    exc = (None, None, None) 

    try: 

        for mgr in managers: 

            exit = mgr.__exit__ 

            enter = mgr.__enter__ 

            vars.append(enter()) 

            exits.append(exit) 

        yield vars 

    except: 

        exc = sys.exc_info() 

    finally: 

        while exits: 

            exit = exits.pop() 

            try: 

                if exit(*exc): 

                    exc = (None, None, None) 

            except: 

                exc = sys.exc_info() 

        if exc != (None, None, None): 

            # Don't rely on sys.exc_info() still containing 

            # the right information. Another exception may 

            # have been raised and caught by an exit method 

            raise exc[0], exc[1], exc[2] 

 

 

class closing(object): 

    """Context to automatically close something at the end of a block. 

 

    Code like this: 

 

        with closing(<module>.open(<arguments>)) as f: 

            <block> 

 

    is equivalent to this: 

 

        f = <module>.open(<arguments>) 

        try: 

            <block> 

        finally: 

            f.close() 

 

    """ 

    def __init__(self, thing): 

        self.thing = thing 

    def __enter__(self): 

        return self.thing 

    def __exit__(self, *exc_info): 

        self.thing.close()