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

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

#!/usr/bin/env python 

 

""" 

@file ion/util/context.py 

@author Adam R. Smith 

@brief Context storage utilities, aka thread-local. 

""" 

 

import sys 

import weakref 

import threading 

 

class temp(object): 

    def __init__(self, f): 

        self.f = f 

 

class StackLocal(object): 

    ''' 

    NOTE: DO NOT USE StackLocal right now. It is not safe to use with Twisted. 

 

    StackLocal provides an interface matching threading.local as close as possible for situations where thread/greenlet context is not possible. 

    The intended usage is to define a global (package-level) variable to serve as a container for data that is local to a particular context. 

    Once you have created a instance of StackLocal, you can assign new attributes to it: 

        context = StackLocal() 

        context.foo = 'bar' 

    Take care when using it to be aware of where you are in the call stack when you store data in a StackLocal instance. 

    When you first assign an attribute with a particular name, the current stack frame becomes the root context for that attribute. 

    If you think of the call chains as a tree, this attribute is now shared by all nodes (stack frames) that are child nodes of the root frame where it was defined. 

    When child nodes (function calls/stack frames) modify the value of the attribute, a new root is not created: all other children of that root will see the change. 

    This emulates thread-local storage reasonably well for situations where you cannot use threading.local. 

 

    Note that StackLocal currently leaks memory slowly. It is intended to be used as a placeholder until proper 

    thread-local storage can be swapped in. 

    ''' 

 

    frame_attrs = None 

    attr_frames = None 

 

    def __init__(self): 

        #object.__setattr__(self, 'frame_attrs', weakref.WeakKeyDictionary()) 

        #object.__setattr__(self, 'attr_frames', weakref.WeakValueDictionary()) 

 

        object.__setattr__(self, 'frame_attrs', {}) 

        object.__setattr__(self, 'attr_frames', {}) 

 

 

    def __setattr__(self, key, val): 

        ''' 

        Set an attribute to exist for the life of this stack frame and all its children. 

        The first time you set an attribute, the current stack frame becomes its root. 

        You can safely change the value in child frames and have the change reflected in other calls 

        that originate from the same root. 

        ''' 

 

        frame = None 

        if key in self.attr_frames: # Try to find the root frame for this attribute 

            attr_frame = self.attr_frames[key] 

 

            frame = sys._getframe(1) 

            while frame: 

                if repr(frame) == attr_frame: 

                    break 

                frame = frame.f_back 

 

        if frame is None: 

            frame = sys._getframe(1) 

            frameid = repr(frame) 

            self.attr_frames[key] = frameid 

 

        frameid = repr(frame) 

 

        if frameid in self.frame_attrs: 

            attrs = self.frame_attrs[frameid] 

        else: 

            self.frame_attrs[frameid] = attrs = {} 

 

        attrs[key] = val 

        return val 

 

    def __get(self, key): 

        if not key in self.attr_frames: 

            return None 

 

        attr_frame = self.attr_frames[key] 

 

        # Ensure the definition frame for this attribute is a parent of the current frame 

        frame = sys._getframe(1) 

        while frame: 

            frameid = repr(frame) 

            if frameid == attr_frame: 

                attrs = self.frame_attrs[frameid] 

                if not key in attrs: 

                    return None 

 

                return attrs[key] 

 

            frame = frame.f_back 

 

        return None 

 

    def __getattr__(self, key): 

        ''' Get an attribute that was defined by any parent stack frame of the current. ''' 

        val = self.__get(key) 

        if val is None: 

            raise AttributeError('There is no attribute named "%s" in the current stack.') 

        return val 

 

    def get(self, key, defaultVal=None): 

        val = self.__get(key) 

        if val is None: 

            return defaultVal 

        return val 

 

 

class ContextLocal(threading.local): 

    """ 

    Extend threading.local to have a dict-style 'get' method. 

    Eventually this class could be a generic context-local class that works in threads, deferreds, greenlets, etc. 

    """ 

 

    def get(self, key, defaultVal=None): 

        try: 

            return self.__getattribute__(key) 

        except AttributeError: 

            return defaultVal 

 

    def clear(self): 

        self.__dict__.clear() 

 

 

class ContextObject(object): 

 

 

    def __init__(self, convid="Default Context"): 

        self.progenitor_convid = convid 

 

    def get(self, key, defaultVal=None): 

        try: 

            return self.__getattribute__(key) 

        except AttributeError: 

            return defaultVal 

 

 

    def clear(self, convid="Default Context"): 

        self.__dict__.clear() 

        self.progenitor_convid = convid 

 

    def __str__(self): 

        return str(self.__dict__.items()) 

 

 

 

class ConversationContext(object): 

 

 

    def __init__(self): 

        self.d = {} 

 

    def create_context(self,name): 

        context = ContextObject(name) 

        self.d[name] = context 

        return context 

 

 

    def get_context(self,name): 

        context = self.d.get(name,None) 

        if context is None: 

            raise KeyError('Conversation Context not found!') 

        return context 

 

 

    def reference_context(self, name, context): 

        self.d[name] = context 

        return None 

 

    def remove(self,name): 

        del self.d[name] 

 

    def __str__(self): 

        return str(self.d.items()) 

 

    def clear(self): 

 

        self.d.clear() 

 

    def replace_context(self): 

        """ 

        If a context completes, we don't really know what to set it back to afterward... so just pick one. Hopefully it doesn't matter.... 

        """ 

        try: 

            return self.d.values()[0] 

        except IndexError, ie: 

            return None 

 

 

""" 

if __name__ == '__main__': 

    context = StackLocal() 

    frame = sys._getframe() 

    t = temp(frame) 

    ref = weakref.ref(t) 

 

    def level_3(): 

        msg = context.msg 

        foo = context.get('foo', None) 

 

        pass 

 

    def level_2(): 

        return level_3() 

 

    def level_1(): 

        return level_2() 

 

    def fake_request(): 

        context.msg = 'foo' 

        #context.foo = None 

 

        def request_context_1(): 

            # Should get 'foo' 

            level_1() 

 

        def request_context_2(): 

            # Should get 'foo2' 

            context.msg = 'foo2' 

            level_1() 

 

        def request_context_3(): 

            # Should get 'foo3' 

            context.msg = 'foo3' 

            level_1() 

 

        def request_context_4(): 

            # Should get 'foo3' 

            level_1() 

 

        def request_context_5(): 

            # Should get 'bar5' 

            context.foo = 'bar5' 

            level_1() 

 

        def request_context_6(): 

            # Should get 'None' 

            level_1() 

 

        request_context_1() 

        request_context_2() 

        request_context_3() 

        request_context_4() 

        request_context_5() 

        request_context_6() 

 

    fake_request()  

 

"""