| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 1 | """ pydevd_vars deals with variables: |
| 2 | resolution/conversion to XML. |
| 3 | """ |
| 4 | import pickle |
| 5 | from django_frame import DjangoTemplateFrame |
| 6 | from pydevd_constants import * #@UnusedWildImport |
| 7 | from types import * #@UnusedWildImport |
| 8 | |
| 9 | from pydevd_xml import * |
| 10 | |
| 11 | try: |
| 12 | from StringIO import StringIO |
| 13 | except ImportError: |
| 14 | from io import StringIO |
| 15 | import sys #@Reimport |
| 16 | |
| 17 | if USE_LIB_COPY: |
| 18 | import _pydev_threading as threading |
| 19 | else: |
| 20 | import threading |
| 21 | import pydevd_resolver |
| 22 | import traceback |
| 23 | |
| 24 | try: |
| 25 | from pydevd_exec import Exec |
| 26 | except: |
| 27 | from pydevd_exec2 import Exec |
| 28 | |
| 29 | #-------------------------------------------------------------------------- defining true and false for earlier versions |
| 30 | |
| 31 | try: |
| 32 | __setFalse = False |
| 33 | except: |
| 34 | import __builtin__ |
| 35 | |
| 36 | setattr(__builtin__, 'True', 1) |
| 37 | setattr(__builtin__, 'False', 0) |
| 38 | |
| 39 | #------------------------------------------------------------------------------------------------------ class for errors |
| 40 | |
| 41 | class VariableError(RuntimeError): pass |
| 42 | |
| 43 | class FrameNotFoundError(RuntimeError): pass |
| 44 | |
| 45 | |
| 46 | if USE_PSYCO_OPTIMIZATION: |
| 47 | try: |
| 48 | import psyco |
| 49 | |
| 50 | varToXML = psyco.proxy(varToXML) |
| 51 | except ImportError: |
| 52 | if hasattr(sys, 'exc_clear'): #jython does not have it |
| 53 | sys.exc_clear() #don't keep the traceback -- clients don't want to see it |
| 54 | |
| 55 | def iterFrames(initialFrame): |
| 56 | """NO-YIELD VERSION: Iterates through all the frames starting at the specified frame (which will be the first returned item)""" |
| 57 | #cannot use yield |
| 58 | frames = [] |
| 59 | |
| 60 | while initialFrame is not None: |
| 61 | frames.append(initialFrame) |
| 62 | initialFrame = initialFrame.f_back |
| 63 | |
| 64 | return frames |
| 65 | |
| 66 | def dumpFrames(thread_id): |
| 67 | sys.stdout.write('dumping frames\n') |
| 68 | if thread_id != GetThreadId(threading.currentThread()): |
| 69 | raise VariableError("findFrame: must execute on same thread") |
| 70 | |
| 71 | curFrame = GetFrame() |
| 72 | for frame in iterFrames(curFrame): |
| 73 | sys.stdout.write('%s\n' % pickle.dumps(frame)) |
| 74 | |
| 75 | |
| 76 | #=============================================================================== |
| 77 | # AdditionalFramesContainer |
| 78 | #=============================================================================== |
| 79 | class AdditionalFramesContainer: |
| 80 | lock = threading.Lock() |
| 81 | additional_frames = {} #dict of dicts |
| 82 | |
| 83 | |
| 84 | def addAdditionalFrameById(thread_id, frames_by_id): |
| 85 | AdditionalFramesContainer.additional_frames[thread_id] = frames_by_id |
| 86 | |
| 87 | |
| 88 | def removeAdditionalFrameById(thread_id): |
| 89 | del AdditionalFramesContainer.additional_frames[thread_id] |
| 90 | |
| 91 | |
| 92 | |
| 93 | def findFrame(thread_id, frame_id): |
| 94 | """ returns a frame on the thread that has a given frame_id """ |
| 95 | if thread_id != GetThreadId(threading.currentThread()): |
| 96 | raise VariableError("findFrame: must execute on same thread") |
| 97 | |
| 98 | lookingFor = int(frame_id) |
| 99 | |
| 100 | if AdditionalFramesContainer.additional_frames: |
| 101 | if DictContains(AdditionalFramesContainer.additional_frames, thread_id): |
| 102 | frame = AdditionalFramesContainer.additional_frames[thread_id].get(lookingFor) |
| 103 | |
| 104 | if frame is not None: |
| 105 | return frame |
| 106 | |
| 107 | curFrame = GetFrame() |
| 108 | if frame_id == "*": |
| 109 | return curFrame # any frame is specified with "*" |
| 110 | |
| 111 | frameFound = None |
| 112 | |
| 113 | for frame in iterFrames(curFrame): |
| 114 | if lookingFor == id(frame): |
| 115 | frameFound = frame |
| 116 | del frame |
| 117 | break |
| 118 | |
| 119 | del frame |
| 120 | |
| 121 | #Important: python can hold a reference to the frame from the current context |
| 122 | #if an exception is raised, so, if we don't explicitly add those deletes |
| 123 | #we might have those variables living much more than we'd want to. |
| 124 | |
| 125 | #I.e.: sys.exc_info holding reference to frame that raises exception (so, other places |
| 126 | #need to call sys.exc_clear()) |
| 127 | del curFrame |
| 128 | |
| 129 | if frameFound is None: |
| 130 | msgFrames = '' |
| 131 | i = 0 |
| 132 | |
| 133 | for frame in iterFrames(GetFrame()): |
| 134 | i += 1 |
| 135 | msgFrames += str(id(frame)) |
| 136 | if i % 5 == 0: |
| 137 | msgFrames += '\n' |
| 138 | else: |
| 139 | msgFrames += ' - ' |
| 140 | |
| 141 | errMsg = '''findFrame: frame not found. |
| 142 | Looking for thread_id:%s, frame_id:%s |
| 143 | Current thread_id:%s, available frames: |
| 144 | %s |
| 145 | ''' % (thread_id, lookingFor, GetThreadId(threading.currentThread()), msgFrames) |
| 146 | |
| 147 | sys.stderr.write(errMsg) |
| 148 | return None |
| 149 | |
| 150 | return frameFound |
| 151 | |
| 152 | def resolveCompoundVariable(thread_id, frame_id, scope, attrs): |
| 153 | """ returns the value of the compound variable as a dictionary""" |
| 154 | frame = findFrame(thread_id, frame_id) |
| 155 | if frame is None: |
| 156 | return {} |
| 157 | |
| 158 | attrList = attrs.split('\t') |
| 159 | |
| 160 | if scope == "GLOBAL": |
| 161 | var = frame.f_globals |
| 162 | del attrList[0] # globals are special, and they get a single dummy unused attribute |
| 163 | else: |
| 164 | var = frame.f_locals |
| 165 | type, _typeName, resolver = getType(var) |
| 166 | try: |
| 167 | resolver.resolve(var, attrList[0]) |
| 168 | except: |
| 169 | var = frame.f_globals |
| 170 | |
| 171 | for k in attrList: |
| 172 | type, _typeName, resolver = getType(var) |
| 173 | var = resolver.resolve(var, k) |
| 174 | |
| 175 | try: |
| 176 | type, _typeName, resolver = getType(var) |
| 177 | return resolver.getDictionary(var) |
| 178 | except: |
| 179 | traceback.print_exc() |
| 180 | |
| 181 | |
| 182 | def resolveVar(var, attrs): |
| 183 | attrList = attrs.split('\t') |
| 184 | |
| 185 | for k in attrList: |
| 186 | type, _typeName, resolver = getType(var) |
| 187 | |
| 188 | var = resolver.resolve(var, k) |
| 189 | |
| 190 | try: |
| 191 | type, _typeName, resolver = getType(var) |
| 192 | return resolver.getDictionary(var) |
| 193 | except: |
| 194 | traceback.print_exc() |
| 195 | |
| 196 | |
| 197 | def evaluateExpression(thread_id, frame_id, expression, doExec): |
| 198 | """returns the result of the evaluated expression |
| 199 | @param doExec: determines if we should do an exec or an eval |
| 200 | """ |
| 201 | frame = findFrame(thread_id, frame_id) |
| 202 | if frame is None: |
| 203 | return |
| 204 | |
| 205 | expression = str(expression.replace('@LINE@', '\n')) |
| 206 | |
| 207 | |
| 208 | #Not using frame.f_globals because of https://sourceforge.net/tracker2/?func=detail&aid=2541355&group_id=85796&atid=577329 |
| 209 | #(Names not resolved in generator expression in method) |
| 210 | #See message: http://mail.python.org/pipermail/python-list/2009-January/526522.html |
| 211 | updated_globals = {} |
| 212 | updated_globals.update(frame.f_globals) |
| 213 | updated_globals.update(frame.f_locals) #locals later because it has precedence over the actual globals |
| 214 | |
| 215 | try: |
| 216 | if doExec: |
| 217 | try: |
| 218 | #try to make it an eval (if it is an eval we can print it, otherwise we'll exec it and |
| 219 | #it will have whatever the user actually did) |
| 220 | compiled = compile(expression, '<string>', 'eval') |
| 221 | except: |
| 222 | Exec(expression, updated_globals, frame.f_locals) |
| 223 | else: |
| 224 | result = eval(compiled, updated_globals, frame.f_locals) |
| 225 | if result is not None: #Only print if it's not None (as python does) |
| 226 | sys.stdout.write('%s\n' % (result,)) |
| 227 | return |
| 228 | |
| 229 | else: |
| 230 | result = None |
| 231 | try: |
| 232 | result = eval(expression, updated_globals, frame.f_locals) |
| 233 | except Exception: |
| 234 | s = StringIO() |
| 235 | traceback.print_exc(file=s) |
| 236 | |
| 237 | result = s.getvalue() |
| 238 | |
| 239 | try: |
| 240 | try: |
| 241 | etype, value, tb = sys.exc_info() |
| 242 | result = value |
| 243 | finally: |
| 244 | etype = value = tb = None |
| 245 | except: |
| 246 | pass |
| 247 | |
| 248 | result = ExceptionOnEvaluate(result) |
| 249 | |
| 250 | return result |
| 251 | finally: |
| 252 | #Should not be kept alive if an exception happens and this frame is kept in the stack. |
| 253 | del updated_globals |
| 254 | del frame |
| 255 | |
| 256 | def changeAttrExpression(thread_id, frame_id, attr, expression): |
| 257 | """Changes some attribute in a given frame. |
| 258 | @note: it will not (currently) work if we're not in the topmost frame (that's a python |
| 259 | deficiency -- and it appears that there is no way of making it currently work -- |
| 260 | will probably need some change to the python internals) |
| 261 | """ |
| 262 | frame = findFrame(thread_id, frame_id) |
| 263 | if frame is None: |
| 264 | return |
| 265 | |
| 266 | if isinstance(frame, DjangoTemplateFrame): |
| 267 | result = eval(expression, frame.f_globals, frame.f_locals) |
| 268 | frame.changeVariable(attr, result) |
| 269 | |
| 270 | try: |
| 271 | expression = expression.replace('@LINE@', '\n') |
| 272 | #tests (needs proposed patch in python accepted) |
| 273 | # if hasattr(frame, 'savelocals'): |
| 274 | # if attr in frame.f_locals: |
| 275 | # frame.f_locals[attr] = eval(expression, frame.f_globals, frame.f_locals) |
| 276 | # frame.savelocals() |
| 277 | # return |
| 278 | # |
| 279 | # elif attr in frame.f_globals: |
| 280 | # frame.f_globals[attr] = eval(expression, frame.f_globals, frame.f_locals) |
| 281 | # return |
| 282 | |
| 283 | |
| 284 | if attr[:7] == "Globals": |
| 285 | attr = attr[8:] |
| 286 | if attr in frame.f_globals: |
| 287 | frame.f_globals[attr] = eval(expression, frame.f_globals, frame.f_locals) |
| 288 | return frame.f_globals[attr] |
| 289 | else: |
| 290 | #default way (only works for changing it in the topmost frame) |
| 291 | result = eval(expression, frame.f_globals, frame.f_locals) |
| 292 | Exec('%s=%s' % (attr, expression), frame.f_globals, frame.f_locals) |
| 293 | return result |
| 294 | |
| 295 | |
| 296 | except Exception: |
| 297 | traceback.print_exc() |
| 298 | |
| 299 | |
| 300 | |
| 301 | |
| 302 | |