From ef377db33716b13f4cea646d314ce1d75a61e435 Mon Sep 17 00:00:00 2001 From: Dmitry Trofimov Date: Tue, 26 Aug 2014 15:24:03 +0200 Subject: [PATCH] Pydev.Debugger updated. --- python/helpers/pydev/README.md | 7 + python/helpers/pydev/_pydev_imports_tipper.py | 118 ++- .../pydev/_pydev_imps/_pydev_inspect.py | 8 +- .../helpers/pydev/_pydev_jy_imports_tipper.py | 165 +-- python/helpers/pydev/_pydev_threading.py | 990 +----------------- python/helpers/pydev/_pydev_tipper_common.py | 29 +- python/helpers/pydev/fix_getpass.py | 19 +- python/helpers/pydev/merge_pydev_pycharm.txt | 40 +- python/helpers/pydev/pycompletion.py | 6 +- python/helpers/pydev/pycompletionserver.py | 53 +- python/helpers/pydev/pydev_console_utils.py | 55 +- python/helpers/pydev/pydev_imports.py | 32 +- .../pydev/pydev_ipython/inputhookglut.py | 2 +- .../pydev/pydev_ipython/inputhookpyglet.py | 2 +- .../pydev/pydev_ipython/inputhookqt4.py | 6 +- .../pydev/pydev_ipython/inputhookwx.py | 2 +- python/helpers/pydev/pydev_ipython_console.py | 5 +- .../pydev/pydev_ipython_console_011.py | 46 +- python/helpers/pydev/pydev_localhost.py | 13 +- python/helpers/pydev/pydev_monkey.py | 119 ++- .../helpers/pydev/pydev_runfiles_parallel.py | 9 +- .../pydev/pydev_runfiles_parallel_client.py | 103 +- .../helpers/pydev/pydev_runfiles_xml_rpc.py | 9 +- python/helpers/pydev/pydevconsole.py | 45 +- python/helpers/pydev/pydevd.py | 415 +++----- .../pydev/pydevd_additional_thread_info.py | 7 +- python/helpers/pydev/pydevd_breakpoints.py | 7 +- python/helpers/pydev/pydevd_comm.py | 116 +- python/helpers/pydev/pydevd_console.py | 56 +- python/helpers/pydev/pydevd_constants.py | 32 +- python/helpers/pydev/pydevd_custom_frames.py | 3 +- python/helpers/pydev/pydevd_frame.py | 17 +- python/helpers/pydev/pydevd_resolver.py | 97 +- python/helpers/pydev/pydevd_signature.py | 17 +- python/helpers/pydev/pydevd_stackless.py | 5 +- python/helpers/pydev/pydevd_tracing.py | 7 +- python/helpers/pydev/pydevd_vars.py | 11 +- python/helpers/pydev/runfiles.py | 7 +- .../pydev/tests/test_check_pydevconsole.py | 1 + .../helpers/pydev/tests/test_get_referrers.py | 15 - python/helpers/pydev/tests/test_jyserver.py | 8 + .../pydev/tests/test_jysimpleTipper.py | 39 +- .../pydev/tests/test_pydev_ipython_011.py | 217 ++-- .../helpers/pydev/tests/test_pydevconsole.py | 283 ++--- python/helpers/pydev/tests/test_pyserver.py | 22 +- .../helpers/pydev/tests/test_simpleTipper.py | 38 +- .../helpers/pydev/tests_mainloop/gui-glut.py | 78 +- .../helpers/pydev/tests_mainloop/gui-gtk.py | 49 +- .../helpers/pydev/tests_mainloop/gui-gtk3.py | 45 +- .../pydev/tests_mainloop/gui-pyglet.py | 35 +- python/helpers/pydev/tests_mainloop/gui-qt.py | 45 +- python/helpers/pydev/tests_mainloop/gui-tk.py | 44 +- python/helpers/pydev/tests_mainloop/gui-wx.py | 174 +-- .../pydev/tests_python/_debugger_case18.py | 2 +- .../pydev/tests_python/_debugger_case19.py | 2 +- .../pydev/tests_python/_debugger_case7.py | 2 +- .../pydev/tests_python/_debugger_case89.py | 12 +- .../tests_python/_debugger_case_qthread2.py | 1 + .../tests_python/_debugger_case_qthread3.py | 1 + .../test_additional_thread_info.py | 26 +- .../pydev/tests_python/test_debugger.py | 656 ++++++++---- .../pydev/tests_python/test_pydev_monkey.py | 22 +- .../pydev/tests_python/test_save_locals.py | 6 +- .../pydev/tests_runfiles/test_runfiles.py | 64 +- 64 files changed, 2030 insertions(+), 2537 deletions(-) diff --git a/python/helpers/pydev/README.md b/python/helpers/pydev/README.md index 7b221164e66f..17df01ca8113 100644 --- a/python/helpers/pydev/README.md +++ b/python/helpers/pydev/README.md @@ -1,2 +1,9 @@ PyDev.Debugger ============== + +[![Build Status](https://travis-ci.org/fabioz/PyDev.Debugger.png)](https://travis-ci.org/fabioz/PyDev.Debugger) + +This repository contains the sources for the Debugger used in PyDev & PyCharm. + +It should be compatible with Python 2.4 onwards (as well as Jython 2.2.1, IronPython and PyPy -- and any +other variant which properly supports the Python structure for debuggers -- i.e.: sys.settrace/threading.settrace). \ No newline at end of file diff --git a/python/helpers/pydev/_pydev_imports_tipper.py b/python/helpers/pydev/_pydev_imports_tipper.py index e4b3b863f210..76cf2cdc473f 100644 --- a/python/helpers/pydev/_pydev_imports_tipper.py +++ b/python/helpers/pydev/_pydev_imports_tipper.py @@ -4,6 +4,10 @@ import sys from _pydev_tipper_common import DoFind +try: + xrange +except: + xrange = range #completion types. TYPE_IMPORT = '0' @@ -19,20 +23,20 @@ def _imp(name, log=None): except: if '.' in name: sub = name[0:name.rfind('.')] - + if log is not None: log.AddContent('Unable to import', name, 'trying with', sub) log.AddException() - + return _imp(sub, log) else: s = 'Unable to import module: %s - sys.path: %s' % (str(name), sys.path) if log is not None: log.AddContent(s) log.AddException() - + raise ImportError(s) - + IS_IPY = False if sys.platform == 'cli': @@ -53,9 +57,9 @@ if sys.platform == 'cli': clr.AddReference(name) except: pass #That's OK (not dot net module). - + return _old_imp(initial_name, log) - + def GetFile(mod): @@ -69,19 +73,19 @@ def GetFile(mod): filename = f[:-4] + '.py' if os.path.exists(filename): f = filename - + return f def Find(name, log=None): f = None - + mod = _imp(name, log) parent = mod foundAs = '' - + if inspect.ismodule(mod): f = GetFile(mod) - + components = name.split('.') old_comp = None @@ -94,22 +98,22 @@ def Find(name, log=None): except AttributeError: if old_comp != comp: raise - + if inspect.ismodule(mod): f = GetFile(mod) else: if len(foundAs) > 0: foundAs = foundAs + '.' foundAs = foundAs + comp - + old_comp = comp - + return f, mod, parent, foundAs def Search(data): '''@return file, line, col ''' - + data = data.replace('\n', '') if data.endswith('.'): data = data.rstrip('.') @@ -118,19 +122,19 @@ def Search(data): return DoFind(f, mod), foundAs except: return DoFind(f, parent), foundAs - - + + def GenerateTip(data, log=None): data = data.replace('\n', '') if data.endswith('.'): data = data.rstrip('.') - + f, mod, parent, foundAs = Find(data, log) #print_ >> open('temp.txt', 'w'), f tips = GenerateImportsTipForModule(mod) return f, tips - - + + def CheckChar(c): if c == '-' or c == '.': return '_' @@ -146,7 +150,7 @@ def GenerateImportsTipForModule(obj_to_complete, dirComps=None, getattr=getattr, name, doc, args, type (from the TYPE_* constants) ''' ret = [] - + if dirComps is None: dirComps = dir(obj_to_complete) if hasattr(obj_to_complete, '__dict__'): @@ -155,22 +159,22 @@ def GenerateImportsTipForModule(obj_to_complete, dirComps=None, getattr=getattr, dirComps.append('__class__') getCompleteInfo = True - + if len(dirComps) > 1000: - #ok, we don't want to let our users wait forever... + #ok, we don't want to let our users wait forever... #no complete info for you... - + getCompleteInfo = False - + dontGetDocsOn = (float, int, str, tuple, list) for d in dirComps: - + if d is None: continue - + if not filter(d): continue - + args = '' try: @@ -182,18 +186,18 @@ def GenerateImportsTipForModule(obj_to_complete, dirComps=None, getattr=getattr, if getCompleteInfo: try: retType = TYPE_BUILTIN - + #check if we have to get docs getDoc = True for class_ in dontGetDocsOn: - + if isinstance(obj, class_): getDoc = False break - + doc = '' if getDoc: - #no need to get this info... too many constants are defined and + #no need to get this info... too many constants are defined and #makes things much slower (passing all that through sockets takes quite some time) try: doc = inspect.getdoc(obj) @@ -201,12 +205,12 @@ def GenerateImportsTipForModule(obj_to_complete, dirComps=None, getattr=getattr, doc = '' except: #may happen on jython when checking java classes (so, just ignore it) doc = '' - - + + if inspect.ismethod(obj) or inspect.isbuiltin(obj) or inspect.isfunction(obj) or inspect.isroutine(obj): try: args, vargs, kwargs, defaults = inspect.getargspec(obj) - + r = '' for a in (args): if len(r) > 0: @@ -250,7 +254,7 @@ def GenerateImportsTipForModule(obj_to_complete, dirComps=None, getattr=getattr, if major: args = major[major.index('('):] found = True - + if not found: i = doc.find('->') @@ -260,12 +264,12 @@ def GenerateImportsTipForModule(obj_to_complete, dirComps=None, getattr=getattr, i = doc.find('\n') if i < 0: i = doc.find('\r') - - + + if i > 0: s = doc[0:i] s = s.strip() - + #let's see if we have a docstring in the first line if s[-1] == ')': start = s.find('(') @@ -275,21 +279,21 @@ def GenerateImportsTipForModule(obj_to_complete, dirComps=None, getattr=getattr, end = s.find(')') if end <= 0: end = len(s) - + args = s[start:end] if not args[-1] == ')': args = args + ')' - - + + #now, get rid of unwanted chars l = len(args) - 1 r = [] - for i in range(len(args)): + for i in xrange(len(args)): if i == 0 or i == l: r.append(args[i]) else: r.append(CheckChar(args[i])) - + args = ''.join(r) if IS_IPY: @@ -305,43 +309,43 @@ def GenerateImportsTipForModule(obj_to_complete, dirComps=None, getattr=getattr, except: pass - + retType = TYPE_FUNCTION - + elif inspect.isclass(obj): retType = TYPE_CLASS - + elif inspect.ismodule(obj): retType = TYPE_IMPORT - + else: retType = TYPE_ATTR - - + + #add token and doc to return - assure only strings. ret.append((d, doc, args, retType)) - + except: #just ignore and get it without aditional info ret.append((d, '', args, TYPE_BUILTIN)) - + else: #getCompleteInfo == False if inspect.ismethod(obj) or inspect.isbuiltin(obj) or inspect.isfunction(obj) or inspect.isroutine(obj): retType = TYPE_FUNCTION - + elif inspect.isclass(obj): retType = TYPE_CLASS - + elif inspect.ismodule(obj): retType = TYPE_IMPORT - + else: retType = TYPE_ATTR #ok, no complete info, let's try to do this as fast and clean as possible #so, no docs for this kind of information, only the signatures ret.append((d, '', str(args), retType)) - + return ret - - + + diff --git a/python/helpers/pydev/_pydev_imps/_pydev_inspect.py b/python/helpers/pydev/_pydev_imps/_pydev_inspect.py index 57147644e769..5fd33d876c1b 100644 --- a/python/helpers/pydev/_pydev_imps/_pydev_inspect.py +++ b/python/helpers/pydev/_pydev_imps/_pydev_inspect.py @@ -27,13 +27,7 @@ Here are some of the useful functions provided by this module: __author__ = 'Ka-Ping Yee ' __date__ = '1 Jan 2001' -import sys -import os -import types -import string -import re -import imp -import tokenize +import sys, os, types, string, re, imp, tokenize # ----------------------------------------------------------- type-checking def ismodule(object): diff --git a/python/helpers/pydev/_pydev_jy_imports_tipper.py b/python/helpers/pydev/_pydev_jy_imports_tipper.py index 7a957e03b02a..db1d7f895106 100644 --- a/python/helpers/pydev/_pydev_jy_imports_tipper.py +++ b/python/helpers/pydev/_pydev_jy_imports_tipper.py @@ -14,13 +14,18 @@ except NameError: # version < 2.3 -- didn't have the True/False builtins import __builtin__ setattr(__builtin__, 'True', 1) setattr(__builtin__, 'False', 0) - - + + from org.python.core import PyReflectedFunction #@UnresolvedImport from org.python import core #@UnresolvedImport from org.python.core import PyClass #@UnresolvedImport +try: + xrange +except: + xrange = range + #completion types. TYPE_IMPORT = '0' @@ -48,11 +53,11 @@ def Find(name): name = 'org.python.core.PyString' elif name == '__builtin__.dict': name = 'org.python.core.PyDictionary' - + mod = _imp(name) parent = mod foundAs = '' - + if hasattr(mod, '__file__'): f = mod.__file__ @@ -68,29 +73,29 @@ def Find(name): except AttributeError: if old_comp != comp: raise - + if hasattr(mod, '__file__'): f = mod.__file__ else: if len(foundAs) > 0: foundAs = foundAs + '.' foundAs = foundAs + comp - + old_comp = comp - + return f, mod, parent, foundAs def formatParamClassName(paramClassName): if paramClassName.startswith('['): if paramClassName == '[C': paramClassName = 'char[]' - + elif paramClassName == '[B': paramClassName = 'byte[]' - + elif paramClassName == '[I': paramClassName = 'int[]' - + elif paramClassName.startswith('[L') and paramClassName.endswith(';'): paramClassName = paramClassName[2:-1] paramClassName += '[]' @@ -101,17 +106,17 @@ def GenerateTip(data, log=None): data = data.replace('\n', '') if data.endswith('.'): data = data.rstrip('.') - + f, mod, parent, foundAs = Find(data) tips = GenerateImportsTipForModule(mod) return f, tips - + #======================================================================================================================= # Info #======================================================================================================================= class Info: - + def __init__(self, name, **kwargs): self.name = name self.doc = kwargs.get('doc', None) @@ -119,47 +124,47 @@ class Info: self.varargs = kwargs.get('varargs', None) #string self.kwargs = kwargs.get('kwargs', None) #string self.ret = kwargs.get('ret', None) #string - + def basicAsStr(self): '''@returns this class information as a string (just basic format) ''' - + s = 'function:%s args=%s, varargs=%s, kwargs=%s, docs:%s' % \ (str(self.name), str(self.args), str(self.varargs), str(self.kwargs), str(self.doc)) return s - + def getAsDoc(self): s = str(self.name) if self.doc: s += '\n@doc %s\n' % str(self.doc) - + if self.args: s += '\n@params ' for arg in self.args: s += str(formatParamClassName(arg)) s += ' ' - + if self.varargs: s += '\n@varargs ' s += str(self.varargs) - + if self.kwargs: s += '\n@kwargs ' s += str(self.kwargs) - + if self.ret: s += '\n@return ' s += str(formatParamClassName(str(self.ret))) - + return str(s) - + def isclass(cls): return isinstance(cls, core.PyClass) def ismethod(func): '''this function should return the information gathered on a function - + @param func: this is the function we want to get info on @return a tuple where: 0 = indicates whether the parameter passed is a method or not @@ -167,24 +172,24 @@ def ismethod(func): this is a list because when we have methods from java with the same name and different signatures, we actually have many methods, each with its own set of arguments ''' - + try: if isinstance(func, core.PyFunction): #ok, this is from python, created by jython #print_ ' PyFunction' - + def getargs(func_code): """Get information about the arguments accepted by a code object. - + Three things are returned: (args, varargs, varkw), where 'args' is a list of argument names (possibly containing nested lists), and 'varargs' and 'varkw' are the names of the * and ** arguments or None.""" - + nargs = func_code.co_argcount names = func_code.co_varnames args = list(names[:nargs]) step = 0 - + varargs = None if func_code.co_flags & func_code.CO_VARARGS: varargs = func_code.co_varnames[nargs] @@ -193,35 +198,35 @@ def ismethod(func): if func_code.co_flags & func_code.CO_VARKEYWORDS: varkw = func_code.co_varnames[nargs] return args, varargs, varkw - + args = getargs(func.func_code) return 1, [Info(func.func_name, args=args[0], varargs=args[1], kwargs=args[2], doc=func.func_doc)] - + if isinstance(func, core.PyMethod): #this is something from java itself, and jython just wrapped it... - + #things to play in func: #['__call__', '__class__', '__cmp__', '__delattr__', '__dir__', '__doc__', '__findattr__', '__name__', '_doget', 'im_class', #'im_func', 'im_self', 'toString'] #print_ ' PyMethod' #that's the PyReflectedFunction... keep going to get it func = func.im_func - + if isinstance(func, PyReflectedFunction): #this is something from java itself, and jython just wrapped it... - + #print_ ' PyReflectedFunction' - + infos = [] - for i in range(len(func.argslist)): + for i in xrange(len(func.argslist)): #things to play in func.argslist[i]: - + #'PyArgsCall', 'PyArgsKeywordsCall', 'REPLACE', 'StandardCall', 'args', 'compare', 'compareTo', 'data', 'declaringClass' #'flags', 'isStatic', 'matches', 'precedence'] - + #print_ ' ', func.argslist[i].data.__class__ #func.argslist[i].data.__class__ == java.lang.reflect.Method - + if func.argslist[i]: met = func.argslist[i].data name = met.getName() @@ -230,9 +235,9 @@ def ismethod(func): except AttributeError: ret = '' parameterTypes = met.getParameterTypes() - + args = [] - for j in range(len(parameterTypes)): + for j in xrange(len(parameterTypes)): paramTypesClass = parameterTypes[j] try: try: @@ -246,7 +251,7 @@ def ismethod(func): except: paramClassName = repr(paramTypesClass) #just in case something else happens... it will at least be visible #if the parameter equals [C, it means it it a char array, so, let's change it - + a = formatParamClassName(paramClassName) #a = a.replace('[]','Array') #a = a.replace('Object', 'obj') @@ -255,18 +260,18 @@ def ismethod(func): #a = a.replace('Char', 'c') #a = a.replace('Double', 'd') args.append(a) #so we don't leave invalid code - - + + info = Info(name, args=args, ret=ret) #print_ info.basicAsStr() infos.append(info) - + return 1, infos except Exception: s = StringIO.StringIO() traceback.print_exc(file=s) return 1, [Info(str('ERROR'), doc=s.getvalue())] - + return 0, None def ismodule(mod): @@ -274,7 +279,7 @@ def ismodule(mod): if not hasattr(mod, 'getClass') and not hasattr(mod, '__class__') \ and hasattr(mod, '__name__'): return 1 - + return isinstance(mod, core.PyModule) @@ -293,11 +298,11 @@ def dirObj(obj): except TypeError: #may happen on jython when getting the java.lang.Class class c = obj.getSuperclass(obj) - + while c != None: classes.append(c) c = c.getSuperclass() - + #get info about interfaces interfs = [] for obj in classes: @@ -306,57 +311,57 @@ def dirObj(obj): except TypeError: interfs.extend(obj.getInterfaces(obj)) classes.extend(interfs) - + #now is the time when we actually get info on the declared methods and fields for obj in classes: try: declaredMethods = obj.getDeclaredMethods() except TypeError: declaredMethods = obj.getDeclaredMethods(obj) - + try: declaredFields = obj.getDeclaredFields() except TypeError: declaredFields = obj.getDeclaredFields(obj) - - for i in range(len(declaredMethods)): + + for i in xrange(len(declaredMethods)): name = declaredMethods[i].getName() ret.append(name) found.put(name, 1) - - for i in range(len(declaredFields)): + + for i in xrange(len(declaredFields)): name = declaredFields[i].getName() ret.append(name) found.put(name, 1) - - - elif isclass(obj.__class__): + + + elif isclass(obj.__class__): d = dir(obj.__class__) for name in d: ret.append(name) found.put(name, 1) - + #this simple dir does not always get all the info, that's why we have the part before - #(e.g.: if we do a dir on String, some methods that are from other interfaces such as + #(e.g.: if we do a dir on String, some methods that are from other interfaces such as #charAt don't appear) d = dir(original) for name in d: if found.get(name) != 1: ret.append(name) - + return ret def formatArg(arg): '''formats an argument to be shown ''' - + s = str(arg) dot = s.rfind('.') if dot >= 0: s = s[dot + 1:] - + s = s.replace(';', '') s = s.replace('[]', 'Array') if len(s) > 0: @@ -364,13 +369,13 @@ def formatArg(arg): s = c + s[1:] return s - - - + + + def Search(data): '''@return file, line, col ''' - + data = data.replace('\n', '') if data.endswith('.'): data = data.rstrip('.') @@ -379,8 +384,8 @@ def Search(data): return DoFind(f, mod), foundAs except: return DoFind(f, parent), foundAs - - + + def GenerateImportsTipForModule(obj_to_complete, dirComps=None, getattr=getattr, filter=lambda name:True): ''' @param obj_to_complete: the object from where we should get the completions @@ -391,18 +396,18 @@ def GenerateImportsTipForModule(obj_to_complete, dirComps=None, getattr=getattr, name, doc, args, type (from the TYPE_* constants) ''' ret = [] - + if dirComps is None: dirComps = dirObj(obj_to_complete) - + for d in dirComps: if d is None: continue - + if not filter(d): continue - + args = '' doc = '' retType = TYPE_BUILTIN @@ -421,7 +426,7 @@ def GenerateImportsTipForModule(obj_to_complete, dirComps=None, getattr=getattr, #note: this only happens when we add things to the sys.path at runtime, if they are added to the classpath #before the run, everything goes fine. # - #The code below ilustrates what I mean... + #The code below ilustrates what I mean... # #import sys #sys.path.insert(1, r"C:\bin\eclipse310\plugins\org.junit_3.8.1\junit.jar" ) @@ -429,7 +434,7 @@ def GenerateImportsTipForModule(obj_to_complete, dirComps=None, getattr=getattr, #import junit.framework #print_ dir(junit.framework) #shows the TestCase class here # - #import junit.framework.TestCase + #import junit.framework.TestCase # #raises the error: #Traceback (innermost last): @@ -458,19 +463,19 @@ def GenerateImportsTipForModule(obj_to_complete, dirComps=None, getattr=getattr, except TypeError: traceback.print_exc() args = '()' - + retType = TYPE_FUNCTION - + elif isclass(obj): retType = TYPE_CLASS - + elif ismodule(obj): retType = TYPE_IMPORT - + #add token and doc to return - assure only strings. ret.append((d, doc, args, retType)) - - + + return ret diff --git a/python/helpers/pydev/_pydev_threading.py b/python/helpers/pydev/_pydev_threading.py index 52d48c93ed3e..62b300b86cd0 100644 --- a/python/helpers/pydev/_pydev_threading.py +++ b/python/helpers/pydev/_pydev_threading.py @@ -1,982 +1,14 @@ -"""Thread module emulating a subset of Java's threading model.""" +from threading import enumerate, currentThread, Condition, Event, Timer, Thread +try: + from threading import settrace +except: + pass -import sys as _sys try: - import _pydev_thread as thread -except ImportError: - import thread - -import warnings - -from _pydev_time import time as _time, sleep as _sleep -from traceback import format_exc as _format_exc - -# Note regarding PEP 8 compliant aliases -# This threading model was originally inspired by Java, and inherited -# the convention of camelCase function and method names from that -# language. While those names are not in any imminent danger of being -# deprecated, starting with Python 2.6, the module now provides a -# PEP 8 compliant alias for any such method name. -# Using the new PEP 8 compliant names also facilitates substitution -# with the multiprocessing module, which doesn't provide the old -# Java inspired names. - - -# Rename some stuff so "from threading import *" is safe -__all__ = ['activeCount', 'active_count', 'Condition', 'currentThread', - 'current_thread', 'enumerate', 'Event', - 'Lock', 'RLock', 'Semaphore', 'BoundedSemaphore', 'Thread', - 'Timer', 'setprofile', 'settrace', 'local', 'stack_size'] - -_start_new_thread = thread.start_new_thread -_allocate_lock = thread.allocate_lock -_get_ident = thread.get_ident -ThreadError = thread.error -del thread - - -# sys.exc_clear is used to work around the fact that except blocks -# don't fully clear the exception until 3.0. -warnings.filterwarnings('ignore', category=DeprecationWarning, - module='threading', message='sys.exc_clear') - -# Debug support (adapted from ihooks.py). -# All the major classes here derive from _Verbose. We force that to -# be a new-style class so that all the major classes here are new-style. -# This helps debugging (type(instance) is more revealing for instances -# of new-style classes). - -_VERBOSE = False - -if __debug__: - - class _Verbose(object): - - def __init__(self, verbose=None): - if verbose is None: - verbose = _VERBOSE - self.__verbose = verbose - - def _note(self, format, *args): - if self.__verbose: - format = format % args - # Issue #4188: calling current_thread() can incur an infinite - # recursion if it has to create a DummyThread on the fly. - ident = _get_ident() - try: - name = _active[ident].name - except KeyError: - name = "" % ident - format = "%s: %s\n" % (name, format) - _sys.stderr.write(format) - -else: - # Disable this when using "python -O" - class _Verbose(object): - def __init__(self, verbose=None): - pass - def _note(self, *args): - pass - -# Support for profile and trace hooks - -_profile_hook = None -_trace_hook = None - -def setprofile(func): - global _profile_hook - _profile_hook = func - -def settrace(func): - global _trace_hook - _trace_hook = func - -# Synchronization classes - -Lock = _allocate_lock - -def RLock(*args, **kwargs): - return _RLock(*args, **kwargs) - -class _RLock(_Verbose): - - def __init__(self, verbose=None): - _Verbose.__init__(self, verbose) - self.__block = _allocate_lock() - self.__owner = None - self.__count = 0 - - def __repr__(self): - owner = self.__owner - try: - owner = _active[owner].name - except KeyError: - pass - return "<%s owner=%r count=%d>" % ( - self.__class__.__name__, owner, self.__count) - - def acquire(self, blocking=1): - me = _get_ident() - if self.__owner == me: - self.__count = self.__count + 1 - if __debug__: - self._note("%s.acquire(%s): recursive success", self, blocking) - return 1 - rc = self.__block.acquire(blocking) - if rc: - self.__owner = me - self.__count = 1 - if __debug__: - self._note("%s.acquire(%s): initial success", self, blocking) - else: - if __debug__: - self._note("%s.acquire(%s): failure", self, blocking) - return rc - - __enter__ = acquire - - def release(self): - if self.__owner != _get_ident(): - raise RuntimeError("cannot release un-acquired lock") - self.__count = count = self.__count - 1 - if not count: - self.__owner = None - self.__block.release() - if __debug__: - self._note("%s.release(): final release", self) - else: - if __debug__: - self._note("%s.release(): non-final release", self) - - def __exit__(self, t, v, tb): - self.release() - - # Internal methods used by condition variables - - def _acquire_restore(self, count_owner): - count, owner = count_owner - self.__block.acquire() - self.__count = count - self.__owner = owner - if __debug__: - self._note("%s._acquire_restore()", self) - - def _release_save(self): - if __debug__: - self._note("%s._release_save()", self) - count = self.__count - self.__count = 0 - owner = self.__owner - self.__owner = None - self.__block.release() - return (count, owner) - - def _is_owned(self): - return self.__owner == _get_ident() - - -def Condition(*args, **kwargs): - return _Condition(*args, **kwargs) - -class _Condition(_Verbose): - - def __init__(self, lock=None, verbose=None): - _Verbose.__init__(self, verbose) - if lock is None: - lock = RLock() - self.__lock = lock - # Export the lock's acquire() and release() methods - self.acquire = lock.acquire - self.release = lock.release - # If the lock defines _release_save() and/or _acquire_restore(), - # these override the default implementations (which just call - # release() and acquire() on the lock). Ditto for _is_owned(). - try: - self._release_save = lock._release_save - except AttributeError: - pass - try: - self._acquire_restore = lock._acquire_restore - except AttributeError: - pass - try: - self._is_owned = lock._is_owned - except AttributeError: - pass - self.__waiters = [] - - def __enter__(self): - return self.__lock.__enter__() - - def __exit__(self, *args): - return self.__lock.__exit__(*args) - - def __repr__(self): - return "" % (self.__lock, len(self.__waiters)) - - def _release_save(self): - self.__lock.release() # No state to save - - def _acquire_restore(self, x): - self.__lock.acquire() # Ignore saved state - - def _is_owned(self): - # Return True if lock is owned by current_thread. - # This method is called only if __lock doesn't have _is_owned(). - if self.__lock.acquire(0): - self.__lock.release() - return False - else: - return True - - def wait(self, timeout=None): - if not self._is_owned(): - raise RuntimeError("cannot wait on un-acquired lock") - waiter = _allocate_lock() - waiter.acquire() - self.__waiters.append(waiter) - saved_state = self._release_save() - try: # restore state no matter what (e.g., KeyboardInterrupt) - if timeout is None: - waiter.acquire() - if __debug__: - self._note("%s.wait(): got it", self) - else: - # Balancing act: We can't afford a pure busy loop, so we - # have to sleep; but if we sleep the whole timeout time, - # we'll be unresponsive. The scheme here sleeps very - # little at first, longer as time goes on, but never longer - # than 20 times per second (or the timeout time remaining). - endtime = _time() + timeout - delay = 0.0005 # 500 us -> initial delay of 1 ms - while True: - gotit = waiter.acquire(0) - if gotit: - break - remaining = endtime - _time() - if remaining <= 0: - break - delay = min(delay * 2, remaining, .05) - _sleep(delay) - if not gotit: - if __debug__: - self._note("%s.wait(%s): timed out", self, timeout) - try: - self.__waiters.remove(waiter) - except ValueError: - pass - else: - if __debug__: - self._note("%s.wait(%s): got it", self, timeout) - finally: - self._acquire_restore(saved_state) - - def notify(self, n=1): - if not self._is_owned(): - raise RuntimeError("cannot notify on un-acquired lock") - __waiters = self.__waiters - waiters = __waiters[:n] - if not waiters: - if __debug__: - self._note("%s.notify(): no waiters", self) - return - self._note("%s.notify(): notifying %d waiter%s", self, n, - n!=1 and "s" or "") - for waiter in waiters: - waiter.release() - try: - __waiters.remove(waiter) - except ValueError: - pass - - def notifyAll(self): - self.notify(len(self.__waiters)) - - notify_all = notifyAll - - -def Semaphore(*args, **kwargs): - return _Semaphore(*args, **kwargs) - -class _Semaphore(_Verbose): - - # After Tim Peters' semaphore class, but not quite the same (no maximum) - - def __init__(self, value=1, verbose=None): - if value < 0: - raise ValueError("semaphore initial value must be >= 0") - _Verbose.__init__(self, verbose) - self.__cond = Condition(Lock()) - self.__value = value - - def acquire(self, blocking=1): - rc = False - self.__cond.acquire() - while self.__value == 0: - if not blocking: - break - if __debug__: - self._note("%s.acquire(%s): blocked waiting, value=%s", - self, blocking, self.__value) - self.__cond.wait() - else: - self.__value = self.__value - 1 - if __debug__: - self._note("%s.acquire: success, value=%s", - self, self.__value) - rc = True - self.__cond.release() - return rc - - __enter__ = acquire - - def release(self): - self.__cond.acquire() - self.__value = self.__value + 1 - if __debug__: - self._note("%s.release: success, value=%s", - self, self.__value) - self.__cond.notify() - self.__cond.release() - - def __exit__(self, t, v, tb): - self.release() - - -def BoundedSemaphore(*args, **kwargs): - return _BoundedSemaphore(*args, **kwargs) - -class _BoundedSemaphore(_Semaphore): - """Semaphore that checks that # releases is <= # acquires""" - def __init__(self, value=1, verbose=None): - _Semaphore.__init__(self, value, verbose) - self._initial_value = value - - def release(self): - if self._Semaphore__value >= self._initial_value: - raise ValueError, "Semaphore released too many times" - return _Semaphore.release(self) - - -def Event(*args, **kwargs): - return _Event(*args, **kwargs) - -class _Event(_Verbose): - - # After Tim Peters' event class (without is_posted()) - - def __init__(self, verbose=None): - _Verbose.__init__(self, verbose) - self.__cond = Condition(Lock()) - self.__flag = False - - def _reset_internal_locks(self): - # private! called by Thread._reset_internal_locks by _after_fork() - self.__cond.__init__() - - def isSet(self): - return self.__flag - - is_set = isSet - - def set(self): - self.__cond.acquire() - try: - self.__flag = True - self.__cond.notify_all() - finally: - self.__cond.release() - - def clear(self): - self.__cond.acquire() - try: - self.__flag = False - finally: - self.__cond.release() - - def wait(self, timeout=None): - self.__cond.acquire() - try: - if not self.__flag: - self.__cond.wait(timeout) - return self.__flag - finally: - self.__cond.release() - -# Helper to generate new thread names -_counter = 0 -def _newname(template="Thread-%d"): - global _counter - _counter = _counter + 1 - return template % _counter - -# Active thread administration -_active_limbo_lock = _allocate_lock() -_active = {} # maps thread id to Thread object -_limbo = {} - - -# Main class for threads - -class Thread(_Verbose): - - __initialized = False - # Need to store a reference to sys.exc_info for printing - # out exceptions when a thread tries to use a global var. during interp. - # shutdown and thus raises an exception about trying to perform some - # operation on/with a NoneType - __exc_info = _sys.exc_info - # Keep sys.exc_clear too to clear the exception just before - # allowing .join() to return. - __exc_clear = _sys.exc_clear - - def __init__(self, group=None, target=None, name=None, - args=(), kwargs=None, verbose=None): - assert group is None, "group argument must be None for now" - _Verbose.__init__(self, verbose) - if kwargs is None: - kwargs = {} - self.__target = target - self.__name = str(name or _newname()) - self.__args = args - self.__kwargs = kwargs - self.__daemonic = self._set_daemon() - self.__ident = None - self.__started = Event() - self.__stopped = False - self.__block = Condition(Lock()) - self.__initialized = True - # sys.stderr is not stored in the class like - # sys.exc_info since it can be changed between instances - self.__stderr = _sys.stderr - - def _reset_internal_locks(self): - # private! Called by _after_fork() to reset our internal locks as - # they may be in an invalid state leading to a deadlock or crash. - if hasattr(self, '_Thread__block'): # DummyThread deletes self.__block - self.__block.__init__() - self.__started._reset_internal_locks() - - @property - def _block(self): - # used by a unittest - return self.__block - - def _set_daemon(self): - # Overridden in _MainThread and _DummyThread - return current_thread().daemon - - def __repr__(self): - assert self.__initialized, "Thread.__init__() was not called" - status = "initial" - if self.__started.is_set(): - status = "started" - if self.__stopped: - status = "stopped" - if self.__daemonic: - status += " daemon" - if self.__ident is not None: - status += " %s" % self.__ident - return "<%s(%s, %s)>" % (self.__class__.__name__, self.__name, status) - - def start(self): - if not self.__initialized: - raise RuntimeError("thread.__init__() not called") - if self.__started.is_set(): - raise RuntimeError("threads can only be started once") - if __debug__: - self._note("%s.start(): starting thread", self) - with _active_limbo_lock: - _limbo[self] = self - try: - _start_new_thread(self.__bootstrap, ()) - except Exception: - with _active_limbo_lock: - del _limbo[self] - raise - self.__started.wait() - - def run(self): - try: - if self.__target: - self.__target(*self.__args, **self.__kwargs) - finally: - # Avoid a refcycle if the thread is running a function with - # an argument that has a member that points to the thread. - del self.__target, self.__args, self.__kwargs - - def __bootstrap(self): - # Wrapper around the real bootstrap code that ignores - # exceptions during interpreter cleanup. Those typically - # happen when a daemon thread wakes up at an unfortunate - # moment, finds the world around it destroyed, and raises some - # random exception *** while trying to report the exception in - # __bootstrap_inner() below ***. Those random exceptions - # don't help anybody, and they confuse users, so we suppress - # them. We suppress them only when it appears that the world - # indeed has already been destroyed, so that exceptions in - # __bootstrap_inner() during normal business hours are properly - # reported. Also, we only suppress them for daemonic threads; - # if a non-daemonic encounters this, something else is wrong. - try: - self.__bootstrap_inner() - except: - if self.__daemonic and _sys is None: - return - raise - - def _set_ident(self): - self.__ident = _get_ident() - - def __bootstrap_inner(self): - try: - self._set_ident() - self.__started.set() - with _active_limbo_lock: - _active[self.__ident] = self - del _limbo[self] - if __debug__: - self._note("%s.__bootstrap(): thread started", self) - - if _trace_hook: - self._note("%s.__bootstrap(): registering trace hook", self) - _sys.settrace(_trace_hook) - if _profile_hook: - self._note("%s.__bootstrap(): registering profile hook", self) - _sys.setprofile(_profile_hook) - - try: - self.run() - except SystemExit: - if __debug__: - self._note("%s.__bootstrap(): raised SystemExit", self) - except: - if __debug__: - self._note("%s.__bootstrap(): unhandled exception", self) - # If sys.stderr is no more (most likely from interpreter - # shutdown) use self.__stderr. Otherwise still use sys (as in - # _sys) in case sys.stderr was redefined since the creation of - # self. - if _sys: - _sys.stderr.write("Exception in thread %s:\n%s\n" % - (self.name, _format_exc())) - else: - # Do the best job possible w/o a huge amt. of code to - # approximate a traceback (code ideas from - # Lib/traceback.py) - exc_type, exc_value, exc_tb = self.__exc_info() - try: - print>>self.__stderr, ( - "Exception in thread " + self.name + - " (most likely raised during interpreter shutdown):") - print>>self.__stderr, ( - "Traceback (most recent call last):") - while exc_tb: - print>>self.__stderr, ( - ' File "%s", line %s, in %s' % - (exc_tb.tb_frame.f_code.co_filename, - exc_tb.tb_lineno, - exc_tb.tb_frame.f_code.co_name)) - exc_tb = exc_tb.tb_next - print>>self.__stderr, ("%s: %s" % (exc_type, exc_value)) - # Make sure that exc_tb gets deleted since it is a memory - # hog; deleting everything else is just for thoroughness - finally: - del exc_type, exc_value, exc_tb - else: - if __debug__: - self._note("%s.__bootstrap(): normal return", self) - finally: - # Prevent a race in - # test_threading.test_no_refcycle_through_target when - # the exception keeps the target alive past when we - # assert that it's dead. - self.__exc_clear() - finally: - with _active_limbo_lock: - self.__stop() - try: - # We don't call self.__delete() because it also - # grabs _active_limbo_lock. - del _active[_get_ident()] - except: - pass - - def __stop(self): - self.__block.acquire() - self.__stopped = True - self.__block.notify_all() - self.__block.release() - - def __delete(self): - "Remove current thread from the dict of currently running threads." - - # Notes about running with dummy_thread: - # - # Must take care to not raise an exception if dummy_thread is being - # used (and thus this module is being used as an instance of - # dummy_threading). dummy_thread.get_ident() always returns -1 since - # there is only one thread if dummy_thread is being used. Thus - # len(_active) is always <= 1 here, and any Thread instance created - # overwrites the (if any) thread currently registered in _active. - # - # An instance of _MainThread is always created by 'threading'. This - # gets overwritten the instant an instance of Thread is created; both - # threads return -1 from dummy_thread.get_ident() and thus have the - # same key in the dict. So when the _MainThread instance created by - # 'threading' tries to clean itself up when atexit calls this method - # it gets a KeyError if another Thread instance was created. - # - # This all means that KeyError from trying to delete something from - # _active if dummy_threading is being used is a red herring. But - # since it isn't if dummy_threading is *not* being used then don't - # hide the exception. - - try: - with _active_limbo_lock: - del _active[_get_ident()] - # There must not be any python code between the previous line - # and after the lock is released. Otherwise a tracing function - # could try to acquire the lock again in the same thread, (in - # current_thread()), and would block. - except KeyError: - if 'dummy_threading' not in _sys.modules: - raise - - def join(self, timeout=None): - if not self.__initialized: - raise RuntimeError("Thread.__init__() not called") - if not self.__started.is_set(): - raise RuntimeError("cannot join thread before it is started") - if self is current_thread(): - raise RuntimeError("cannot join current thread") - - if __debug__: - if not self.__stopped: - self._note("%s.join(): waiting until thread stops", self) - self.__block.acquire() - try: - if timeout is None: - while not self.__stopped: - self.__block.wait() - if __debug__: - self._note("%s.join(): thread stopped", self) - else: - deadline = _time() + timeout - while not self.__stopped: - delay = deadline - _time() - if delay <= 0: - if __debug__: - self._note("%s.join(): timed out", self) - break - self.__block.wait(delay) - else: - if __debug__: - self._note("%s.join(): thread stopped", self) - finally: - self.__block.release() - - @property - def name(self): - assert self.__initialized, "Thread.__init__() not called" - return self.__name - - @name.setter - def name(self, name): - assert self.__initialized, "Thread.__init__() not called" - self.__name = str(name) - - @property - def ident(self): - assert self.__initialized, "Thread.__init__() not called" - return self.__ident - - def isAlive(self): - assert self.__initialized, "Thread.__init__() not called" - return self.__started.is_set() and not self.__stopped - - is_alive = isAlive - - @property - def daemon(self): - assert self.__initialized, "Thread.__init__() not called" - return self.__daemonic - - @daemon.setter - def daemon(self, daemonic): - if not self.__initialized: - raise RuntimeError("Thread.__init__() not called") - if self.__started.is_set(): - raise RuntimeError("cannot set daemon status of active thread"); - self.__daemonic = daemonic - - def isDaemon(self): - return self.daemon - - def setDaemon(self, daemonic): - self.daemon = daemonic - - def getName(self): - return self.name - - def setName(self, name): - self.name = name - -# The timer class was contributed by Itamar Shtull-Trauring - -def Timer(*args, **kwargs): - return _Timer(*args, **kwargs) - -class _Timer(Thread): - """Call a function after a specified number of seconds: - - t = Timer(30.0, f, args=[], kwargs={}) - t.start() - t.cancel() # stop the timer's action if it's still waiting - """ - - def __init__(self, interval, function, args=[], kwargs={}): - Thread.__init__(self) - self.interval = interval - self.function = function - self.args = args - self.kwargs = kwargs - self.finished = Event() - - def cancel(self): - """Stop the timer if it hasn't finished yet""" - self.finished.set() - - def run(self): - self.finished.wait(self.interval) - if not self.finished.is_set(): - self.function(*self.args, **self.kwargs) - self.finished.set() - -# Special thread class to represent the main thread -# This is garbage collected through an exit handler - -class _MainThread(Thread): - - def __init__(self): - Thread.__init__(self, name="MainThread") - self._Thread__started.set() - self._set_ident() - with _active_limbo_lock: - _active[_get_ident()] = self - - def _set_daemon(self): - return False - - def _exitfunc(self): - self._Thread__stop() - t = _pickSomeNonDaemonThread() - if t: - if __debug__: - self._note("%s: waiting for other threads", self) - while t: - t.join() - t = _pickSomeNonDaemonThread() - if __debug__: - self._note("%s: exiting", self) - self._Thread__delete() - -def _pickSomeNonDaemonThread(): - for t in enumerate(): - if not t.daemon and t.is_alive(): - return t - return None - - -# Dummy thread class to represent threads not started here. -# These aren't garbage collected when they die, nor can they be waited for. -# If they invoke anything in threading.py that calls current_thread(), they -# leave an entry in the _active dict forever after. -# Their purpose is to return *something* from current_thread(). -# They are marked as daemon threads so we won't wait for them -# when we exit (conform previous semantics). - -class _DummyThread(Thread): - - def __init__(self): - Thread.__init__(self, name=_newname("Dummy-%d")) - - # Thread.__block consumes an OS-level locking primitive, which - # can never be used by a _DummyThread. Since a _DummyThread - # instance is immortal, that's bad, so release this resource. - del self._Thread__block - - self._Thread__started.set() - self._set_ident() - with _active_limbo_lock: - _active[_get_ident()] = self - - def _set_daemon(self): - return True - - def join(self, timeout=None): - assert False, "cannot join a dummy thread" - - -# Global API functions - -def currentThread(): - try: - return _active[_get_ident()] - except KeyError: - ##print "current_thread(): no current thread for", _get_ident() - return _DummyThread() - -current_thread = currentThread - -def activeCount(): - with _active_limbo_lock: - return len(_active) + len(_limbo) - -active_count = activeCount - -def _enumerate(): - # Same as enumerate(), but without the lock. Internal use only. - return _active.values() + _limbo.values() - -def enumerate(): - with _active_limbo_lock: - return _active.values() + _limbo.values() - -# Create the main thread object, -# and make it available for the interpreter -# (Py_Main) as threading._shutdown. - -_shutdown = _MainThread()._exitfunc - -# get thread-local implementation, either from the thread -# module, or from the python fallback - -try: - from _pydev_thread import _local as local -except ImportError: - from _threading_local import local - - -def _after_fork(): - # This function is called by Python/ceval.c:PyEval_ReInitThreads which - # is called from PyOS_AfterFork. Here we cleanup threading module state - # that should not exist after a fork. - - # Reset _active_limbo_lock, in case we forked while the lock was held - # by another (non-forked) thread. http://bugs.python.org/issue874900 - global _active_limbo_lock - _active_limbo_lock = _allocate_lock() - - # fork() only copied the current thread; clear references to others. - new_active = {} - current = current_thread() - with _active_limbo_lock: - for thread in _active.itervalues(): - # Any lock/condition variable may be currently locked or in an - # invalid state, so we reinitialize them. - if hasattr(thread, '_reset_internal_locks'): - thread._reset_internal_locks() - if thread is current: - # There is only one active thread. We reset the ident to - # its new value since it can have changed. - ident = _get_ident() - thread._Thread__ident = ident - new_active[ident] = thread - else: - # All the others are already stopped. - thread._Thread__stop() - - _limbo.clear() - _active.clear() - _active.update(new_active) - assert len(_active) == 1 - - -# Self-test code - -def _test(): - - class BoundedQueue(_Verbose): - - def __init__(self, limit): - _Verbose.__init__(self) - self.mon = RLock() - self.rc = Condition(self.mon) - self.wc = Condition(self.mon) - self.limit = limit - self.queue = deque() - - def put(self, item): - self.mon.acquire() - while len(self.queue) >= self.limit: - self._note("put(%s): queue full", item) - self.wc.wait() - self.queue.append(item) - self._note("put(%s): appended, length now %d", - item, len(self.queue)) - self.rc.notify() - self.mon.release() - - def get(self): - self.mon.acquire() - while not self.queue: - self._note("get(): queue empty") - self.rc.wait() - item = self.queue.popleft() - self._note("get(): got %s, %d left", item, len(self.queue)) - self.wc.notify() - self.mon.release() - return item - - class ProducerThread(Thread): - - def __init__(self, queue, quota): - Thread.__init__(self, name="Producer") - self.queue = queue - self.quota = quota - - def run(self): - from random import random - counter = 0 - while counter < self.quota: - counter = counter + 1 - self.queue.put("%s.%d" % (self.name, counter)) - _sleep(random() * 0.00001) - - - class ConsumerThread(Thread): - - def __init__(self, queue, count): - Thread.__init__(self, name="Consumer") - self.queue = queue - self.count = count - - def run(self): - while self.count > 0: - item = self.queue.get() - print item - self.count = self.count - 1 - - NP = 3 - QL = 4 - NI = 5 - - Q = BoundedQueue(QL) - P = [] - for i in range(NP): - t = ProducerThread(Q, NI) - t.name = ("Producer-%d" % (i+1)) - P.append(t) - C = ConsumerThread(Q, NI*NP) - for t in P: - t.start() - _sleep(0.000001) - C.start() - for t in P: - t.join() - C.join() - -if __name__ == '__main__': - _test() + from gevent import monkey + saved = monkey.saved['threading'] + for key, val in saved.items(): + globals()[key] = val +except: + pass diff --git a/python/helpers/pydev/_pydev_tipper_common.py b/python/helpers/pydev/_pydev_tipper_common.py index f8c46d232b10..8e6267fd27ea 100644 --- a/python/helpers/pydev/_pydev_tipper_common.py +++ b/python/helpers/pydev/_pydev_tipper_common.py @@ -2,7 +2,7 @@ try: import inspect except: try: - import _pydev_inspect as inspect # for older versions + from _pydev_imps import _pydev_inspect as inspect except: import traceback;traceback.print_exc() #Ok, no inspect available (search will not work) @@ -10,57 +10,58 @@ try: import re except: try: - import _pydev_re as re # for older versions @UnresolvedImport + import sre as re # for older versions except: import traceback;traceback.print_exc() #Ok, no inspect available (search will not work) +from pydevd_constants import xrange def DoFind(f, mod): import linecache if inspect.ismodule(mod): return f, 0, 0 - + lines = linecache.getlines(f) - + if inspect.isclass(mod): name = mod.__name__ pat = re.compile(r'^\s*class\s*' + name + r'\b') - for i in range(len(lines)): - if pat.match(lines[i]): + for i in xrange(len(lines)): + if pat.match(lines[i]): return f, i, 0 - + return f, 0, 0 if inspect.ismethod(mod): mod = mod.im_func - + if inspect.isfunction(mod): try: mod = mod.func_code except AttributeError: mod = mod.__code__ #python 3k - + if inspect.istraceback(mod): mod = mod.tb_frame - + if inspect.isframe(mod): mod = mod.f_code if inspect.iscode(mod): if not hasattr(mod, 'co_filename'): return None, 0, 0 - + if not hasattr(mod, 'co_firstlineno'): return mod.co_filename, 0, 0 - + lnum = mod.co_firstlineno pat = re.compile(r'^(\s*def\s)|(.*(? 0: - if pat.match(lines[lnum]): + if pat.match(lines[lnum]): break lnum -= 1 - + return f, lnum, 0 raise RuntimeError('Do not know about: ' + f + ' ' + str(mod)) diff --git a/python/helpers/pydev/fix_getpass.py b/python/helpers/pydev/fix_getpass.py index c81d93523238..160acc8d1be1 100644 --- a/python/helpers/pydev/fix_getpass.py +++ b/python/helpers/pydev/fix_getpass.py @@ -1,10 +1,13 @@ def fixGetpass(): - import getpass - import warnings - fallback = getattr(getpass, 'fallback_getpass', None) # >= 2.6 - if not fallback: - fallback = getpass.default_getpass # <= 2.5 - getpass.getpass = fallback - if hasattr(getpass, 'GetPassWarning'): - warnings.simplefilter("ignore", category=getpass.GetPassWarning) + try: + import getpass + except ImportError: + return #If we can't import it, we can't fix it + import warnings + fallback = getattr(getpass, 'fallback_getpass', None) # >= 2.6 + if not fallback: + fallback = getpass.default_getpass # <= 2.5 + getpass.getpass = fallback + if hasattr(getpass, 'GetPassWarning'): + warnings.simplefilter("ignore", category=getpass.GetPassWarning) diff --git a/python/helpers/pydev/merge_pydev_pycharm.txt b/python/helpers/pydev/merge_pydev_pycharm.txt index e15eaab31c91..e5e10f7421a1 100644 --- a/python/helpers/pydev/merge_pydev_pycharm.txt +++ b/python/helpers/pydev/merge_pydev_pycharm.txt @@ -35,6 +35,25 @@ Done in the merge (started from the PyCharm version and bringing in things from - When the code is interrupted, the buffer in the python side is cleared. +- GEvent debugging: improved PyDev not to use the threading module (uses the thread + primitives directly), so, gevent debugging can work even if pydevd is used for + remote debugging. + + Also, the gevent debugging should probably be closer to the stackless debugging, + where we actually show the live stackless threads -- so, we should show the live + gevent greenlets -- which the current version doesn't do (future work). + +- Supporting Jython 2.2 onwards (note: CPython only tested with 2.7/3.3) + +- When there are big sets/tuples/lists/dicts, the items won't be all shown so that the + debugger speed doesn't suffer (the user should use the console if he wants to see + those items in this case). The limit was set to show up to 300 items (pydevd_resolver.MAX_ITEMS_TO_HANDLE) + +- Monkey-patching qt (QThread/QRunnable) to enable the debugger to work. + Notes: + - It must be imported before the user actually runs its code (as the definitions of QThread/QRunnable + are monkey-patched), so, for the remote debugger to work, pydevd must be imported at the start of + the program, even if pydevd.settrace will only be used later on. Things to be fixed in PyCharm: -------------------------------- @@ -110,27 +129,14 @@ Things to be fixed in PyCharm: Things to be fixed in PyDev: -------------------------------- -1. Provide UI for 'smart step into' +.Provide UI for 'smart step into' (later) -2. Check what to do with 'message' from xml. +. Check what to do with 'message' from xml (later) -3. Enable/test GEvent debugging (environment variable: GEVENT_SUPPORT). +. Deal with sendSignatureCallTrace (later) -4. Deal with sendSignatureCallTrace. +. Set IPYTHONENABLE to False/True to use IPython console (later) -5. Work with Django exceptions: CMD_ADD_DJANGO_EXCEPTION_BREAK/CMD_REMOVE_DJANGO_EXCEPTION_BREAK. - -6. Check console in debug frame. - -7. Set IPYTHONENABLE to False/True to use IPython console. - -TODO: --------- -sendSignatureCallTrace: don't call if not needed - -Console integration (IPython and 'regular') (interrupt) - -Add back: CMD_EVALUATE_CONSOLE_EXPRESSION Manual test: --------------- diff --git a/python/helpers/pydev/pycompletion.py b/python/helpers/pydev/pycompletion.py index e706d5410f3d..33697806c102 100644 --- a/python/helpers/pydev/pycompletion.py +++ b/python/helpers/pydev/pycompletion.py @@ -2,12 +2,10 @@ ''' @author Radim Kubacki ''' -import __builtin__ import _pydev_imports_tipper import traceback import StringIO import sys -import time import urllib import pycompletionserver @@ -24,7 +22,7 @@ def GetImports(module_name): except: s = StringIO.StringIO() exc_info = sys.exc_info() - + traceback.print_exception(exc_info[0], exc_info[1], exc_info[2], limit=None, file=s) err = s.getvalue() pycompletionserver.dbg('Received error: ' + str(err), pycompletionserver.ERROR) @@ -38,4 +36,4 @@ if __name__ == '__main__': mod_name = sys.argv[1] print(GetImports(mod_name)) - + diff --git a/python/helpers/pydev/pycompletionserver.py b/python/helpers/pydev/pycompletionserver.py index 2fdd53903fb1..154ad630aca3 100644 --- a/python/helpers/pydev/pycompletionserver.py +++ b/python/helpers/pydev/pycompletionserver.py @@ -17,30 +17,21 @@ except NameError: setattr(__builtin__, 'True', 1) # Python 3.0 does not accept __builtin__.True = 1 in its syntax setattr(__builtin__, 'False', 0) -import pydevd_constants +from pydevd_constants import IS_JYTHON -try: - from java.lang import Thread - IS_JYTHON = True +if IS_JYTHON: + import java.lang SERVER_NAME = 'jycompletionserver' import _pydev_jy_imports_tipper # as _pydev_imports_tipper #changed to be backward compatible with 1.5 _pydev_imports_tipper = _pydev_jy_imports_tipper -except ImportError: +else: # it is python - IS_JYTHON = False SERVER_NAME = 'pycompletionserver' - if pydevd_constants.USE_LIB_COPY: - from _pydev_threading import Thread - else: - from threading import Thread import _pydev_imports_tipper -if pydevd_constants.USE_LIB_COPY: - import _pydev_socket as socket -else: - import socket +from _pydev_imps import _pydev_socket as socket import sys if sys.platform == "darwin": @@ -65,10 +56,7 @@ for name, mod in sys.modules.items(): import traceback -if pydevd_constants.USE_LIB_COPY: - import _pydev_time as time -else: - import time +from _pydev_imps import _pydev_time as time try: import StringIO @@ -93,7 +81,7 @@ def dbg(s, prior): # f = open('c:/temp/test.txt', 'a') # print_ >> f, s # f.close() - + import pydev_localhost HOST = pydev_localhost.get_localhost() # Symbolic name meaning the local host @@ -191,19 +179,21 @@ class Processor: return '%s(%s)%s' % (MSG_COMPLETIONS, ''.join(compMsg), MSG_END) +class Exit(Exception): + pass -class T(Thread): +class CompletionServer: def __init__(self, port): - Thread.__init__(self) self.ended = False self.port = port self.socket = None # socket to send messages. + self.exit_process_on_kill = True self.processor = Processor() def connectToServer(self): - import socket + from _pydev_imps import _pydev_socket as socket self.socket = s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: @@ -272,7 +262,7 @@ class T(Thread): while data.find(MSG_END) == -1: received = self.socket.recv(BUFFER_SIZE) if len(received) == 0: - sys.exit(0) # ok, connection ended + raise Exit() # ok, connection ended if IS_PYTHON3K: data = data + received.decode('utf-8') else: @@ -284,7 +274,7 @@ class T(Thread): dbg(SERVER_NAME + ' kill message received', INFO1) # break if we received kill message. self.ended = True - sys.exit(0) + raise Exit() dbg(SERVER_NAME + ' starting keep alive thread', INFO2) @@ -365,7 +355,7 @@ class T(Thread): else: self.send(MSG_INVALID_REQUEST) - except SystemExit: + except Exit: self.send(self.getCompletionsMessage(None, [('Exit:', 'SystemExit', '')])) raise @@ -384,11 +374,12 @@ class T(Thread): self.socket.close() self.ended = True - sys.exit(0) # connection broken + raise Exit() # connection broken - except SystemExit: - raise + except Exit: + if self.exit_process_on_kill: + sys.exit(0) # No need to log SystemExit error except: s = StringIO.StringIO() @@ -405,8 +396,6 @@ if __name__ == '__main__': port = int(sys.argv[1]) # this is from where we want to receive messages. - t = T(port) + t = CompletionServer(port) dbg(SERVER_NAME + ' will start', INFO1) - t.start() - time.sleep(5) - t.join() + t.run() diff --git a/python/helpers/pydev/pydev_console_utils.py b/python/helpers/pydev/pydev_console_utils.py index b4c0e13b9201..6e532183b0d1 100644 --- a/python/helpers/pydev/pydev_console_utils.py +++ b/python/helpers/pydev/pydev_console_utils.py @@ -1,36 +1,11 @@ -from pydev_imports import xmlrpclib +from pydev_imports import xmlrpclib, _queue, Exec import sys - -import traceback - -from pydevd_constants import USE_LIB_COPY from pydevd_constants import IS_JYTHON - -try: - if USE_LIB_COPY: - import _pydev_Queue as _queue - else: - import Queue as _queue -except: - import queue as _queue - -try: - from pydevd_exec import Exec -except: - from pydevd_exec2 import Exec - -try: - if USE_LIB_COPY: - import _pydev_thread as thread - else: - import thread -except: - import _thread as thread - +from _pydev_imps import _pydev_thread as thread import pydevd_xml import pydevd_vars - -from pydevd_utils import * +from pydevd_utils import * # @UnusedWildImport +import traceback #======================================================================================================================= # Null @@ -212,8 +187,6 @@ class BaseInterpreterInterface: self._input_error_printed = True sys.stderr.write('\nError when trying to update pydoc.help.input\n') sys.stderr.write('(help() may not work -- please report this as a bug in the pydev bugtracker).\n\n') - import traceback - traceback.print_exc() try: @@ -244,8 +217,6 @@ class BaseInterpreterInterface: except SystemExit: raise except: - import traceback; - traceback.print_exc() return more @@ -375,7 +346,7 @@ class BaseInterpreterInterface: self.interruptable = True def get_server(self): - if self.host is not None: + if getattr(self, 'host', None) is not None: return xmlrpclib.Server('http://%s:%s' % (self.host, self.client_port)) else: return None @@ -420,7 +391,7 @@ class BaseInterpreterInterface: # Important: it has to be really enabled in the main thread, so, schedule # it to run in the main thread. - self.exec_queue.put(do_connect_to_debugger) + self.exec_queue.put(do_change_variable) def _findFrame(self, thread_id, frame_id): ''' @@ -446,15 +417,11 @@ class BaseInterpreterInterface: try: # Try to import the packages needed to attach the debugger import pydevd - import pydevd_vars - if USE_LIB_COPY: - import _pydev_threading as threading - else: - import threading + import _pydev_threading as threading except: # This happens on Jython embedded in host eclipse - import traceback;traceback.print_exc() + traceback.print_exc() sys.stderr.write('pydevd is not available, cannot connect\n',) import pydev_localhost @@ -470,7 +437,7 @@ class BaseInterpreterInterface: import pydevd_tracing pydevd_tracing.SetTrace(None) except: - import traceback;traceback.print_exc() + traceback.print_exc() sys.stderr.write('Failed to connect to target debugger.\n') # Register to process commands when idle @@ -479,7 +446,7 @@ class BaseInterpreterInterface: import pydevconsole pydevconsole.set_debug_hook(self.debugger.processInternalCommands) except: - import traceback;traceback.print_exc() + traceback.print_exc() sys.stderr.write('Version of Python does not support debuggable Interactive Console.\n') # Important: it has to be really enabled in the main thread, so, schedule @@ -505,7 +472,7 @@ class BaseInterpreterInterface: enable_gui(guiname) except: sys.stderr.write("Failed to enable GUI event loop integration for '%s'\n" % guiname) - import traceback;traceback.print_exc() + traceback.print_exc() elif guiname not in ['none', '', None]: # Only print a warning if the guiname was going to do something sys.stderr.write("PyDev console: Python version does not support GUI event loop integration for '%s'\n" % guiname) diff --git a/python/helpers/pydev/pydev_imports.py b/python/helpers/pydev/pydev_imports.py index 06858750c97d..c5132cffbfe3 100644 --- a/python/helpers/pydev/pydev_imports.py +++ b/python/helpers/pydev/pydev_imports.py @@ -1,49 +1,61 @@ -from pydevd_constants import USE_LIB_COPY +from pydevd_constants import USE_LIB_COPY, izip + + try: try: if USE_LIB_COPY: - import _pydev_xmlrpclib as xmlrpclib + from _pydev_imps import _pydev_xmlrpclib as xmlrpclib else: import xmlrpclib except ImportError: import xmlrpc.client as xmlrpclib except ImportError: - import _pydev_xmlrpclib as xmlrpclib + from _pydev_imps import _pydev_xmlrpclib as xmlrpclib + + try: try: if USE_LIB_COPY: - from _pydev_SimpleXMLRPCServer import SimpleXMLRPCServer + from _pydev_imps._pydev_SimpleXMLRPCServer import SimpleXMLRPCServer else: from SimpleXMLRPCServer import SimpleXMLRPCServer except ImportError: from xmlrpc.server import SimpleXMLRPCServer except ImportError: - from _pydev_SimpleXMLRPCServer import SimpleXMLRPCServer + from _pydev_imps._pydev_SimpleXMLRPCServer import SimpleXMLRPCServer + + + try: from StringIO import StringIO except ImportError: from io import StringIO + + try: execfile=execfile #Not in Py3k except NameError: - from _pydev_execfile import execfile + from _pydev_imps._pydev_execfile import execfile + + try: if USE_LIB_COPY: - import _pydev_Queue as _queue + from _pydev_imps import _pydev_Queue as _queue else: import Queue as _queue except: import queue as _queue #@UnresolvedImport + try: from pydevd_exec import Exec except: from pydevd_exec2 import Exec try: - from urllib import quote + from urllib import quote, quote_plus, unquote_plus except: - from urllib.parse import quote #@UnresolvedImport + from urllib.parse import quote, quote_plus, unquote_plus #@UnresolvedImport import os @@ -80,7 +92,7 @@ except: return start i = 0 - for start_seg, dest_seg in zip(orig_list, dest_list): + for start_seg, dest_seg in izip(orig_list, dest_list): if start_seg != os.path.normcase(dest_seg): break i += 1 diff --git a/python/helpers/pydev/pydev_ipython/inputhookglut.py b/python/helpers/pydev/pydev_ipython/inputhookglut.py index f0683ba5890e..477e48d3d22b 100644 --- a/python/helpers/pydev/pydev_ipython/inputhookglut.py +++ b/python/helpers/pydev/pydev_ipython/inputhookglut.py @@ -31,7 +31,7 @@ GLUT Inputhook support functions #----------------------------------------------------------------------------- import os import sys -import time +from _pydev_imps import _pydev_time as time import signal import OpenGL.GLUT as glut import OpenGL.platform as platform diff --git a/python/helpers/pydev/pydev_ipython/inputhookpyglet.py b/python/helpers/pydev/pydev_ipython/inputhookpyglet.py index 0cbb87f34b6d..94a8ac7adaba 100644 --- a/python/helpers/pydev/pydev_ipython/inputhookpyglet.py +++ b/python/helpers/pydev/pydev_ipython/inputhookpyglet.py @@ -22,7 +22,7 @@ Authors import os import sys -import time +from _pydev_imps import _pydev_time as time from timeit import default_timer as clock import pyglet from pydev_ipython.inputhook import stdin_ready diff --git a/python/helpers/pydev/pydev_ipython/inputhookqt4.py b/python/helpers/pydev/pydev_ipython/inputhookqt4.py index 27598fa742fe..b7e1cf0527a8 100644 --- a/python/helpers/pydev/pydev_ipython/inputhookqt4.py +++ b/python/helpers/pydev/pydev_ipython/inputhookqt4.py @@ -19,11 +19,7 @@ Author: Christian Boos import os import signal -from pydevd_constants import USE_LIB_COPY -if USE_LIB_COPY: - import _pydev_threading as threading -else: - import threading +import threading from pydev_ipython.qt_for_kernel import QtCore, QtGui diff --git a/python/helpers/pydev/pydev_ipython/inputhookwx.py b/python/helpers/pydev/pydev_ipython/inputhookwx.py index 6640884663ad..19ffdc72eff9 100644 --- a/python/helpers/pydev/pydev_ipython/inputhookwx.py +++ b/python/helpers/pydev/pydev_ipython/inputhookwx.py @@ -19,7 +19,7 @@ Authors: Robin Dunn, Brian Granger, Ondrej Certik import sys import signal -import time +from _pydev_imps import _pydev_time as time from timeit import default_timer as clock import wx diff --git a/python/helpers/pydev/pydev_ipython_console.py b/python/helpers/pydev/pydev_ipython_console.py index 7ba35211581a..68c26747c6e0 100644 --- a/python/helpers/pydev/pydev_ipython_console.py +++ b/python/helpers/pydev/pydev_ipython_console.py @@ -1,6 +1,5 @@ import sys from pydev_console_utils import BaseInterpreterInterface -import re import os @@ -10,7 +9,7 @@ os.environ['TERM'] = 'emacs' #to use proper page_more() for paging # Uncomment to force PyDev standard shell. # raise ImportError() -from pydev_ipython_console_011 import PyDevFrontEnd +from pydev_ipython_console_011 import get_pydev_frontend #======================================================================================================================= # InterpreterInterface @@ -24,7 +23,7 @@ class InterpreterInterface(BaseInterpreterInterface): BaseInterpreterInterface.__init__(self, mainThread) self.client_port = client_port self.host = host - self.interpreter = PyDevFrontEnd(host, client_port) + self.interpreter = get_pydev_frontend(host, client_port) self._input_error_printed = False self.notification_succeeded = False self.notification_tries = 0 diff --git a/python/helpers/pydev/pydev_ipython_console_011.py b/python/helpers/pydev/pydev_ipython_console_011.py index 54458e7c4af5..717aaccbc26e 100644 --- a/python/helpers/pydev/pydev_ipython_console_011.py +++ b/python/helpers/pydev/pydev_ipython_console_011.py @@ -19,7 +19,6 @@ import os import codeop from IPython.core.error import UsageError -from IPython.core.inputsplitter import IPythonInputSplitter from IPython.core.completer import IPCompleter from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC from IPython.core.usage import default_banner_parts @@ -53,7 +52,8 @@ def show_in_pager(self, strng): print(strng) def create_editor_hook(pydev_host, pydev_client_port): - def call_editor(self, filename, line=0, wait=True): + + def call_editor(filename, line=0, wait=True): """ Open an editor in PyDev """ if line is None: line = 0 @@ -62,6 +62,9 @@ def create_editor_hook(pydev_host, pydev_client_port): # we don't launch a process. This is more like what happens in the zmqshell filename = os.path.abspath(filename) + # import sys + # sys.__stderr__.write('Calling editor at: %s:%s\n' % (pydev_host, pydev_client_port)) + # Tell PyDev to open the editor server = xmlrpclib.Server('http://%s:%s' % (pydev_host, pydev_client_port)) server.IPythonEditor(filename, str(line)) @@ -291,22 +294,17 @@ class PyDevTerminalInteractiveShell(TerminalInteractiveShell): InteractiveShellABC.register(PyDevTerminalInteractiveShell) # @UndefinedVariable #======================================================================================================================= -# PyDevFrontEnd +# _PyDevFrontEnd #======================================================================================================================= -class PyDevFrontEnd: +class _PyDevFrontEnd: version = release.__version__ - def __init__(self, pydev_host, pydev_client_port, *args, **kwarg): + def __init__(self, *args, **kwarg): # Create and initialize our IPython instance. self.ipython = PyDevTerminalInteractiveShell.instance() - # Back channel to PyDev to open editors (in the future other - # info may go back this way. This is the same channel that is - # used to get stdin, see StdIn in pydev_console_utils) - self.ipython.set_hook('editor', create_editor_hook(pydev_host, pydev_client_port)) - # Display the IPython banner, this has version info and # help info self.ipython.show_banner() @@ -412,6 +410,9 @@ class PyDevFrontEnd: def getNamespace(self): return self.ipython.user_ns + + def clearBuffer(self): + del self._curr_exec_lines[:] def addExec(self, line): if self._curr_exec_lines: @@ -463,3 +464,28 @@ IPython.lib.inputhook.enable_gui = pydev_ipython.inputhook.enable_gui # rely on using the inputhooks directly. for name in pydev_ipython.inputhook.__all__: setattr(IPython.lib.inputhook, name, getattr(pydev_ipython.inputhook, name)) + + +class _PyDevFrontEndContainer: + _instance = None + _last_host_port = None + +def get_pydev_frontend(pydev_host, pydev_client_port): + if _PyDevFrontEndContainer._instance is None: + _PyDevFrontEndContainer._instance = _PyDevFrontEnd() + + if _PyDevFrontEndContainer._last_host_port != (pydev_host, pydev_client_port): + _PyDevFrontEndContainer._last_host_port = pydev_host, pydev_client_port + + # Back channel to PyDev to open editors (in the future other + # info may go back this way. This is the same channel that is + # used to get stdin, see StdIn in pydev_console_utils) + _PyDevFrontEndContainer._instance.ipython.hooks['editor'] = create_editor_hook(pydev_host, pydev_client_port) + + # Note: setting the callback directly because setting it with set_hook would actually create a chain instead + # of ovewriting at each new call). + # _PyDevFrontEndContainer._instance.ipython.set_hook('editor', create_editor_hook(pydev_host, pydev_client_port)) + + return _PyDevFrontEndContainer._instance + + \ No newline at end of file diff --git a/python/helpers/pydev/pydev_localhost.py b/python/helpers/pydev/pydev_localhost.py index 4e7a4d95fc12..eacabb5602ba 100644 --- a/python/helpers/pydev/pydev_localhost.py +++ b/python/helpers/pydev/pydev_localhost.py @@ -1,21 +1,18 @@ import pydevd_constants -if pydevd_constants.USE_LIB_COPY: - import _pydev_socket as socket -else: - import socket +from _pydev_imps import _pydev_socket as socket _cache = None def get_localhost(): ''' Should return 127.0.0.1 in ipv4 and ::1 in ipv6 - + localhost is not used because on windows vista/windows 7, there can be issues where the resolving doesn't work - properly and takes a lot of time (had this issue on the pyunit server). - + properly and takes a lot of time (had this issue on the pyunit server). + Using the IP directly solves the problem. ''' #TODO: Needs better investigation! - + global _cache if _cache is None: try: diff --git a/python/helpers/pydev/pydev_monkey.py b/python/helpers/pydev/pydev_monkey.py index 6e7d2e5b999c..d92378ed0ee1 100644 --- a/python/helpers/pydev/pydev_monkey.py +++ b/python/helpers/pydev/pydev_monkey.py @@ -5,6 +5,8 @@ import traceback pydev_src_dir = os.path.dirname(__file__) +from pydevd_constants import xrange + def is_python(path): if path.endswith("'") or path.endswith('"'): path = path[1:len(path)-1] @@ -102,7 +104,7 @@ def str_to_args_windows(args): buf = '' args_len = len(args) - for i in range(args_len): + for i in xrange(args_len): ch = args[i] if (ch == '\\'): backslashes+=1 @@ -385,3 +387,118 @@ def patch_new_process_functions_with_warning(): except ImportError: import _winapi as _subprocess monkey_patch_module(_subprocess, 'CreateProcess', create_CreateProcessWarnMultiproc) + + + +class _NewThreadStartupWithTrace: + + def __init__(self, original_func, args, kwargs): + self.original_func = original_func + self.args = args + self.kwargs = kwargs + + def __call__(self): + from pydevd_comm import GetGlobalDebugger + global_debugger = GetGlobalDebugger() + if global_debugger is not None: + global_debugger.SetTrace(global_debugger.trace_dispatch) + + return self.original_func(*self.args, **self.kwargs) + +class _NewThreadStartupWithoutTrace: + + def __init__(self, original_func, args, kwargs): + self.original_func = original_func + self.args = args + self.kwargs = kwargs + + def __call__(self): + return self.original_func(*self.args, **self.kwargs) + +_UseNewThreadStartup = _NewThreadStartupWithTrace + +def _get_threading_modules_to_patch(): + threading_modules_to_patch = [] + try: + import thread as _thread + threading_modules_to_patch.append(_thread) + except: + import _thread + threading_modules_to_patch.append(_thread) + return threading_modules_to_patch + +threading_modules_to_patch = _get_threading_modules_to_patch() + + + +def patch_thread_module(thread): + + if getattr(thread, '_original_start_new_thread', None) is None: + _original_start_new_thread = thread._original_start_new_thread = thread.start_new_thread + else: + _original_start_new_thread = thread._original_start_new_thread + + + class ClassWithPydevStartNewThread: + + def pydev_start_new_thread(self, function, args=(), kwargs={}): + ''' + We need to replace the original thread.start_new_thread with this function so that threads started + through it and not through the threading module are properly traced. + ''' + return _original_start_new_thread(_UseNewThreadStartup(function, args, kwargs), ()) + + # This is a hack for the situation where the thread.start_new_thread is declared inside a class, such as the one below + # class F(object): + # start_new_thread = thread.start_new_thread + # + # def start_it(self): + # self.start_new_thread(self.function, args, kwargs) + # So, if it's an already bound method, calling self.start_new_thread won't really receive a different 'self' -- it + # does work in the default case because in builtins self isn't passed either. + pydev_start_new_thread = ClassWithPydevStartNewThread().pydev_start_new_thread + + try: + # We need to replace the original thread.start_new_thread with this function so that threads started through + # it and not through the threading module are properly traced. + thread.start_new_thread = pydev_start_new_thread + thread.start_new = pydev_start_new_thread + except: + pass + +def patch_thread_modules(): + for t in threading_modules_to_patch: + patch_thread_module(t) + +def undo_patch_thread_modules(): + for t in threading_modules_to_patch: + try: + t.start_new_thread = t._original_start_new_thread + except: + pass + + try: + t.start_new = t._original_start_new_thread + except: + pass + +def disable_trace_thread_modules(): + ''' + Can be used to temporarily stop tracing threads created with thread.start_new_thread. + ''' + global _UseNewThreadStartup + _UseNewThreadStartup = _NewThreadStartupWithoutTrace + + +def enable_trace_thread_modules(): + ''' + Can be used to start tracing threads created with thread.start_new_thread again. + ''' + global _UseNewThreadStartup + _UseNewThreadStartup = _NewThreadStartupWithTrace + +def get_original_start_new_thread(threading_module): + try: + return threading_module._original_start_new_thread + except: + return threading_module.start_new_thread diff --git a/python/helpers/pydev/pydev_runfiles_parallel.py b/python/helpers/pydev/pydev_runfiles_parallel.py index e14f36d79139..91f55283d5c5 100644 --- a/python/helpers/pydev/pydev_runfiles_parallel.py +++ b/python/helpers/pydev/pydev_runfiles_parallel.py @@ -1,4 +1,5 @@ import unittest +from _pydev_imps import _pydev_thread try: import Queue except: @@ -282,13 +283,9 @@ class ClientThread(threading.Thread): if False: proc = subprocess.Popen(args, env=os.environ, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - stdout_thread = threading.Thread(target=self._reader_thread,args=(proc.stdout, sys.stdout)) - stdout_thread.setDaemon(True) - stdout_thread.start() + _pydev_thread.start_new_thread(self._reader_thread,(proc.stdout, sys.stdout)) - stderr_thread = threading.Thread(target=self._reader_thread,args=(proc.stderr, sys.stderr)) - stderr_thread.setDaemon(True) - stderr_thread.start() + _pydev_thread.start_new_thread(target=self._reader_thread,args=(proc.stderr, sys.stderr)) else: proc = subprocess.Popen(args, env=os.environ, shell=False) proc.wait() diff --git a/python/helpers/pydev/pydev_runfiles_parallel_client.py b/python/helpers/pydev/pydev_runfiles_parallel_client.py index 9a89cd54fd6e..7e5187ea8461 100644 --- a/python/helpers/pydev/pydev_runfiles_parallel_client.py +++ b/python/helpers/pydev/pydev_runfiles_parallel_client.py @@ -1,12 +1,7 @@ from pydevd_constants import * #@UnusedWildImport -try: - from Queue import Queue -except: - from queue import Queue #@UnresolvedImport -import threading -from pydev_imports import xmlrpclib +from pydev_imports import xmlrpclib, _queue +Queue = _queue.Queue import traceback -import time from pydev_runfiles_coverage import StartCoverageSupportFromParams @@ -15,7 +10,7 @@ from pydev_runfiles_coverage import StartCoverageSupportFromParams # ParallelNotification #======================================================================================================================= class ParallelNotification(object): - + def __init__(self, method, args, kwargs): self.method = method self.args = args @@ -23,8 +18,8 @@ class ParallelNotification(object): def ToTuple(self): return self.method, self.args, self.kwargs - - + + #======================================================================================================================= # KillServer #======================================================================================================================= @@ -37,9 +32,9 @@ class KillServer(object): # ServerComm #======================================================================================================================= class ServerComm(threading.Thread): - - + + def __init__(self, job_id, server): self.notifications_queue = Queue() threading.Thread.__init__(self) @@ -47,11 +42,11 @@ class ServerComm(threading.Thread): assert job_id is not None assert port is not None self.job_id = job_id - + self.finished = False self.server = server - - + + def run(self): while True: kill_found = False @@ -62,7 +57,7 @@ class ServerComm(threading.Thread): else: assert isinstance(command, ParallelNotification) commands.append(command.ToTuple()) - + try: while True: command = self.notifications_queue.get(block=False) #No block to create a batch. @@ -85,7 +80,7 @@ class ServerComm(threading.Thread): self.server.lock.release() except: traceback.print_exc() - + if kill_found: self.finished = True return @@ -96,27 +91,27 @@ class ServerComm(threading.Thread): # ServerFacade #======================================================================================================================= class ServerFacade(object): - - + + def __init__(self, notifications_queue): self.notifications_queue = notifications_queue - - + + def notifyTestsCollected(self, *args, **kwargs): pass #This notification won't be passed - - + + def notifyTestRunFinished(self, *args, **kwargs): pass #This notification won't be passed - - + + def notifyStartTest(self, *args, **kwargs): self.notifications_queue.put_nowait(ParallelNotification('notifyStartTest', args, kwargs)) - - + + def notifyTest(self, *args, **kwargs): self.notifications_queue.put_nowait(ParallelNotification('notifyTest', args, kwargs)) - + #======================================================================================================================= @@ -124,25 +119,25 @@ class ServerFacade(object): #======================================================================================================================= def run_client(job_id, port, verbosity, coverage_output_file, coverage_include): job_id = int(job_id) - + import pydev_localhost server = xmlrpclib.Server('http://%s:%s' % (pydev_localhost.get_localhost(), port)) server.lock = threading.Lock() - + server_comm = ServerComm(job_id, server) server_comm.start() - + try: server_facade = ServerFacade(server_comm.notifications_queue) import pydev_runfiles import pydev_runfiles_xml_rpc pydev_runfiles_xml_rpc.SetServer(server_facade) - + #Starts None and when the 1st test is gotten, it's started (because a server may be initiated and terminated #before receiving any test -- which would mean a different process got all the tests to run). coverage = None - + try: tests_to_run = [1] while tests_to_run: @@ -153,34 +148,34 @@ def run_client(job_id, port, verbosity, coverage_output_file, coverage_include): tests_to_run = server.GetTestsToRun(job_id) finally: server.lock.release() - + if not tests_to_run: break - + if coverage is None: _coverage_files, coverage = StartCoverageSupportFromParams( None, coverage_output_file, 1, coverage_include) - + files_to_tests = {} for test in tests_to_run: filename_and_test = test.split('|') if len(filename_and_test) == 2: files_to_tests.setdefault(filename_and_test[0], []).append(filename_and_test[1]) - + configuration = pydev_runfiles.Configuration( - '', - verbosity, - None, - None, - None, - files_to_tests, + '', + verbosity, + None, + None, + None, + files_to_tests, 1, #Always single job here - None, - + None, + #The coverage is handled in this loop. - coverage_output_file=None, - coverage_include=None, + coverage_output_file=None, + coverage_include=None, ) test_runner = pydev_runfiles.PydevTestRunner(configuration) sys.stdout.flush() @@ -190,11 +185,11 @@ def run_client(job_id, port, verbosity, coverage_output_file, coverage_include): coverage.stop() coverage.save() - + except: traceback.print_exc() server_comm.notifications_queue.put_nowait(KillServer()) - + #======================================================================================================================= @@ -204,16 +199,16 @@ if __name__ == '__main__': if len(sys.argv) -1 == 3: job_id, port, verbosity = sys.argv[1:] coverage_output_file, coverage_include = None, None - + elif len(sys.argv) -1 == 5: job_id, port, verbosity, coverage_output_file, coverage_include = sys.argv[1:] - + else: raise AssertionError('Could not find out how to handle the parameters: '+sys.argv[1:]) - + job_id = int(job_id) port = int(port) verbosity = int(verbosity) run_client(job_id, port, verbosity, coverage_output_file, coverage_include) - - + + diff --git a/python/helpers/pydev/pydev_runfiles_xml_rpc.py b/python/helpers/pydev/pydev_runfiles_xml_rpc.py index 35c69189c07c..062f778ac40e 100644 --- a/python/helpers/pydev/pydev_runfiles_xml_rpc.py +++ b/python/helpers/pydev/pydev_runfiles_xml_rpc.py @@ -3,15 +3,10 @@ import traceback import warnings from _pydev_filesystem_encoding import getfilesystemencoding -from pydev_imports import xmlrpclib +from pydev_imports import xmlrpclib, _queue +Queue = _queue.Queue from pydevd_constants import * - -try: - from Queue import Queue -except: - from queue import Queue - #This may happen in IronPython (in Python it shouldn't happen as there are #'fast' replacements that are used in xmlrpclib.py) warnings.filterwarnings( diff --git a/python/helpers/pydev/pydevconsole.py b/python/helpers/pydev/pydevconsole.py index e5bf49411416..a5ece54b972b 100644 --- a/python/helpers/pydev/pydevconsole.py +++ b/python/helpers/pydev/pydevconsole.py @@ -1,3 +1,5 @@ +from _pydev_imps._pydev_thread import start_new_thread + try: from code import InteractiveConsole except ImportError: @@ -9,13 +11,7 @@ from code import InteractiveInterpreter import os import sys -from pydevd_constants import USE_LIB_COPY -from pydevd_constants import IS_JYTHON - -if USE_LIB_COPY: - import _pydev_threading as threading -else: - import threading +import _pydev_threading as threading import traceback import fix_getpass @@ -23,15 +19,7 @@ fix_getpass.fixGetpass() import pydevd_vars -from pydev_imports import Exec - -try: - if USE_LIB_COPY: - import _pydev_Queue as _queue - else: - import Queue as _queue -except: - import queue as _queue +from pydev_imports import Exec, _queue try: import __builtin__ @@ -59,17 +47,6 @@ except: #That's OK, not all versions of python have sys.version_info pass -try: - try: - if USE_LIB_COPY: - import _pydev_xmlrpclib as xmlrpclib - else: - import xmlrpclib - except ImportError: - import xmlrpc.client as xmlrpclib -except ImportError: - import _pydev_xmlrpclib as xmlrpclib - class Command: def __init__(self, interpreter, code_fragment): @@ -81,13 +58,14 @@ class Command: self.code_fragment = code_fragment self.more = None - @staticmethod + def symbol_for_fragment(code_fragment): if code_fragment.is_single_line: symbol = 'single' else: symbol = 'exec' # Jython doesn't support this return symbol + symbol_for_fragment = staticmethod(symbol_for_fragment) def run(self): text = self.code_fragment.text @@ -248,7 +226,7 @@ except: #======================================================================================================================= # _DoExit #======================================================================================================================= -def DoExit(*args): +def _DoExit(*args): ''' We have to override the exit because calling sys.exit will only actually exit the main thread, and as we're in a Xml-rpc server, that won't work. @@ -312,7 +290,6 @@ def start_server(host, port, interpreter): sys.stderr.write(interpreter.get_greeting_msg()) sys.stderr.flush() - interpreter.server = server server.serve_forever() return server @@ -321,15 +298,11 @@ def start_server(host, port, interpreter): def StartServer(host, port, client_port): #replace exit (see comments on method) #note that this does not work in jython!!! (sys method can't be replaced). - sys.exit = DoExit + sys.exit = _DoExit interpreter = InterpreterInterface(host, client_port, threading.currentThread()) - server_thread = threading.Thread(target=start_server, - name='ServerThread', - args=(host, port, interpreter)) - server_thread.setDaemon(True) - server_thread.start() + start_new_thread(start_server,(host, port, interpreter)) process_exec_queue(interpreter) diff --git a/python/helpers/pydev/pydevd.py b/python/helpers/pydev/pydevd.py index 0209c8635b28..7d3541a2b332 100644 --- a/python/helpers/pydev/pydevd.py +++ b/python/helpers/pydev/pydevd.py @@ -2,10 +2,12 @@ from __future__ import nested_scopes # Jython 2.1 support from pydevd_constants import * # @UnusedWildImport +import pydev_monkey_qt +pydev_monkey_qt.patch_qt() + import traceback from django_debug import DjangoLineBreakpoint -from pydevd_signature import SignatureFactory from pydevd_frame import add_exception_to_frame import pydev_imports from pydevd_breakpoints import * #@UnusedWildImport @@ -69,7 +71,9 @@ from pydevd_comm import CMD_CHANGE_VARIABLE, \ CMD_ENABLE_DONT_TRACE, \ CMD_GET_FILE_CONTENTS,\ CMD_SET_PROPERTY_TRACE, CMD_RUN_CUSTOM_OPERATION,\ - InternalRunCustomOperation + InternalRunCustomOperation, CMD_EVALUATE_CONSOLE_EXPRESSION, InternalEvaluateConsoleExpression,\ + InternalConsoleGetCompletions + from pydevd_file_utils import NormFileToServer, GetFilenameAndBase import pydevd_file_utils import pydevd_vars @@ -77,19 +81,16 @@ import pydevd_vm_type import pydevd_tracing import pydevd_io from pydevd_additional_thread_info import PyDBAdditionalThreadInfo -from pydevd_custom_frames import CustomFramesContainer, CustomFramesContainerInit, addCustomFrame +from pydevd_custom_frames import CustomFramesContainer, CustomFramesContainerInit import pydevd_dont_trace import pydevd_traceproperty +from _pydev_imps import _pydev_time as time, _pydev_thread -if USE_LIB_COPY: - import _pydev_time as time - import _pydev_threading as threading -else: - import time - import threading +import _pydev_threading as threading import os +import atexit threadingEnumerate = threading.enumerate @@ -102,7 +103,6 @@ except: DONT_TRACE = { # commonly used things from the stdlib that we don't want to trace - 'threading.py':1, 'Queue.py':1, 'queue.py':1, 'socket.py':1, @@ -111,12 +111,21 @@ DONT_TRACE = { 'threading.py':1, #things from pydev that we don't want to trace + '_pydev_execfile.py':1, + '_pydev_jython_execfile.py':1, + '_pydev_threading':1, + '_pydev_Queue':1, + 'django_debug.py':1, + 'django_frame.py':1, + 'pydev_log.py':1, + 'pydev_monkey.py':1 , 'pydevd.py':1 , 'pydevd_additional_thread_info.py':1, - 'pydevd_custom_frames.py':1, 'pydevd_comm.py':1, 'pydevd_console.py':1 , 'pydevd_constants.py':1, + 'pydevd_custom_frames.py':1, + 'pydevd_dont_trace.py':1, 'pydevd_exec.py':1, 'pydevd_exec2.py':1, 'pydevd_file_utils.py':1, @@ -124,18 +133,18 @@ DONT_TRACE = { 'pydevd_import_class.py':1 , 'pydevd_io.py':1 , 'pydevd_psyco_stub.py':1, + 'pydevd_referrers.py':1 , 'pydevd_reload.py':1 , 'pydevd_resolver.py':1 , + 'pydevd_save_locals.py':1 , + 'pydevd_signature.py':1, 'pydevd_stackless.py':1 , 'pydevd_traceproperty.py':1, 'pydevd_tracing.py':1 , - 'pydevd_signature.py':1, 'pydevd_utils.py':1, 'pydevd_vars.py':1, 'pydevd_vm_type.py':1, - '_pydev_execfile.py':1, - '_pydev_jython_execfile.py':1, - 'pydevd_dont_trace.py':1, + 'pydevd_xml.py':1, } if IS_PY3K: @@ -155,17 +164,28 @@ remote = False from _pydev_filesystem_encoding import getfilesystemencoding file_system_encoding = getfilesystemencoding() -def isThreadAlive(t): - try: - # If thread is not started yet we treat it as alive. - # It is required to debug threads started by start_new_thread in Python 3.4 - if hasattr(t, '_is_stopped'): - alive = not t._is_stopped - else: - alive = not t.__stopped - except: - alive = t.isAlive() - return alive + +# Hack for https://sw-brainwy.rhcloud.com/tracker/PyDev/363 (i.e.: calling isAlive() can throw AssertionError under some circumstances) +# It is required to debug threads started by start_new_thread in Python 3.4 +_temp = threading.Thread() +if hasattr(_temp, '_is_stopped'): # Python 3.4 has this + def isThreadAlive(t): + try: + return not t._is_stopped + except: + return t.isAlive() + +elif hasattr(_temp, '_Thread__stopped'): # Python 2.7 has this + def isThreadAlive(t): + try: + return not t._Thread__stopped + except: + return t.isAlive() + +else: # Haven't checked all other versions, so, let's use the regular isAlive call in this case. + def isThreadAlive(t): + return t.isAlive() +del _temp #======================================================================================================================= # PyDBCommandThread @@ -179,7 +199,7 @@ class PyDBCommandThread(PyDBDaemonThread): self.setName('pydevd.CommandThread') def OnRun(self): - for i in range(1, 10): + for i in xrange(1, 10): time.sleep(0.5) #this one will only start later on (because otherwise we may not have any non-daemon threads if self.killReceived: return @@ -203,7 +223,7 @@ class PyDBCommandThread(PyDBDaemonThread): def killAllPydevThreads(): - threads = threadingEnumerate() + threads = DictKeys(PyDBDaemonThread.created_pydb_daemon_threads) for t in threads: if hasattr(t, 'doKillPydevThread'): t.doKillPydevThread() @@ -217,12 +237,23 @@ class PyDBCheckAliveThread(PyDBDaemonThread): def __init__(self, pyDb): PyDBDaemonThread.__init__(self) self.pyDb = pyDb - self.setDaemon(False) self.setName('pydevd.CheckAliveThread') def OnRun(self): if self.dontTraceMe: - self.pyDb.SetTrace(None) # no debugging on this thread + + disable_tracing = True + + if pydevd_vm_type.GetVmType() == pydevd_vm_type.PydevdVmType.JYTHON and sys.hexversion <= 0x020201f0: + # don't run untraced threads if we're in jython 2.2.1 or lower + # jython bug: if we start a thread and another thread changes the tracing facility + # it affects other threads (it's not set only for the thread but globally) + # Bug: http://sourceforge.net/tracker/index.php?func=detail&aid=1870039&group_id=12867&atid=112867 + disable_tracing = False + + if disable_tracing: + pydevd_tracing.SetTrace(None) # no debugging on this thread + while not self.killReceived: if not self.pyDb.haveAliveThreads(): try: @@ -240,63 +271,6 @@ class PyDBCheckAliveThread(PyDBDaemonThread): def doKillPydevThread(self): pass -if USE_LIB_COPY: - import _pydev_thread as thread -else: - try: - import thread - except ImportError: - import _thread as thread #Py3K changed it. - -_original_start_new_thread = thread.start_new_thread - -if getattr(thread, '_original_start_new_thread', None) is None: - thread._original_start_new_thread = thread.start_new_thread - -#======================================================================================================================= -# NewThreadStartup -#======================================================================================================================= -class NewThreadStartup: - - def __init__(self, original_func, args, kwargs): - self.original_func = original_func - self.args = args - self.kwargs = kwargs - - def __call__(self): - global_debugger = GetGlobalDebugger() - if global_debugger is not None: - global_debugger.SetTrace(global_debugger.trace_dispatch) - return self.original_func(*self.args, **self.kwargs) - -thread.NewThreadStartup = NewThreadStartup - -#======================================================================================================================= -# pydev_start_new_thread -#======================================================================================================================= -def _pydev_start_new_thread(function, args, kwargs={}): - ''' - We need to replace the original thread.start_new_thread with this function so that threads started through - it and not through the threading module are properly traced. - ''' - if USE_LIB_COPY: - import _pydev_thread as thread - else: - try: - import thread - except ImportError: - import _thread as thread #Py3K changed it. - - return thread._original_start_new_thread(thread.NewThreadStartup(function, args, kwargs), ()) - -class PydevStartNewThread(object): - def __get__(self, obj, type=None): - return self - - def __call__(self, function, args, kwargs={}): - return _pydev_start_new_thread(function, args, kwargs) - -pydev_start_new_thread = PydevStartNewThread() #======================================================================================================================= @@ -332,12 +306,15 @@ class PyDB: self.file_to_id_to_line_breakpoint = {} self.file_to_id_to_django_breakpoint = {} + # Note: breakpoints dict should not be mutated: a copy should be created + # and later it should be assigned back (to prevent concurrency issues). self.break_on_uncaught_exceptions = {} self.break_on_caught_exceptions = {} + self.django_exception_break = {} self.readyToRun = False - self._main_lock = threading.Lock() - self._lock_running_thread_ids = threading.Lock() + self._main_lock = _pydev_thread.allocate_lock() + self._lock_running_thread_ids = _pydev_thread.allocate_lock() self._py_db_command_thread_event = threading.Event() CustomFramesContainer._py_db_command_thread_event = self._py_db_command_thread_event self._finishDebuggingSession = False @@ -370,7 +347,11 @@ class PyDB: def haveAliveThreads(self): for t in threadingEnumerate(): - if not isinstance(t, PyDBDaemonThread) and isThreadAlive(t) and not t.isDaemon(): + if isinstance(t, PyDBDaemonThread): + pydev_log.error_once( + 'Error in debugger: Found PyDBDaemonThread through threading.enumerate().\n') + + if isThreadAlive(t) and not t.isDaemon(): return True return False @@ -425,11 +406,9 @@ class PyDB: if thread_id == "*": threads = threadingEnumerate() for t in threads: - thread_name = t.getName() - if not thread_name.startswith('pydevd.') or thread_name == 'pydevd.CommandThread': - thread_id = GetThreadId(t) - queue = self.getInternalQueue(thread_id) - queue.put(int_cmd) + thread_id = GetThreadId(t) + queue = self.getInternalQueue(thread_id) + queue.put(int_cmd) else: queue = self.getInternalQueue(thread_id) @@ -480,7 +459,13 @@ class PyDB: for t in all_threads: thread_id = GetThreadId(t) - if not isinstance(t, PyDBDaemonThread) and isThreadAlive(t): + if isinstance(t, PyDBDaemonThread): + pydev_log.error_once('Found PyDBDaemonThread in threading.enumerate.') + + elif getattr(t, 'is_pydev_daemon_thread', False): + pass # I.e.: skip the DummyThreads created from pydev daemon threads + + elif isThreadAlive(t): program_threads_alive[thread_id] = t if not DictContains(self._running_thread_ids, thread_id): @@ -543,19 +528,18 @@ class PyDB: threads = threadingEnumerate() try: for t in threads: - if not t.getName().startswith('pydevd.'): - # TODO: optimize so that we only actually add that tracing if it's in - # the new breakpoint context. - additionalInfo = None - try: - additionalInfo = t.additionalInfo - except AttributeError: - pass # that's ok, no info currently set + # TODO: optimize so that we only actually add that tracing if it's in + # the new breakpoint context. + additionalInfo = None + try: + additionalInfo = t.additionalInfo + except AttributeError: + pass # that's ok, no info currently set - if additionalInfo is not None: - for frame in additionalInfo.IterFrames(): - if frame is not ignore_frame: - self.SetTraceForFrameAndParents(frame, overwrite_prev_trace=overwrite_prev_trace) + if additionalInfo is not None: + for frame in additionalInfo.IterFrames(): + if frame is not ignore_frame: + self.SetTraceForFrameAndParents(frame, overwrite_prev_trace=overwrite_prev_trace) finally: frame = None t = None @@ -565,7 +549,7 @@ class PyDB: def consolidate_breakpoints(self, file, id_to_breakpoint, breakpoints): break_dict = {} - for breakpoint_id, pybreakpoint in id_to_breakpoint.items(): + for breakpoint_id, pybreakpoint in DictIterItems(id_to_breakpoint): break_dict[pybreakpoint.line] = pybreakpoint breakpoints[file] = break_dict @@ -586,14 +570,18 @@ class PyDB: ) if eb.notify_on_terminate: - self.break_on_uncaught_exceptions[exception] = eb + cp = self.break_on_uncaught_exceptions.copy() + cp[exception] = eb if DebugInfoHolder.DEBUG_TRACE_BREAKPOINTS > 0: - pydev_log.error("Exceptions to hook on terminate: %s\n" % (self.break_on_uncaught_exceptions,)) + pydev_log.error("Exceptions to hook on terminate: %s\n" % (cp,)) + self.break_on_uncaught_exceptions = cp if eb.notify_always: - self.break_on_caught_exceptions[exception] = eb + cp = self.break_on_caught_exceptions.copy() + cp[exception] = eb if DebugInfoHolder.DEBUG_TRACE_BREAKPOINTS > 0: - pydev_log.error("Exceptions to hook always: %s\n" % (self.break_on_caught_exceptions,)) + pydev_log.error("Exceptions to hook always: %s\n" % (cp,)) + self.break_on_caught_exceptions = cp return eb @@ -626,7 +614,7 @@ class PyDB: it may be worth refactoring it (actually, reordering the ifs so that the ones used mostly come before probably will give better performance). ''' - #print ID_TO_MEANING[str(cmd_id)], repr(text) + #print(ID_TO_MEANING[str(cmd_id)], repr(text)) self._main_lock.acquire() try: @@ -868,7 +856,7 @@ class PyDB: id_to_pybreakpoint[breakpoint_id] = breakpoint self.consolidate_breakpoints(file, id_to_pybreakpoint, breakpoints) - self.setTracingForUntracedContexts() + self.setTracingForUntracedContexts(overwrite_prev_trace=True) elif cmd_id == CMD_REMOVE_BREAK: #command to remove some breakpoint @@ -896,7 +884,7 @@ class PyDB: raise NameError(breakpoint_type) try: - id_to_pybreakpoint = file_to_id_to_breakpoint[file] + id_to_pybreakpoint = file_to_id_to_breakpoint.get(file, {}) if DebugInfoHolder.DEBUG_TRACE_BREAKPOINTS > 0: existing = id_to_pybreakpoint[breakpoint_id] sys.stderr.write('Removed breakpoint:%s - line:%s - func_name:%s (id: %s)\n' % ( @@ -934,8 +922,8 @@ class PyDB: # which allows setting caught/uncaught per exception. # splitted = text.split(';') - self.break_on_uncaught_exceptions.clear() - self.break_on_caught_exceptions.clear() + self.break_on_uncaught_exceptions = {} + self.break_on_caught_exceptions = {} added = [] if len(splitted) >= 4: if splitted[0] == 'true': @@ -1031,8 +1019,13 @@ class PyDB: elif cmd_id == CMD_REMOVE_EXCEPTION_BREAK: exception = text try: - DictPop(self.break_on_uncaught_exceptions, exception, None) - DictPop(self.break_on_caught_exceptions, exception, None) + cp = self.break_on_uncaught_exceptions.copy() + DictPop(cp, exception, None) + self.break_on_uncaught_exceptions = cp + + cp = self.break_on_caught_exceptions.copy() + DictPop(cp, exception, None) + self.break_on_caught_exceptions = cp except: pydev_log.debug("Error while removing exception %s"%sys.exc_info()[0]); update_exception_hook(self) @@ -1060,6 +1053,17 @@ class PyDB: except : pass + elif cmd_id == CMD_EVALUATE_CONSOLE_EXPRESSION: + # Command which takes care for the debug console communication + if text != "": + thread_id, frame_id, console_command = text.split('\t', 2) + console_command, line = console_command.split('\t') + if console_command == 'EVALUATE': + int_cmd = InternalEvaluateConsoleExpression(seq, thread_id, frame_id, line) + elif console_command == 'GET_COMPLETIONS': + int_cmd = InternalConsoleGetCompletions(seq, thread_id, frame_id, line) + self.postInternalCommand(int_cmd, thread_id) + elif cmd_id == CMD_RUN_CUSTOM_OPERATION: # Command which runs a custom operation if text != "": @@ -1216,7 +1220,7 @@ class PyDB: try: from_this_thread = [] - for frame_id, custom_frame in CustomFramesContainer.custom_frames.items(): + for frame_id, custom_frame in DictIterItems(CustomFramesContainer.custom_frames): if custom_frame.thread_id == thread.ident: # print >> sys.stderr, 'Frame created: ', frame_id self.writer.addCommand(self.cmdFactory.makeCustomFrameCreatedMessage(frame_id, custom_frame.name)) @@ -1337,7 +1341,7 @@ class PyDB: if self._finishDebuggingSession and not self._terminationEventSent: #that was not working very well because jython gave some socket errors try: - threads = threadingEnumerate() + threads = DictKeys(PyDBDaemonThread.created_pydb_daemon_threads) for t in threads: if hasattr(t, 'doKillPydevThread'): t.doKillPydevThread() @@ -1350,10 +1354,10 @@ class PyDB: is_file_to_ignore = DictContains(DONT_TRACE, base) #we don't want to debug threading or anything related to pydevd + #print('trace_dispatch', base, frame.f_lineno, event, frame.f_code.co_name, is_file_to_ignore) if is_file_to_ignore: return None - #print('trace_dispatch', base, frame.f_lineno, event, frame.f_code.co_name) try: #this shouldn't give an exception, but it could happen... (python bug) #see http://mail.python.org/pipermail/python-bugs-list/2007-June/038796.html @@ -1388,9 +1392,6 @@ class PyDB: self.processThreadNotAlive(GetThreadId(t)) return None # suspend tracing - if is_file_to_ignore: - return None - # each new frame... return additionalInfo.CreateDbFrame((self, filename, additionalInfo, t, frame)).trace_dispatch(frame, event, arg) @@ -1422,8 +1423,9 @@ class PyDB: - def SetTraceForFrameAndParents(self, frame, also_add_to_passed_frame=True, overwrite_prev_trace=False): - dispatch_func = self.trace_dispatch + def SetTraceForFrameAndParents(self, frame, also_add_to_passed_frame=True, overwrite_prev_trace=False, dispatch_func=None): + if dispatch_func is None: + dispatch_func = self.trace_dispatch if also_add_to_passed_frame: self.update_trace(frame, dispatch_func, overwrite_prev_trace) @@ -1460,13 +1462,14 @@ class PyDB: net = NetCommand(str(CMD_THREAD_CREATE), 0, '') self.writer.addCommand(net) - pydevd_tracing.SetTrace(self.trace_dispatch) self.patch_threads() + pydevd_tracing.SetTrace(self.trace_dispatch) PyDBCommandThread(self).start() PyDBCheckAliveThread(self).start() + def patch_threads(self): try: # not available in jython! @@ -1474,11 +1477,8 @@ class PyDB: except: pass - try: - thread.start_new_thread = pydev_start_new_thread - thread.start_new = pydev_start_new_thread - except: - pass + from pydev_monkey import patch_thread_modules + patch_thread_modules() def run(self, file, globals=None, locals=None, set_trace=True): @@ -1549,64 +1549,6 @@ class PyDB: cmd = self.cmdFactory.makeExitMessage() self.writer.addCommand(cmd) - def wait_for_commands(self, globals): - thread = threading.currentThread() - frame = MyFrame() - frame.f_globals = globals - frame.f_locals = globals - frame.f_lineno = -1 - frame.f_code = FCode(setup['file'], setup['file']) - thread_id = GetThreadId(thread) - # frame_id = addCustomFrame(frame, 'todo', GetThreadId(thread)) - cmd = self.cmdFactory.makeThreadSuspendMessage(thread_id, frame, CMD_CONSOLE_EXEC, '') - self.writer.addCommand(cmd) - while True: - self.processInternalCommands() - time.sleep(0.01) - -class MyFrame: - def __init__( - self, - frame, - template_frame_file=None, - template_frame_line=None): - - if template_frame_file is None: - template_frame_file = get_template_file_name(frame) - - self.back_context = frame.f_locals['context'] - self.f_code = FCode('Django Template', template_frame_file) - - if template_frame_line is None: - template_frame_line = get_template_line(frame, template_frame_file) - self.f_lineno = template_frame_line - - self.f_back = frame - self.f_globals = {} - self.f_locals = self.collect_context() - self.f_trace = None - - def collect_context(self): - res = {} - try: - for d in self.back_context.dicts: - res.update(d) - except AttributeError: - pass - return res - - def changeVariable(self, name, value): - for d in self.back_context.dicts: - if DictContains(d, name): - d[name] = value - self.f_locals[name] = value - - -class FCode: - def __init__(self, name, filename): - self.co_name = name - self.co_filename = filename - def set_debug(setup): setup['DEBUG_RECORD_SOCKET_READS'] = True setup['DEBUG_TRACE_BREAKPOINTS'] = 1 @@ -1624,47 +1566,47 @@ def processCommandLine(argv): setup['multiproc'] = False #Used by PyCharm (reuses connection: ssh tunneling) setup['multiprocess'] = False # Used by PyDev (creates new connection to ide) setup['save-signatures'] = False - setup['cmd-line'] = False + setup['print-in-debugger-startup'] = False i = 0 del argv[0] while (i < len(argv)): - if (argv[i] == '--port'): + if argv[i] == '--port': del argv[i] setup['port'] = int(argv[i]) del argv[i] - elif (argv[i] == '--vm_type'): + elif argv[i] == '--vm_type': del argv[i] setup['vm_type'] = argv[i] del argv[i] - elif (argv[i] == '--client'): + elif argv[i] == '--client': del argv[i] setup['client'] = argv[i] del argv[i] - elif (argv[i] == '--server'): + elif argv[i] == '--server': del argv[i] setup['server'] = True - elif (argv[i] == '--file'): + elif argv[i] == '--file': del argv[i] setup['file'] = argv[i] i = len(argv) # pop out, file is our last argument - elif (argv[i] == '--DEBUG_RECORD_SOCKET_READS'): + elif argv[i] == '--DEBUG_RECORD_SOCKET_READS': del argv[i] setup['DEBUG_RECORD_SOCKET_READS'] = True - elif (argv[i] == '--DEBUG'): + elif argv[i] == '--DEBUG': del argv[i] set_debug(setup) - elif (argv[i] == '--multiproc'): + elif argv[i] == '--multiproc': del argv[i] setup['multiproc'] = True - elif (argv[i] == '--multiprocess'): + elif argv[i] == '--multiprocess': del argv[i] setup['multiprocess'] = True - elif (argv[i] == '--save-signatures'): + elif argv[i] == '--save-signatures': del argv[i] setup['save-signatures'] = True - elif (argv[i] == '--cmd-line'): + elif argv[i] == '--print-in-debugger-startup': del argv[i] - setup['cmd-line'] = True + setup['print-in-debugger-startup'] = True else: raise ValueError("unexpected option " + argv[i]) return setup @@ -1675,18 +1617,6 @@ def usage(doExit=0): if doExit: sys.exit(0) -def SetTraceForParents(frame, dispatch_func): - frame = frame.f_back - while frame: - if frame.f_trace is None: - frame.f_trace = dispatch_func - - frame = frame.f_back - del frame - -def exit_hook(): - debugger = GetGlobalDebugger() - debugger.exiting() def initStdoutRedirect(): if not getattr(sys, 'stdoutBuf', None): @@ -1751,7 +1681,7 @@ def settrace( -_set_trace_lock = threading.Lock() +_set_trace_lock = _pydev_thread.allocate_lock() def _locked_settrace( host, @@ -1806,7 +1736,7 @@ def _locked_settrace( CustomFramesContainer.custom_frames_lock.acquire() try: - for _frameId, custom_frame in CustomFramesContainer.custom_frames.items(): + for _frameId, custom_frame in DictIterItems(CustomFramesContainer.custom_frames): debugger.SetTraceForFrameAndParents(custom_frame.frame, False) finally: CustomFramesContainer.custom_frames_lock.release() @@ -1833,7 +1763,9 @@ def _locked_settrace( # As this is the first connection, also set tracing for any untraced threads debugger.setTracingForUntracedContexts(ignore_frame=GetFrame(), overwrite_prev_trace=overwrite_prev_trace) - sys.exitfunc = exit_hook + # Stop the tracing as the last thing before the actual shutdown for a clean exit. + atexit.register(stoptrace) + #Suspend as the last thing after all tracing is in place. if suspend: debugger.setSuspend(t, CMD_SET_BREAK) @@ -1876,21 +1808,17 @@ def stoptrace(): except: pass - try: - thread.start_new_thread = _original_start_new_thread - thread.start_new = _original_start_new_thread - except: - pass - + from pydev_monkey import undo_patch_thread_modules + undo_patch_thread_modules() + debugger = GetGlobalDebugger() - + if debugger: - debugger.trace_dispatch = None - - debugger.SetTraceForFrameAndParents(GetFrame(), False) - + + debugger.SetTraceForFrameAndParents( + GetFrame(), also_add_to_passed_frame=True, overwrite_prev_trace=True, dispatch_func=lambda *args:None) debugger.exiting() - + killAllPydevThreads() connected = False @@ -1981,6 +1909,7 @@ class SetupHolder: # main #======================================================================================================================= if __name__ == '__main__': + # parse the command line. --file is our last argument that is required try: sys.original_argv = sys.argv[:] @@ -1990,6 +1919,12 @@ if __name__ == '__main__': traceback.print_exc() usage(1) + if setup['print-in-debugger-startup']: + try: + pid = ' (pid: %s)' % os.getpid() + except: + pid = '' + sys.stderr.write("pydev debugger: starting%s\n" % pid) fix_getpass.fixGetpass() @@ -2098,11 +2033,6 @@ if __name__ == '__main__': except: pass # It's ok not having stackless there... - debugger = PyDB() - - if setup['cmd-line']: - debugger.cmd_line = True - if fix_app_engine_debug: sys.stderr.write("pydev debugger: google app engine integration enabled\n") curr_dir = os.path.dirname(__file__) @@ -2115,8 +2045,10 @@ if __name__ == '__main__': sys.argv.insert(3, '--automatic_restart=no') sys.argv.insert(4, '--max_module_instances=1') + debugger = PyDB() # Run the dev_appserver debugger.run(setup['file'], None, None, set_trace=False) + else: # as to get here all our imports are already resolved, the psyco module can be # changed and we'll still get the speedups in the debugger, as those functions @@ -2132,11 +2064,14 @@ if __name__ == '__main__': import pydevd_psyco_stub sys.modules['psyco'] = pydevd_psyco_stub + debugger = PyDB() if setup['save-signatures']: if pydevd_vm_type.GetVmType() == pydevd_vm_type.PydevdVmType.JYTHON: sys.stderr.write("Collecting run-time type information is not supported for Jython\n") else: + # Only import it if we're going to use it! + from pydevd_signature import SignatureFactory debugger.signature_factory = SignatureFactory() try: @@ -2149,8 +2084,4 @@ if __name__ == '__main__': connected = True # Mark that we're connected when started from inside ide. debugger.run(setup['file'], None, None) - - # if setup['cmd-line']: - # debugger.wait_for_commands(globals) - - + diff --git a/python/helpers/pydev/pydevd_additional_thread_info.py b/python/helpers/pydev/pydevd_additional_thread_info.py index fa906adf385e..76fb49ed1986 100644 --- a/python/helpers/pydev/pydevd_additional_thread_info.py +++ b/python/helpers/pydev/pydevd_additional_thread_info.py @@ -1,9 +1,6 @@ import sys from pydevd_constants import * #@UnusedWildImport -if USE_LIB_COPY: - import _pydev_threading as threading -else: - import threading +from _pydev_imps import _pydev_thread from pydevd_frame import PyDBFrame import weakref @@ -62,7 +59,7 @@ class PyDBAdditionalThreadInfoWithoutCurrentFramesSupport(AbstractPyDBAdditional #Or if the user compiled threadframe (from http://www.majid.info/mylos/stories/2004/06/10/threadframe.html) #NOT RLock!! (could deadlock if it was) - self.lock = threading.Lock() + self.lock = _pydev_thread.allocate_lock() self._acquire_lock = self.lock.acquire self._release_lock = self.lock.release diff --git a/python/helpers/pydev/pydevd_breakpoints.py b/python/helpers/pydev/pydevd_breakpoints.py index 8c65436bac21..1171157257e9 100644 --- a/python/helpers/pydev/pydevd_breakpoints.py +++ b/python/helpers/pydev/pydevd_breakpoints.py @@ -8,10 +8,7 @@ _original_excepthook = None _handle_exceptions = None -if USE_LIB_COPY: - import _pydev_threading as threading -else: - import threading +import _pydev_threading as threading threadingCurrentThread = threading.currentThread @@ -145,7 +142,7 @@ def _restore_pm_excepthook(): def update_exception_hook(dbg): if dbg.break_on_uncaught_exceptions: - _set_pm_excepthook(dbg.break_on_uncaught_exceptions.copy()) + _set_pm_excepthook(dbg.break_on_uncaught_exceptions) else: _restore_pm_excepthook() diff --git a/python/helpers/pydev/pydevd_comm.py b/python/helpers/pydev/pydevd_comm.py index 8d37d0d35ba5..9a3d9eb01c82 100644 --- a/python/helpers/pydev/pydevd_comm.py +++ b/python/helpers/pydev/pydevd_comm.py @@ -61,32 +61,11 @@ from pydevd_constants import * #@UnusedWildImport import sys -if USE_LIB_COPY: - import _pydev_time as time - import _pydev_threading as threading - try: - import _pydev_thread as thread - except ImportError: - import _thread as thread #Py3K changed it. - import _pydev_Queue as _queue - from _pydev_socket import socket - from _pydev_socket import AF_INET, SOCK_STREAM - from _pydev_socket import SHUT_RD, SHUT_WR -else: - import time - import threading - try: - import thread - except ImportError: - import _thread as thread #Py3K changed it. - - try: - import Queue as _queue - except ImportError: - import queue as _queue - from socket import socket - from socket import AF_INET, SOCK_STREAM - from socket import SHUT_RD, SHUT_WR +from _pydev_imps import _pydev_time as time, _pydev_thread +from _pydev_imps import _pydev_thread as thread +import _pydev_threading as threading +from _pydev_imps._pydev_socket import socket, AF_INET, SOCK_STREAM, SHUT_RD, SHUT_WR +from pydev_imports import _queue try: from urllib import quote, quote_plus, unquote, unquote_plus @@ -103,6 +82,8 @@ import pydev_log import _pydev_completer from pydevd_tracing import GetExceptionTracebackStr +import pydevd_console +from pydev_monkey import disable_trace_thread_modules, enable_trace_thread_modules @@ -262,22 +243,42 @@ def SetGlobalDebugger(dbg): #======================================================================================================================= # PyDBDaemonThread #======================================================================================================================= -class PyDBDaemonThread(threading.Thread): +class PyDBDaemonThread: + + created_pydb_daemon_threads = {} def __init__(self): - threading.Thread.__init__(self) - self.setDaemon(True) + # Note: subclasses are always daemon threads. self.killReceived = False self.dontTraceMe = True - def run(self): - if sys.platform.startswith("java"): - import org.python.core as PyCore #@UnresolvedImport - ss = PyCore.PySystemState() - # Note: Py.setSystemState() affects only the current thread. - PyCore.Py.setSystemState(ss) + def setName(self, name): + self.name = name - self.OnRun() + def start(self): + import pydev_monkey + start_new_thread = pydev_monkey.get_original_start_new_thread(_pydev_thread) + start_new_thread(self.run, ()) + + def run(self): + created_pydb_daemon = self.created_pydb_daemon_threads + created_pydb_daemon[self] = 1 + dummy_thread = threading.currentThread() + dummy_thread.is_pydev_daemon_thread = True + try: + try: + if IS_JYTHON: + import org.python.core as PyCore #@UnresolvedImport + ss = PyCore.PySystemState() + # Note: Py.setSystemState() affects only the current thread. + PyCore.Py.setSystemState(ss) + + self.OnRun() + except: + if sys is not None and traceback is not None: + traceback.print_exc() + finally: + del created_pydb_daemon[self] def OnRun(self): raise NotImplementedError('Should be reimplemented by: %s' % self.__class__) @@ -288,7 +289,18 @@ class PyDBDaemonThread(threading.Thread): def stopTrace(self): if self.dontTraceMe: - pydevd_tracing.SetTrace(None) # no debugging on this thread + + disable_tracing = True + + if pydevd_vm_type.GetVmType() == pydevd_vm_type.PydevdVmType.JYTHON and sys.hexversion <= 0x020201f0: + # don't run untraced threads if we're in jython 2.2.1 or lower + # jython bug: if we start a thread and another thread changes the tracing facility + # it affects other threads (it's not set only for the thread but globally) + # Bug: http://sourceforge.net/tracker/index.php?func=detail&aid=1870039&group_id=12867&atid=112867 + disable_tracing = False + + if disable_tracing: + pydevd_tracing.SetTrace(None) # no debugging on this thread #======================================================================================================================= @@ -371,7 +383,6 @@ class WriterThread(PyDBDaemonThread): """ writer thread writes out the commands in an infinite loop """ def __init__(self, sock): PyDBDaemonThread.__init__(self) - self.setDaemon(False) #writer isn't daemon to be able to deliver all messages after main thread terminated self.sock = sock self.setName("pydevd.Writer") self.cmdQueue = _queue.Queue() @@ -389,11 +400,16 @@ class WriterThread(PyDBDaemonThread): """ just loop and write responses """ self.stopTrace() + get_has_timeout = sys.hexversion >= 0x02030000 # 2.3 onwards have it. try: while True: try: try: - cmd = self.cmdQueue.get(1, 0.1) + if get_has_timeout: + cmd = self.cmdQueue.get(1, 0.1) + else: + time.sleep(.01) + cmd = self.cmdQueue.get(0) except _queue.Empty: if self.killReceived: try: @@ -790,7 +806,7 @@ class ReloadCodeCommand(InternalThreadCommand): self.thread_id = thread_id self.module_name = module_name self.executed = False - self.lock = threading.Lock() + self.lock = _pydev_thread.allocate_lock() def canBeExecutedBy(self, thread_id): @@ -1008,7 +1024,7 @@ class InternalEvaluateExpression(InternalThreadCommand): try: result = pydevd_vars.evaluateExpression(self.thread_id, self.frame_id, self.expression, self.doExec) xml = "" - xml += pydevd_vars.varToXML(result, "", self.doTrim) + xml += pydevd_vars.varToXML(result, self.expression, self.doTrim) xml += "" cmd = dbg.cmdFactory.makeEvaluateExpressionMessage(self.sequence, xml) dbg.writer.addCommand(cmd) @@ -1168,7 +1184,12 @@ class InternalEvaluateConsoleExpression(InternalThreadCommand): console_message = pydevd_console.execute_console_command(frame, self.thread_id, self.frame_id, self.line) cmd = dbg.cmdFactory.makeSendConsoleMessage(self.sequence, console_message.toXML()) else: - console_message.add_console_message(pydevd_console.CONSOLE_ERROR, "Select the valid frame in the debug view") + from pydevd_console import ConsoleMessage + console_message = ConsoleMessage() + console_message.add_console_message( + pydevd_console.CONSOLE_ERROR, + "Select the valid frame in the debug view (thread: %s, frame: %s invalid)" % (self.thread_id, self.frame_id), + ) cmd = dbg.cmdFactory.makeErrorMessage(self.sequence, console_message.toXML()) except: exc = GetExceptionTracebackStr() @@ -1245,13 +1266,10 @@ class InternalConsoleExec(InternalThreadCommand): def doIt(self, dbg): """ Converts request into python variable """ - pydev_start_new_thread = None try: try: - pydev_start_new_thread = thread.start_new_thread - - thread.start_new_thread = thread._original_start_new_thread #don't trace new threads created by console command - thread.start_new = thread._original_start_new_thread + #don't trace new threads created by console command + disable_trace_thread_modules() result = pydevconsole.consoleExec(self.thread_id, self.frame_id, self.expression) xml = "" @@ -1265,8 +1283,8 @@ class InternalConsoleExec(InternalThreadCommand): cmd = dbg.cmdFactory.makeErrorMessage(self.sequence, "Error evaluating console expression " + exc) dbg.writer.addCommand(cmd) finally: - thread.start_new_thread = pydev_start_new_thread - thread.start_new = pydev_start_new_thread + enable_trace_thread_modules() + sys.stderr.flush() sys.stdout.flush() diff --git a/python/helpers/pydev/pydevd_console.py b/python/helpers/pydev/pydevd_console.py index 0751d7755f4d..52b18bb29211 100644 --- a/python/helpers/pydev/pydevd_console.py +++ b/python/helpers/pydev/pydevd_console.py @@ -27,9 +27,9 @@ class ConsoleMessage: self.more = False # List of tuple [('error', 'error_message'), ('message_list', 'output_message')] self.console_messages = [] - + def add_console_message(self, message_type, message): - """add messages in the console_messages list + """add messages in the console_messages list """ for m in message.split("\n"): if m.strip(): @@ -50,14 +50,14 @@ class ConsoleMessage: """ makeValid = makeValidXmlValue - + xml = '%s' % (self.more) for message_type, message in self.console_messages: xml += '<%s message="%s">' % (message_type, makeValid(message), message_type) xml += '' - + return xml @@ -65,7 +65,7 @@ class ConsoleMessage: # DebugConsoleStdIn #======================================================================================================================= class DebugConsoleStdIn(BaseStdIn): - + overrides(BaseStdIn.readline) def readline(self, *args, **kwargs): sys.stderr.write('Warning: Reading from stdin is still not supported in this console.\n') @@ -75,10 +75,10 @@ class DebugConsoleStdIn(BaseStdIn): # DebugConsole #======================================================================================================================= class DebugConsole(InteractiveConsole, BaseInterpreterInterface): - """Wrapper around code.InteractiveConsole, in order to send + """Wrapper around code.InteractiveConsole, in order to send errors and outputs to the debug console """ - + overrides(BaseInterpreterInterface.createStdIn) def createStdIn(self): return DebugConsoleStdIn() #For now, raw_input is not supported in this console. @@ -86,12 +86,12 @@ class DebugConsole(InteractiveConsole, BaseInterpreterInterface): overrides(InteractiveConsole.push) def push(self, line, frame): - """Change built-in stdout and stderr methods by the + """Change built-in stdout and stderr methods by the new custom StdMessage. execute the InteractiveConsole.push. Change the stdout and stderr back be the original built-ins - - Return boolean (True if more input is required else False), + + Return boolean (True if more input is required else False), output_messages and input_messages """ more = False @@ -102,7 +102,7 @@ class DebugConsole(InteractiveConsole, BaseInterpreterInterface): self.frame = frame out = sys.stdout = IOBuf() err = sys.stderr = IOBuf() - more, _need_input = self.addExec(line) + more = self.addExec(line) except Exception: exc = GetExceptionTracebackStr() err.buflist.append("Internal Error: %s" % (exc,)) @@ -111,15 +111,15 @@ class DebugConsole(InteractiveConsole, BaseInterpreterInterface): self.frame = None frame = None sys.stdout = original_stdout - sys.stderr = original_stderr + sys.stderr = original_stderr return more, out.buflist, err.buflist - - + + overrides(BaseInterpreterInterface.doAddExec) def doAddExec(self, line): return InteractiveConsole.push(self, line) - + overrides(InteractiveConsole.runcode) def runcode(self, code): @@ -147,24 +147,24 @@ class DebugConsole(InteractiveConsole, BaseInterpreterInterface): # InteractiveConsoleCache #======================================================================================================================= class InteractiveConsoleCache: - + thread_id = None frame_id = None interactive_console_instance = None - + #Note: On Jython 2.1 we can't use classmethod or staticmethod, so, just make the functions below free-functions. def get_interactive_console(thread_id, frame_id, frame, console_message): """returns the global interactive console. - interactive console should have been initialized by this time + interactive console should have been initialized by this time """ if InteractiveConsoleCache.thread_id == thread_id and InteractiveConsoleCache.frame_id == frame_id: return InteractiveConsoleCache.interactive_console_instance - + InteractiveConsoleCache.interactive_console_instance = DebugConsole() - InteractiveConsoleCache.thread_id = thread_id + InteractiveConsoleCache.thread_id = thread_id InteractiveConsoleCache.frame_id = frame_id - + console_stacktrace = traceback.extract_stack(frame, limit=1) if console_stacktrace: current_context = console_stacktrace[0] # top entry from stacktrace @@ -177,23 +177,23 @@ def clear_interactive_console(): InteractiveConsoleCache.thread_id = None InteractiveConsoleCache.frame_id = None InteractiveConsoleCache.interactive_console_instance = None - + def execute_console_command(frame, thread_id, frame_id, line): - """fetch an interactive console instance from the cache and + """fetch an interactive console instance from the cache and push the received command to the console. - - create and return an instance of console_message + + create and return an instance of console_message """ console_message = ConsoleMessage() - + interpreter = get_interactive_console(thread_id, frame_id, frame, console_message) more, output_messages, error_messages = interpreter.push(line, frame) console_message.update_more(more) - + for message in output_messages: console_message.add_console_message(CONSOLE_OUTPUT, message) - + for message in error_messages: console_message.add_console_message(CONSOLE_ERROR, message) diff --git a/python/helpers/pydev/pydevd_constants.py b/python/helpers/pydev/pydevd_constants.py index d6500e14cd44..e878d3b48ead 100644 --- a/python/helpers/pydev/pydevd_constants.py +++ b/python/helpers/pydev/pydevd_constants.py @@ -1,7 +1,6 @@ ''' This module holds the constants used for specifying the states of the debugger. ''' - STATE_RUN = 1 STATE_SUSPEND = 2 @@ -73,13 +72,10 @@ except AttributeError: SUPPORT_GEVENT = os.getenv('GEVENT_SUPPORT', 'False') == 'True' USE_LIB_COPY = SUPPORT_GEVENT and not IS_PY3K and sys.version_info[1] >= 6 +import _pydev_threading as threading -if USE_LIB_COPY: - import _pydev_threading as threading -else: - import threading - -_nextThreadIdLock = threading.Lock() +from _pydev_imps import _pydev_thread +_nextThreadIdLock = _pydev_thread.allocate_lock() #======================================================================================================================= # Jython? @@ -119,20 +115,40 @@ if IS_PY3K: return list(d.values()) DictIterValues = dict.values + + def DictIterItems(d): + return d.items() + + def DictItems(d): + return list(d.items()) + else: DictKeys = dict.keys try: DictIterValues = dict.itervalues except: DictIterValues = dict.values #Older versions don't have the itervalues + DictValues = dict.values + def DictIterItems(d): + return d.iteritems() + + def DictItems(d): + return d.items() + try: - xrange + xrange = xrange except: #Python 3k does not have it xrange = range + +try: + import itertools + izip = itertools.izip +except: + izip = zip try: object diff --git a/python/helpers/pydev/pydevd_custom_frames.py b/python/helpers/pydev/pydevd_custom_frames.py index e259356b8336..8709e6a17dd6 100644 --- a/python/helpers/pydev/pydevd_custom_frames.py +++ b/python/helpers/pydev/pydevd_custom_frames.py @@ -1,5 +1,6 @@ from pydevd_constants import * #@UnusedWildImport from pydevd_file_utils import GetFilenameAndBase +from _pydev_imps import _pydev_thread threadingCurrentThread = threading.currentThread DEBUG = False @@ -13,7 +14,7 @@ class CustomFramesContainer: def CustomFramesContainerInit(): #Note: no staticmethod on jython 2.1 (so, use free-function) - CustomFramesContainer.custom_frames_lock = threading.Lock() + CustomFramesContainer.custom_frames_lock = _pydev_thread.allocate_lock() # custom_frames can only be accessed if properly locked with custom_frames_lock! # Key is a string identifying the frame (as well as the thread it belongs to). diff --git a/python/helpers/pydev/pydevd_frame.py b/python/helpers/pydev/pydevd_frame.py index f1c035002bb5..5d1e78458391 100644 --- a/python/helpers/pydev/pydevd_frame.py +++ b/python/helpers/pydev/pydevd_frame.py @@ -15,7 +15,11 @@ from pydevd_comm import CMD_ADD_DJANGO_EXCEPTION_BREAK, \ CMD_STEP_INTO, CMD_SMART_STEP_INTO, CMD_RUN_TO_LINE, CMD_SET_NEXT_STATEMENT from pydevd_constants import * # @UnusedWildImport from pydevd_file_utils import GetFilenameAndBase -from pydevd_signature import sendSignatureCallTrace +try: + from pydevd_signature import sendSignatureCallTrace +except ImportError: + def sendSignatureCallTrace(*args, **kwargs): + pass import pydevd_vars import pydevd_dont_trace @@ -81,7 +85,7 @@ class PyDBFrame: if trace is not None: #on jython trace is None on the first event exception_breakpoint = get_exception_breakpoint( - exception, mainDebugger.break_on_caught_exceptions.copy()) + exception, mainDebugger.break_on_caught_exceptions) if exception_breakpoint is not None: if not exception_breakpoint.notify_on_first_raise_only or just_raised(trace): @@ -203,6 +207,7 @@ class PyDBFrame: try: frame_id_to_frame = {} + frame_id_to_frame[id(frame)] = frame f = trace_obj.tb_frame while f is not None: frame_id_to_frame[id(f)] = f @@ -244,7 +249,7 @@ class PyDBFrame: if getattr(thread, 'pydev_do_not_trace', None): return None - if event == 'call': + if event == 'call' and main_debugger.signature_factory: sendSignatureCallTrace(main_debugger, frame, filename) is_exception_event = event == 'exception' @@ -403,12 +408,6 @@ class PyDBFrame: if stop: self.setSuspend(thread, CMD_SET_BREAK) - if event == 'return': - if main_debugger.cmd_line: - base = basename(back.f_code.co_filename) - if base == 'pydevd.py' and back.f_code.co_name == 'run': - self.setSuspend(thread, CMD_SET_BREAK) # we suspend on exit - # if thread has a suspend flag, we suspend with a busy wait if info.pydev_state == STATE_SUSPEND: self.doWaitSuspend(thread, frame, event, arg) diff --git a/python/helpers/pydev/pydevd_resolver.py b/python/helpers/pydev/pydevd_resolver.py index 614549f74f37..ad49bd881ba0 100644 --- a/python/helpers/pydev/pydevd_resolver.py +++ b/python/helpers/pydev/pydevd_resolver.py @@ -13,9 +13,13 @@ except: setattr(__builtin__, 'False', 0) import pydevd_constants +from pydevd_constants import DictIterItems, xrange -MAX_ITEMS_TO_HANDLE = 500 +# Note: 300 is already a lot to see in the outline (after that the user should really use the shell to get things) +# and this also means we'll pass less information to the client side (which makes debugging faster). +MAX_ITEMS_TO_HANDLE = 300 + TOO_LARGE_MSG = 'Too large to show contents. Max items to show: ' + str(MAX_ITEMS_TO_HANDLE) TOO_LARGE_ATTR = 'Unable to handle:' @@ -58,7 +62,7 @@ except: class AbstractResolver: ''' This class exists only for documentation purposes to explain how to create a resolver. - + Some examples on how to resolve things: - list: getDictionary could return a dict with index->item and use the index to resolve it later - set: getDictionary could return a dict with id(object)->object and reiterate in that array to resolve it later @@ -69,7 +73,7 @@ class AbstractResolver: ''' In this method, we'll resolve some child item given the string representation of the item in the key representing the previously asked dictionary. - + @param var: this is the actual variable to be resolved. @param attribute: this is the string representation of a key previously returned in getDictionary. ''' @@ -78,7 +82,7 @@ class AbstractResolver: def getDictionary(self, var): ''' @param var: this is the variable that should have its children gotten. - + @return: a dictionary where each pair key, value should be shown to the user as children items in the variables view for the given var. ''' @@ -128,12 +132,12 @@ class DefaultResolver: declaredMethods = obj.getDeclaredMethods() declaredFields = obj.getDeclaredFields() - for i in range(len(declaredMethods)): + for i in xrange(len(declaredMethods)): name = declaredMethods[i].getName() ret[name] = declaredMethods[i].toString() found.put(name, 1) - for i in range(len(declaredFields)): + for i in xrange(len(declaredFields)): name = declaredFields[i].getName() found.put(name, 1) #if declaredFields[i].isAccessible(): @@ -145,7 +149,7 @@ class DefaultResolver: ret[name] = declaredFields[i].toString() #this simple dir does not always get all the info, that's why we have the part before - #(e.g.: if we do a dir on String, some methods that are from other interfaces such as + #(e.g.: if we do a dir on String, some methods that are from other interfaces such as #charAt don't appear) try: d = dir(original) @@ -169,8 +173,8 @@ class DefaultResolver: names = var.__members__ d = {} - #Be aware that the order in which the filters are applied attempts to - #optimize the operation by removing as many items as possible in the + #Be aware that the order in which the filters are applied attempts to + #optimize the operation by removing as many items as possible in the #first filters, leaving fewer items for later filters if filterBuiltIn or filterFunction: @@ -212,18 +216,18 @@ class DefaultResolver: class DictResolver: def resolve(self, dict, key): - if key == '__len__': + if key in ('__len__', TOO_LARGE_ATTR): return None if '(' not in key: #we have to treat that because the dict resolver is also used to directly resolve the global and local - #scopes (which already have the items directly) + #scopes (which already have the items directly) return dict[key] #ok, we have to iterate over the items to find the one that matches the id, because that's the only way #to actually find the reference from the string we have before. expected_id = int(key.split('(')[-1][:-1]) - for key, val in dict.items(): + for key, val in DictIterItems(dict): if id(key) == expected_id: return val @@ -241,10 +245,15 @@ class DictResolver: def getDictionary(self, dict): ret = {} - for key, val in dict.items(): + i = 0 + for key, val in DictIterItems(dict): + i += 1 #we need to add the id because otherwise we cannot find the real object to get its contents later on. key = '%s (%s)' % (self.keyStr(key), id(key)) ret[key] = val + if i > MAX_ITEMS_TO_HANDLE: + ret[TOO_LARGE_ATTR] = TOO_LARGE_MSG + break ret['__len__'] = len(dict) return ret @@ -261,24 +270,25 @@ class TupleResolver: #to enumerate tuples and lists @param var: that's the original attribute @param attribute: that's the key passed in the dict (as a string) ''' - if attribute == '__len__' or attribute == TOO_LARGE_ATTR: + if attribute in ('__len__', TOO_LARGE_ATTR): return None return var[int(attribute)] def getDictionary(self, var): - #return dict( [ (i, x) for i, x in enumerate(var) ] ) - # modified 'cause jython does not have enumerate support l = len(var) d = {} - - if l < MAX_ITEMS_TO_HANDLE: - format = '%0' + str(int(len(str(l)))) + 'd' + + format_str = '%0' + str(int(len(str(l)))) + 'd' + + i = 0 + for item in var: + d[format_str % i] = item + i += 1 - - for i, item in zip(range(l), var): - d[ format % i ] = item - else: - d[TOO_LARGE_ATTR] = TOO_LARGE_MSG + if i > MAX_ITEMS_TO_HANDLE: + d[TOO_LARGE_ATTR] = TOO_LARGE_MSG + break + d['__len__'] = len(var) return d @@ -293,7 +303,7 @@ class SetResolver: ''' def resolve(self, var, attribute): - if attribute == '__len__': + if attribute in ('__len__', TOO_LARGE_ATTR): return None attribute = int(attribute) @@ -305,8 +315,16 @@ class SetResolver: def getDictionary(self, var): d = {} + i = 0 for item in var: - d[ id(item) ] = item + i+= 1 + d[id(item)] = item + + if i > MAX_ITEMS_TO_HANDLE: + d[TOO_LARGE_ATTR] = TOO_LARGE_MSG + break + + d['__len__'] = len(var) return d @@ -325,7 +343,7 @@ class InstanceResolver: ret = {} declaredFields = obj.__class__.getDeclaredFields() - for i in range(len(declaredFields)): + for i in xrange(len(declaredFields)): name = declaredFields[i].getName() try: declaredFields[i].setAccessible(True) @@ -352,7 +370,7 @@ class JyArrayResolver: def getDictionary(self, obj): ret = {} - for i in range(len(obj)): + for i in xrange(len(obj)): ret[ i ] = obj[i] ret['__len__'] = len(obj) @@ -367,13 +385,24 @@ class NdArrayResolver: This resolves a numpy ndarray returning some metadata about the NDArray ''' + def is_numeric(self, obj): + if not hasattr(obj, 'dtype'): + return False + return obj.dtype.kind in 'biufc' + def resolve(self, obj, attribute): if attribute == '__internals__': return defaultResolver.getDictionary(obj) if attribute == 'min': - return obj.min() + if self.is_numeric(obj): + return obj.min() + else: + return None if attribute == 'max': - return obj.max() + if self.is_numeric(obj): + return obj.max() + else: + return None if attribute == 'shape': return obj.shape if attribute == 'dtype': @@ -389,8 +418,12 @@ class NdArrayResolver: ret['min'] = 'ndarray too big, calculating min would slow down debugging' ret['max'] = 'ndarray too big, calculating max would slow down debugging' else: - ret['min'] = obj.min() - ret['max'] = obj.max() + if self.is_numeric(obj): + ret['min'] = obj.min() + ret['max'] = obj.max() + else: + ret['min'] = 'not a numeric object' + ret['max'] = 'not a numeric object' ret['shape'] = obj.shape ret['dtype'] = obj.dtype ret['size'] = obj.size diff --git a/python/helpers/pydev/pydevd_signature.py b/python/helpers/pydev/pydevd_signature.py index e11bb5dd446b..d7b37c84cb76 100644 --- a/python/helpers/pydev/pydevd_signature.py +++ b/python/helpers/pydev/pydevd_signature.py @@ -1,11 +1,17 @@ import inspect -import trace import os -trace._warn = lambda *args: None # workaround for http://bugs.python.org/issue17143 (PY-8706) +try: + import trace +except ImportError: + pass +else: + trace._warn = lambda *args: None # workaround for http://bugs.python.org/issue17143 (PY-8706) + import gc from pydevd_comm import CMD_SIGNATURE_CALL_TRACE, NetCommand import pydevd_vars +from pydevd_constants import xrange class Signature(object): def __init__(self, file, name): @@ -43,7 +49,7 @@ class SignatureFactory(object): locals = frame.f_locals filename, modulename, funcname = self.file_module_function_of(frame) res = Signature(filename, funcname) - for i in range(0, code.co_argcount): + for i in xrange(0, code.co_argcount): name = code.co_varnames[i] tp = type(locals[name]) class_name = tp.__name__ @@ -123,9 +129,8 @@ def create_signature_message(signature): return NetCommand(CMD_SIGNATURE_CALL_TRACE, 0, cmdText) def sendSignatureCallTrace(dbg, frame, filename): - if dbg.signature_factory: - if dbg.signature_factory.is_in_scope(filename): - dbg.writer.addCommand(create_signature_message(dbg.signature_factory.create_signature(frame))) + if dbg.signature_factory.is_in_scope(filename): + dbg.writer.addCommand(create_signature_message(dbg.signature_factory.create_signature(frame))) diff --git a/python/helpers/pydev/pydevd_stackless.py b/python/helpers/pydev/pydevd_stackless.py index 21ba86a00e39..c2fd508e05d4 100644 --- a/python/helpers/pydev/pydevd_stackless.py +++ b/python/helpers/pydev/pydevd_stackless.py @@ -7,6 +7,7 @@ from pydevd_comm import GetGlobalDebugger import weakref from pydevd_file_utils import GetFilenameAndBase from pydevd import DONT_TRACE +from pydevd_constants import DictItems # Used so that we don't loose the id (because we'll remove when it's not alive and would generate a new id for the @@ -195,7 +196,7 @@ def _schedule_callback(prev, next): register_tasklet_info(prev) try: - for tasklet_ref, tasklet_info in list(_weak_tasklet_registered_to_info.items()): # Make sure it's a copy! + for tasklet_ref, tasklet_info in DictItems(_weak_tasklet_registered_to_info): # Make sure it's a copy! tasklet = tasklet_ref() if tasklet is None or not tasklet.alive: # Garbage-collected already! @@ -269,7 +270,7 @@ if not hasattr(stackless.tasklet, "trace_function"): register_tasklet_info(prev) try: - for tasklet_ref, tasklet_info in list(_weak_tasklet_registered_to_info.items()): # Make sure it's a copy! + for tasklet_ref, tasklet_info in DictItems(_weak_tasklet_registered_to_info): # Make sure it's a copy! tasklet = tasklet_ref() if tasklet is None or not tasklet.alive: # Garbage-collected already! diff --git a/python/helpers/pydev/pydevd_tracing.py b/python/helpers/pydev/pydevd_tracing.py index 7bc1ba5c2c23..d362462013a9 100644 --- a/python/helpers/pydev/pydevd_tracing.py +++ b/python/helpers/pydev/pydevd_tracing.py @@ -1,4 +1,5 @@ from pydevd_constants import * #@UnusedWildImport +from _pydev_imps import _pydev_thread try: import cStringIO as StringIO #may not always be available @UnusedImport @@ -8,10 +9,6 @@ except: except: import io as StringIO -if USE_LIB_COPY: - import _pydev_threading as threading -else: - import threading import sys #@Reimport import traceback @@ -21,7 +18,7 @@ class TracingFunctionHolder: ''' _original_tracing = None _warn = True - _lock = threading.Lock() + _lock = _pydev_thread.allocate_lock() _traceback_limit = 1 _warnings_shown = {} diff --git a/python/helpers/pydev/pydevd_vars.py b/python/helpers/pydev/pydevd_vars.py index 79bcb5fba697..3baea5b61b99 100644 --- a/python/helpers/pydev/pydevd_vars.py +++ b/python/helpers/pydev/pydevd_vars.py @@ -8,6 +8,7 @@ from types import * #@UnusedWildImport from pydevd_custom_frames import getCustomFrame from pydevd_xml import * +from _pydev_imps import _pydev_thread try: from StringIO import StringIO @@ -15,11 +16,7 @@ except ImportError: from io import StringIO import sys #@Reimport -if USE_LIB_COPY: - import _pydev_threading as threading -else: - import threading -import pydevd_resolver +import _pydev_threading as threading import traceback import pydevd_save_locals from pydev_imports import Exec, quote, execfile @@ -71,7 +68,7 @@ def dumpFrames(thread_id): # AdditionalFramesContainer #=============================================================================== class AdditionalFramesContainer: - lock = threading.Lock() + lock = _pydev_thread.allocate_lock() additional_frames = {} #dict of dicts @@ -204,7 +201,7 @@ def getVariable(thread_id, frame_id, scope, attrs): attrList = [] if scope == 'EXPRESSION': - for count in range(len(attrList)): + for count in xrange(len(attrList)): if count == 0: # An Expression can be in any scope (globals/locals), therefore it needs to evaluated as an expression var = evaluateExpression(thread_id, frame_id, attrList[count], False) diff --git a/python/helpers/pydev/runfiles.py b/python/helpers/pydev/runfiles.py index 67c88be4fe7c..c2db611055c4 100644 --- a/python/helpers/pydev/runfiles.py +++ b/python/helpers/pydev/runfiles.py @@ -75,7 +75,7 @@ def main(): if test_framework == 0: - pydev_runfiles.main(configuration) + return pydev_runfiles.main(configuration) #Note: still doesn't return a proper value. else: #We'll convert the parameters to what nose or py.test expects. @@ -144,7 +144,8 @@ def main(): import pydev_runfiles_nose PYDEV_NOSE_PLUGIN_SINGLETON = pydev_runfiles_nose.StartPydevNosePluginSingleton(configuration) argv.append('--with-pydevplugin') - nose.run(argv=argv, addplugins=[PYDEV_NOSE_PLUGIN_SINGLETON]) + # Return 'not' because it will return 'success' (so, exit == 0 if success) + return not nose.run(argv=argv, addplugins=[PYDEV_NOSE_PLUGIN_SINGLETON]) elif test_framework == PY_TEST_FRAMEWORK: if DEBUG: @@ -189,7 +190,7 @@ def main(): argv.append('-p') argv.append('pydev_runfiles_pytest2') - pytest.main(argv) + return pytest.main(argv) else: raise AssertionError('Cannot handle test framework: %s at this point.' % (test_framework,)) diff --git a/python/helpers/pydev/tests/test_check_pydevconsole.py b/python/helpers/pydev/tests/test_check_pydevconsole.py index 957e4314a9ab..5d09968cfe19 100644 --- a/python/helpers/pydev/tests/test_check_pydevconsole.py +++ b/python/helpers/pydev/tests/test_check_pydevconsole.py @@ -3,6 +3,7 @@ import unittest import pydevconsole from pydev_imports import xmlrpclib, SimpleXMLRPCServer +import sys from pydev_localhost import get_localhost from pydev_ipython_console_011 import get_pydev_frontend diff --git a/python/helpers/pydev/tests/test_get_referrers.py b/python/helpers/pydev/tests/test_get_referrers.py index 87f24cd12c75..8284b2779c40 100644 --- a/python/helpers/pydev/tests/test_get_referrers.py +++ b/python/helpers/pydev/tests/test_get_referrers.py @@ -1,22 +1,7 @@ -import os.path import sys -import weakref import threading import time -IS_JYTHON = sys.platform.find('java') != -1 - -try: - this_file_name = __file__ -except NameError: - # stupid jython. plain old __file__ isnt working for some reason - import test_runfiles #@UnresolvedImport - importing the module itself - this_file_name = test_runfiles.__file__ - - -desired_runfiles_path = os.path.normpath(os.path.dirname(this_file_name) + "/..") -sys.path.insert(0, desired_runfiles_path) - import unittest import pydevd_referrers from pydev_imports import StringIO diff --git a/python/helpers/pydev/tests/test_jyserver.py b/python/helpers/pydev/tests/test_jyserver.py index 8765400ae39d..12be8bcf5b1b 100644 --- a/python/helpers/pydev/tests/test_jyserver.py +++ b/python/helpers/pydev/tests/test_jyserver.py @@ -36,10 +36,15 @@ class Test(unittest.TestCase): unittest.TestCase.tearDown(self) def testIt(self): + if not IS_JYTHON: + return dbg('ok') def testMessage(self): + if not IS_JYTHON: + return t = jycompletionserver.T(0) + t.exit_process_on_kill = False l = [] l.append(('Def', 'description' , 'args')) @@ -65,6 +70,8 @@ class Test(unittest.TestCase): def testCompletionSocketsAndMessages(self): + if not IS_JYTHON: + return dbg('testCompletionSocketsAndMessages') t, socket = self.createConnections() self.socket = socket @@ -121,6 +128,7 @@ class Test(unittest.TestCase): Creates the connections needed for testing. ''' t = jycompletionserver.T(p1) + t.exit_process_on_kill = False t.start() diff --git a/python/helpers/pydev/tests/test_jysimpleTipper.py b/python/helpers/pydev/tests/test_jysimpleTipper.py index 10fc8f53a388..bf421b2347c2 100644 --- a/python/helpers/pydev/tests/test_jysimpleTipper.py +++ b/python/helpers/pydev/tests/test_jysimpleTipper.py @@ -4,17 +4,14 @@ import unittest import os import sys -#make it as if we were executing from the directory above this one (so that we can use pycompletionserver -#without the need for it being in the pythonpath) -sys.argv[0] = os.path.dirname(sys.argv[0]) -#twice the dirname to get the previous level from this file. -sys.path.insert(1, os.path.join(os.path.dirname(sys.argv[0]))) #this does not work (they must be in the system pythonpath) #sys.path.insert(1, r"D:\bin\eclipse321\plugins\org.junit_3.8.1\junit.jar" ) #some late loading jar tests #sys.path.insert(1, r"D:\bin\eclipse331_1\plugins\org.apache.ant_1.7.0.v200706080842\lib\ant.jar" ) #some late loading jar tests +IS_JYTHON = 0 if sys.platform.find('java') != -1: + IS_JYTHON = 1 from _pydev_jy_imports_tipper import ismethod from _pydev_jy_imports_tipper import isclass from _pydev_jy_imports_tipper import dirObj @@ -236,22 +233,24 @@ class TestCompl(unittest.TestCase): assert isMet[1][0].basicAsStr() == "function:met2 args=['arg1', 'arg2'], varargs=vararg, kwargs=kwarg, docs:docmet2" assert not isclass(met2) + +if not IS_JYTHON: + # Disable tests if not running under Jython + class TestMod(unittest.TestCase): + pass + class TestCompl(TestMod): + pass + class TestSearch(TestMod): + pass if __name__ == '__main__': - if sys.platform.find('java') != -1: - #Only run if jython - suite = unittest.makeSuite(TestCompl) - suite2 = unittest.makeSuite(TestMod) - suite3 = unittest.makeSuite(TestSearch) + #Only run if jython + suite = unittest.makeSuite(TestCompl) + suite2 = unittest.makeSuite(TestMod) + suite3 = unittest.makeSuite(TestSearch) + + unittest.TextTestRunner(verbosity=1).run(suite) + unittest.TextTestRunner(verbosity=1).run(suite2) + unittest.TextTestRunner(verbosity=1).run(suite3) - unittest.TextTestRunner(verbosity=1).run(suite) - unittest.TextTestRunner(verbosity=1).run(suite2) - unittest.TextTestRunner(verbosity=1).run(suite3) - -# suite.addTest(Test('testCase12')) -# suite = unittest.TestSuite() -# unittest.TextTestRunner(verbosity=1).run(suite) - - else: - sys.stdout.write('Not running jython tests for non-java platform: %s' % sys.platform) diff --git a/python/helpers/pydev/tests/test_pydev_ipython_011.py b/python/helpers/pydev/tests/test_pydev_ipython_011.py index 3cfa70fd0112..dc4684f35f3d 100644 --- a/python/helpers/pydev/tests/test_pydev_ipython_011.py +++ b/python/helpers/pydev/tests/test_pydev_ipython_011.py @@ -7,81 +7,87 @@ from pydev_imports import StringIO, SimpleXMLRPCServer from pydev_localhost import get_localhost from pydev_console_utils import StdIn import socket +from pydev_ipython_console_011 import get_pydev_frontend +import time -# make it as if we were executing from the directory above this one -sys.argv[0] = os.path.dirname(sys.argv[0]) -# twice the dirname to get the previous level from this file. -sys.path.insert(1, os.path.join(os.path.dirname(sys.argv[0]))) +try: + xrange +except: + xrange = range -# PyDevFrontEnd depends on singleton in IPython, so you -# can't make multiple versions. So we reuse front_end for -# all the tests - -orig_stdout = sys.stdout -orig_stderr = sys.stderr - -stdout = sys.stdout = StringIO() -stderr = sys.stderr = StringIO() - -from pydev_ipython_console_011 import PyDevFrontEnd -s = socket.socket() -s.bind(('', 0)) -client_port = s.getsockname()[1] -s.close() -front_end = PyDevFrontEnd(get_localhost(), client_port) - - -def addExec(code, expected_more=False): - more = front_end.addExec(code) - eq_(expected_more, more) class TestBase(unittest.TestCase): + + def setUp(self): - front_end.input_splitter.reset() - stdout.truncate(0) - stdout.seek(0) - stderr.truncate(0) - stderr.seek(0) + # PyDevFrontEnd depends on singleton in IPython, so you + # can't make multiple versions. So we reuse self.front_end for + # all the tests + self.front_end = get_pydev_frontend(get_localhost(), 0) + + from pydev_ipython.inputhook import set_return_control_callback + set_return_control_callback(lambda:True) + self.front_end.clearBuffer() + def tearDown(self): pass + + def addExec(self, code, expected_more=False): + more = self.front_end.addExec(code) + eq_(expected_more, more) + + def redirectStdout(self): + from IPython.utils import io + + self.original_stdout = sys.stdout + sys.stdout = io.stdout = StringIO() + + def restoreStdout(self): + from IPython.utils import io + io.stdout = sys.stdout = self.original_stdout class TestPyDevFrontEnd(TestBase): + def testAddExec_1(self): - addExec('if True:', True) + self.addExec('if True:', True) + def testAddExec_2(self): - addExec('if True:\n testAddExec_a = 10\n', True) + #Change: 'more' must now be controlled in the client side after the initial 'True' returned. + self.addExec('if True:\n testAddExec_a = 10\n', False) + assert 'testAddExec_a' in self.front_end.getNamespace() + def testAddExec_3(self): - assert 'testAddExec_a' not in front_end.getNamespace() - addExec('if True:\n testAddExec_a = 10\n\n') - assert 'testAddExec_a' in front_end.getNamespace() - eq_(front_end.getNamespace()['testAddExec_a'], 10) + assert 'testAddExec_x' not in self.front_end.getNamespace() + self.addExec('if True:\n testAddExec_x = 10\n\n') + assert 'testAddExec_x' in self.front_end.getNamespace() + eq_(self.front_end.getNamespace()['testAddExec_x'], 10) def testGetNamespace(self): - assert 'testGetNamespace_a' not in front_end.getNamespace() - addExec('testGetNamespace_a = 10') - assert 'testGetNamespace_a' in front_end.getNamespace() - eq_(front_end.getNamespace()['testGetNamespace_a'], 10) + assert 'testGetNamespace_a' not in self.front_end.getNamespace() + self.addExec('testGetNamespace_a = 10') + assert 'testGetNamespace_a' in self.front_end.getNamespace() + eq_(self.front_end.getNamespace()['testGetNamespace_a'], 10) def testComplete(self): - unused_text, matches = front_end.complete('%') + unused_text, matches = self.front_end.complete('%') assert len(matches) > 1, 'at least one magic should appear in completions' def testCompleteDoesNotDoPythonMatches(self): # Test that IPython's completions do not do the things that # PyDev's completions will handle - addExec('testComplete_a = 5') - addExec('testComplete_b = 10') - addExec('testComplete_c = 15') - unused_text, matches = front_end.complete('testComplete_') + self.addExec('testComplete_a = 5') + self.addExec('testComplete_b = 10') + self.addExec('testComplete_c = 15') + unused_text, matches = self.front_end.complete('testComplete_') assert len(matches) == 0 def testGetCompletions_1(self): # Test the merged completions include the standard completions - addExec('testComplete_a = 5') - addExec('testComplete_b = 10') - addExec('testComplete_c = 15') - res = front_end.getCompletions('testComplete_', 'testComplete_') + self.addExec('testComplete_a = 5') + self.addExec('testComplete_b = 10') + self.addExec('testComplete_c = 15') + res = self.front_end.getCompletions('testComplete_', 'testComplete_') matches = [f[0] for f in res] assert len(matches) == 3 eq_(set(['testComplete_a', 'testComplete_b', 'testComplete_c']), set(matches)) @@ -90,60 +96,80 @@ class TestPyDevFrontEnd(TestBase): # Test that we get IPython completions in results # we do this by checking kw completion which PyDev does # not do by default - addExec('def ccc(ABC=123): pass') - res = front_end.getCompletions('ccc(', '') + self.addExec('def ccc(ABC=123): pass') + res = self.front_end.getCompletions('ccc(', '') matches = [f[0] for f in res] assert 'ABC=' in matches def testGetCompletions_3(self): # Test that magics return IPYTHON magic as type - res = front_end.getCompletions('%cd', '%cd') + res = self.front_end.getCompletions('%cd', '%cd') assert len(res) == 1 eq_(res[0][3], '12') # '12' == IToken.TYPE_IPYTHON_MAGIC assert len(res[0][1]) > 100, 'docstring for %cd should be a reasonably long string' class TestRunningCode(TestBase): def testPrint(self): - addExec('print("output")') - eq_(stdout.getvalue(), 'output\n') + self.redirectStdout() + try: + self.addExec('print("output")') + eq_(sys.stdout.getvalue(), 'output\n') + finally: + self.restoreStdout() def testQuestionMark_1(self): - addExec('?') - assert len(stdout.getvalue()) > 1000, 'IPython help should be pretty big' + self.redirectStdout() + try: + self.addExec('?') + assert len(sys.stdout.getvalue()) > 1000, 'IPython help should be pretty big' + finally: + self.restoreStdout() def testQuestionMark_2(self): - addExec('int?') - assert stdout.getvalue().find('Convert') != -1 + self.redirectStdout() + try: + self.addExec('int?') + assert sys.stdout.getvalue().find('Convert') != -1 + finally: + self.restoreStdout() def testGui(self): - from pydev_ipython.inputhook import get_inputhook, set_stdin_file - set_stdin_file(sys.stdin) - assert get_inputhook() is None - addExec('%gui tk') - # we can't test the GUI works here because we aren't connected to XML-RPC so - # nowhere for hook to run - assert get_inputhook() is not None - addExec('%gui none') - assert get_inputhook() is None + try: + import Tkinter + except: + return + else: + from pydev_ipython.inputhook import get_inputhook + assert get_inputhook() is None + self.addExec('%gui tk') + # we can't test the GUI works here because we aren't connected to XML-RPC so + # nowhere for hook to run + assert get_inputhook() is not None + self.addExec('%gui none') + assert get_inputhook() is None def testHistory(self): ''' Make sure commands are added to IPython's history ''' - addExec('a=1') - addExec('b=2') - _ih = front_end.getNamespace()['_ih'] - eq_(_ih[-1], 'b=2') - eq_(_ih[-2], 'a=1') - - addExec('history') - hist = stdout.getvalue().split('\n') - eq_(hist[-1], '') - eq_(hist[-2], 'history') - eq_(hist[-3], 'b=2') - eq_(hist[-4], 'a=1') + self.redirectStdout() + try: + self.addExec('a=1') + self.addExec('b=2') + _ih = self.front_end.getNamespace()['_ih'] + eq_(_ih[-1], 'b=2') + eq_(_ih[-2], 'a=1') + + self.addExec('history') + hist = sys.stdout.getvalue().split('\n') + eq_(hist[-1], '') + eq_(hist[-2], 'history') + eq_(hist[-3], 'b=2') + eq_(hist[-4], 'a=1') + finally: + self.restoreStdout() def testEdit(self): - ''' Make sure we can issue an edit command ''' + ''' Make sure we can issue an edit command''' called_RequestInput = [False] called_IPythonEditor = [False] def startClientThread(client_port): @@ -163,26 +189,47 @@ class TestRunningCode(TestBase): handle_request_input = HandleRequestInput() import pydev_localhost - client_server = SimpleXMLRPCServer((pydev_localhost.get_localhost(), self.client_port), logRequests=False) + self.client_server = client_server = SimpleXMLRPCServer( + (pydev_localhost.get_localhost(), self.client_port), logRequests=False) client_server.register_function(handle_request_input.RequestInput) client_server.register_function(handle_request_input.IPythonEditor) client_server.serve_forever() + + def shutdown(self): + return + self.client_server.shutdown() client_thread = ClientThread(client_port) client_thread.setDaemon(True) client_thread.start() return client_thread - startClientThread(client_port) + # PyDevFrontEnd depends on singleton in IPython, so you + # can't make multiple versions. So we reuse self.front_end for + # all the tests + s = socket.socket() + s.bind(('', 0)) + self.client_port = client_port = s.getsockname()[1] + s.close() + self.front_end = get_pydev_frontend(get_localhost(), client_port) + + client_thread = startClientThread(self.client_port) orig_stdin = sys.stdin - sys.stdin = StdIn(self, get_localhost(), client_port) + sys.stdin = StdIn(self, get_localhost(), self.client_port) try: filename = 'made_up_file.py' - addExec('%edit ' + filename) - eq_(called_IPythonEditor[0], (os.path.abspath(filename), 0)) + self.addExec('%edit ' + filename) + + for i in xrange(10): + if called_IPythonEditor[0] == (os.path.abspath(filename), '0'): + break + time.sleep(.1) + + eq_(called_IPythonEditor[0], (os.path.abspath(filename), '0')) assert called_RequestInput[0], "Make sure the 'wait' parameter has been respected" finally: sys.stdin = orig_stdin + client_thread.shutdown() if __name__ == '__main__': diff --git a/python/helpers/pydev/tests/test_pydevconsole.py b/python/helpers/pydev/tests/test_pydevconsole.py index 9a9e3edaf124..18421980ae82 100644 --- a/python/helpers/pydev/tests/test_pydevconsole.py +++ b/python/helpers/pydev/tests/test_pydevconsole.py @@ -1,10 +1,6 @@ import threading import unittest import sys -import os - -sys.argv[0] = os.path.dirname(sys.argv[0]) -sys.path.insert(1, os.path.join(os.path.dirname(sys.argv[0]))) import pydevconsole from pydev_imports import xmlrpclib, SimpleXMLRPCServer, StringIO @@ -19,104 +15,112 @@ except NameError: #======================================================================================================================= class Test(unittest.TestCase): - def setUp(self): + def testConsoleHello(self): self.original_stdout = sys.stdout sys.stdout = StringIO() - - - def tearDown(self): - ret = sys.stdout #@UnusedVariable - sys.stdout = self.original_stdout - #print_ ret.getvalue() -- use to see test output - - def testConsoleHello(self): - client_port, _server_port = self.getFreeAddresses() - client_thread = self.startClientThread(client_port) #@UnusedVariable - import time - time.sleep(.3) #let's give it some time to start the threads - - import pydev_localhost - interpreter = pydevconsole.InterpreterInterface(pydev_localhost.get_localhost(), client_port, server=None) - - (result,) = interpreter.hello("Hello pydevconsole") - self.assertEqual(result, "Hello eclipse") + + try: + client_port, _server_port = self.getFreeAddresses() + client_thread = self.startClientThread(client_port) #@UnusedVariable + import time + time.sleep(.3) #let's give it some time to start the threads + + import pydev_localhost + interpreter = pydevconsole.InterpreterInterface(pydev_localhost.get_localhost(), client_port, threading.currentThread()) + + (result,) = interpreter.hello("Hello pydevconsole") + self.assertEqual(result, "Hello eclipse") + finally: + sys.stdout = self.original_stdout def testConsoleRequests(self): - client_port, _server_port = self.getFreeAddresses() - client_thread = self.startClientThread(client_port) #@UnusedVariable - import time - time.sleep(.3) #let's give it some time to start the threads - - import pydev_localhost - interpreter = pydevconsole.InterpreterInterface(pydev_localhost.get_localhost(), client_port, server=None) - interpreter.addExec('class Foo:') - interpreter.addExec(' CONSTANT=1') - interpreter.addExec('') - interpreter.addExec('foo=Foo()') - interpreter.addExec('foo.__doc__=None') - interpreter.addExec('val = %s()' % (raw_input_name,)) - interpreter.addExec('50') - interpreter.addExec('print (val)') - found = sys.stdout.getvalue().split() + self.original_stdout = sys.stdout + sys.stdout = StringIO() + try: - self.assertEqual(['50', 'input_request'], found) - except: - self.assertEqual(['input_request'], found) #IPython - - comps = interpreter.getCompletions('foo.', 'foo.') - self.assert_( - ('CONSTANT', '', '', '3') in comps or ('CONSTANT', '', '', '4') in comps, \ - 'Found: %s' % comps - ) - - comps = interpreter.getCompletions('"".', '"".') - self.assert_( - ('__add__', 'x.__add__(y) <==> x+y', '', '3') in comps or - ('__add__', '', '', '4') in comps or - ('__add__', 'x.__add__(y) <==> x+y\r\nx.__add__(y) <==> x+y', '()', '2') in comps or - ('__add__', 'x.\n__add__(y) <==> x+yx.\n__add__(y) <==> x+y', '()', '2'), - 'Did not find __add__ in : %s' % (comps,) - ) - - - completions = interpreter.getCompletions('', '') - for c in completions: - if c[0] == 'AssertionError': - break - else: - self.fail('Could not find AssertionError') - - completions = interpreter.getCompletions('Assert', 'Assert') - for c in completions: - if c[0] == 'RuntimeError': - self.fail('Did not expect to find RuntimeError there') - - self.assert_(('__doc__', None, '', '3') not in interpreter.getCompletions('foo.CO', 'foo.')) - - comps = interpreter.getCompletions('va', 'va') - self.assert_(('val', '', '', '3') in comps or ('val', '', '', '4') in comps) - - interpreter.addExec('s = "mystring"') - - desc = interpreter.getDescription('val') - self.assert_(desc.find('str(object) -> string') >= 0 or - desc == "'input_request'" or - desc.find('str(string[, encoding[, errors]]) -> str') >= 0 or - desc.find('str(Char* value)') >= 0 or - desc.find('str(value: Char*)') >= 0, - 'Could not find what was needed in %s' % desc) - - desc = interpreter.getDescription('val.join') - self.assert_(desc.find('S.join(sequence) -> string') >= 0 or - desc.find('S.join(sequence) -> str') >= 0 or - desc.find('S.join(iterable) -> string') >= 0 or - desc == "" or - desc == "" or - desc.find('str join(str self, list sequence)') >= 0 or - desc.find('S.join(iterable) -> str') >= 0 or - desc.find('join(self: str, sequence: list) -> str') >= 0, - "Could not recognize: %s" % (desc,)) + client_port, _server_port = self.getFreeAddresses() + client_thread = self.startClientThread(client_port) #@UnusedVariable + import time + time.sleep(.3) #let's give it some time to start the threads + + import pydev_localhost + from pydev_console_utils import CodeFragment + + interpreter = pydevconsole.InterpreterInterface(pydev_localhost.get_localhost(), client_port, threading.currentThread()) + sys.stdout = StringIO() + interpreter.addExec(CodeFragment('class Foo:')) + interpreter.addExec(CodeFragment(' CONSTANT=1')) + interpreter.addExec(CodeFragment('')) + interpreter.addExec(CodeFragment('foo=Foo()')) + interpreter.addExec(CodeFragment('foo.__doc__=None')) + interpreter.addExec(CodeFragment('val = %s()' % (raw_input_name,))) + interpreter.addExec(CodeFragment('50')) + interpreter.addExec(CodeFragment('print (val)')) + found = sys.stdout.getvalue().split() + try: + self.assertEqual(['50', 'input_request'], found) + except: + self.assertEqual(['input_request'], found) #IPython + + comps = interpreter.getCompletions('foo.', 'foo.') + self.assert_( + ('CONSTANT', '', '', '3') in comps or ('CONSTANT', '', '', '4') in comps, \ + 'Found: %s' % comps + ) + + comps = interpreter.getCompletions('"".', '"".') + self.assert_( + ('__add__', 'x.__add__(y) <==> x+y', '', '3') in comps or + ('__add__', '', '', '4') in comps or + ('__add__', 'x.__add__(y) <==> x+y\r\nx.__add__(y) <==> x+y', '()', '2') in comps or + ('__add__', 'x.\n__add__(y) <==> x+yx.\n__add__(y) <==> x+y', '()', '2'), + 'Did not find __add__ in : %s' % (comps,) + ) + + + completions = interpreter.getCompletions('', '') + for c in completions: + if c[0] == 'AssertionError': + break + else: + self.fail('Could not find AssertionError') + + completions = interpreter.getCompletions('Assert', 'Assert') + for c in completions: + if c[0] == 'RuntimeError': + self.fail('Did not expect to find RuntimeError there') + + self.assert_(('__doc__', None, '', '3') not in interpreter.getCompletions('foo.CO', 'foo.')) + + comps = interpreter.getCompletions('va', 'va') + self.assert_(('val', '', '', '3') in comps or ('val', '', '', '4') in comps) + + interpreter.addExec(CodeFragment('s = "mystring"')) + + desc = interpreter.getDescription('val') + self.assert_(desc.find('str(object) -> string') >= 0 or + desc == "'input_request'" or + desc.find('str(string[, encoding[, errors]]) -> str') >= 0 or + desc.find('str(Char* value)') >= 0 or + desc.find('str(object=\'\') -> string') >= 0 or + desc.find('str(value: Char*)') >= 0 or + desc.find('str(object=\'\') -> str') >= 0 + , + 'Could not find what was needed in %s' % desc) + + desc = interpreter.getDescription('val.join') + self.assert_(desc.find('S.join(sequence) -> string') >= 0 or + desc.find('S.join(sequence) -> str') >= 0 or + desc.find('S.join(iterable) -> string') >= 0 or + desc == "" or + desc == "" or + desc.find('str join(str self, list sequence)') >= 0 or + desc.find('S.join(iterable) -> str') >= 0 or + desc.find('join(self: str, sequence: list) -> str') >= 0, + "Could not recognize: %s" % (desc,)) + finally: + sys.stdout = self.original_stdout def startClientThread(self, client_port): @@ -124,19 +128,28 @@ class Test(unittest.TestCase): def __init__(self, client_port): threading.Thread.__init__(self) self.client_port = client_port + def run(self): class HandleRequestInput: def RequestInput(self): + client_thread.requested_input = True return 'input_request' - + + def NotifyFinished(self, *args, **kwargs): + client_thread.notified_finished += 1 + return 1 + handle_request_input = HandleRequestInput() - + import pydev_localhost client_server = SimpleXMLRPCServer((pydev_localhost.get_localhost(), self.client_port), logRequests=False) client_server.register_function(handle_request_input.RequestInput) + client_server.register_function(handle_request_input.NotifyFinished) client_server.serve_forever() - + client_thread = ClientThread(client_port) + client_thread.requested_input = False + client_thread.notified_finished = 0 client_thread.setDaemon(True) client_thread.start() return client_thread @@ -194,34 +207,50 @@ class Test(unittest.TestCase): def testServer(self): - client_port, server_port = self.getFreeAddresses() - class ServerThread(threading.Thread): - def __init__(self, client_port, server_port): - threading.Thread.__init__(self) - self.client_port = client_port - self.server_port = server_port - - def run(self): - import pydev_localhost - pydevconsole.StartServer(pydev_localhost.get_localhost(), self.server_port, self.client_port) - server_thread = ServerThread(client_port, server_port) - server_thread.setDaemon(True) - server_thread.start() - - client_thread = self.startClientThread(client_port) #@UnusedVariable - - import time - time.sleep(.3) #let's give it some time to start the threads - - import pydev_localhost - server = xmlrpclib.Server('http://%s:%s' % (pydev_localhost.get_localhost(), server_port)) - server.addExec('class Foo:') - server.addExec(' pass') - server.addExec('') - server.addExec('foo = Foo()') - server.addExec('a = %s()' % (raw_input_name,)) - server.addExec('print (a)') - self.assertEqual(['input_request'], sys.stdout.getvalue().split()) + self.original_stdout = sys.stdout + sys.stdout = StringIO() + try: + client_port, server_port = self.getFreeAddresses() + class ServerThread(threading.Thread): + def __init__(self, client_port, server_port): + threading.Thread.__init__(self) + self.client_port = client_port + self.server_port = server_port + + def run(self): + import pydev_localhost + pydevconsole.StartServer(pydev_localhost.get_localhost(), self.server_port, self.client_port) + server_thread = ServerThread(client_port, server_port) + server_thread.setDaemon(True) + server_thread.start() + + client_thread = self.startClientThread(client_port) #@UnusedVariable + + import time + time.sleep(.3) #let's give it some time to start the threads + sys.stdout = StringIO() + + import pydev_localhost + server = xmlrpclib.Server('http://%s:%s' % (pydev_localhost.get_localhost(), server_port)) + server.execLine('class Foo:') + server.execLine(' pass') + server.execLine('') + server.execLine('foo = Foo()') + server.execLine('a = %s()' % (raw_input_name,)) + server.execLine('print (a)') + initial = time.time() + while not client_thread.requested_input: + if time.time() - initial > 2: + raise AssertionError('Did not get the return asked before the timeout.') + time.sleep(.1) + + while ['input_request'] != sys.stdout.getvalue().split(): + if time.time() - initial > 2: + break + time.sleep(.1) + self.assertEqual(['input_request'], sys.stdout.getvalue().split()) + finally: + sys.stdout = self.original_stdout #======================================================================================================================= # main diff --git a/python/helpers/pydev/tests/test_pyserver.py b/python/helpers/pydev/tests/test_pyserver.py index a74876b8c8b9..ea9daff14e39 100644 --- a/python/helpers/pydev/tests/test_pyserver.py +++ b/python/helpers/pydev/tests/test_pyserver.py @@ -3,6 +3,7 @@ ''' import sys import os +from _pydev_imps._pydev_thread import start_new_thread #make it as if we were executing from the directory above this one (so that we can use pycompletionserver #without the need for it being in the pythonpath) @@ -13,6 +14,13 @@ sys.path.insert(1, os.path.join(os.path.dirname(sys.argv[0]))) IS_PYTHON_3K = 0 if sys.platform.find('java') == -1: + try: + import __builtin__ #@UnusedImport + BUILTIN_MOD = '__builtin__' + except ImportError: + BUILTIN_MOD = 'builtins' + + try: import inspect @@ -41,7 +49,7 @@ if sys.platform.find('java') == -1: unittest.TestCase.tearDown(self) def testMessage(self): - t = pycompletionserver.T(0) + t = pycompletionserver.CompletionServer(0) l = [] l.append(('Def', 'description' , 'args')) @@ -62,14 +70,14 @@ if sys.platform.find('java') == -1: ''' Creates the connections needed for testing. ''' - t = pycompletionserver.T(p1) - - t.start() - server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.bind((pycompletionserver.HOST, p1)) server.listen(1) #socket to receive messages. + t = pycompletionserver.CompletionServer(p1) + t.exit_process_on_kill = False + start_new_thread(t.run, ()) + s, addr = server.accept() return t, s @@ -106,6 +114,8 @@ if sys.platform.find('java') == -1: #math is a builtin and because of that, it starts with None as a file start = '@@COMPLETIONS(None,(__doc__,' start_2 = '@@COMPLETIONS(None,(__name__,' + if '/math.so,' in completions or '/math.cpython-33m.so,' in completions or '/math.cpython-34m.so,' in completions: + return self.assert_(completions.startswith(start) or completions.startswith(start_2), '%s DOESNT START WITH %s' % (completions, (start, start_2))) self.assert_('@@COMPLETIONS' in completions) @@ -113,7 +123,7 @@ if sys.platform.find('java') == -1: #now, test i - msg = quote_plus('__builtin__.list') + msg = quote_plus('%s.list' % BUILTIN_MOD) send(socket, "@@IMPORTS:%s\nEND@@" % msg) found = self.readMsg() self.assert_('sort' in found, 'Could not find sort in: %s' % (found,)) diff --git a/python/helpers/pydev/tests/test_simpleTipper.py b/python/helpers/pydev/tests/test_simpleTipper.py index f759ad60f678..255a5218698f 100644 --- a/python/helpers/pydev/tests/test_simpleTipper.py +++ b/python/helpers/pydev/tests/test_simpleTipper.py @@ -1,12 +1,7 @@ ''' @author Fabio Zadrozny ''' -import os import sys -#make it as if we were executing from the directory above this one (so that we can use pycompletionserver -#without the need for it being in the pythonpath) -#twice the dirname to get the previous level from this file. -sys.path.insert(1, os.path.split(os.path.split(__file__)[0])[0]) try: import __builtin__ #@UnusedImport @@ -50,13 +45,14 @@ if sys.platform.find('java') == -1: pass def testImports5(self): - tip = _pydev_imports_tipper.GenerateTip('__builtin__.list') + tip = _pydev_imports_tipper.GenerateTip('%s.list' % BUILTIN_MOD) s = self.assertIn('sort', tip) self.CheckArgs( s, '(cmp=None, key=None, reverse=False)', '(self, object cmp, object key, bool reverse)', - '(self, cmp: object, key: object, reverse: bool)' + '(self, cmp: object, key: object, reverse: bool)', + '(key=None, reverse=False)', ) def testImports2a(self): @@ -64,14 +60,24 @@ if sys.platform.find('java') == -1: self.assertIn('__doc__', tips) def testImports2b(self): - tips = _pydev_imports_tipper.GenerateTip('%s' % BUILTIN_MOD) - t = self.assertIn('file' , tips) - self.assert_('->' in t[1].strip() or 'file' in t[1]) + try: + file + except: + pass + else: + tips = _pydev_imports_tipper.GenerateTip('%s' % BUILTIN_MOD) + t = self.assertIn('file' , tips) + self.assert_('->' in t[1].strip() or 'file' in t[1]) def testImports2c(self): - tips = _pydev_imports_tipper.GenerateTip('%s.file' % BUILTIN_MOD) - t = self.assertIn('readlines' , tips) - self.assert_('->' in t[1] or 'sizehint' in t[1]) + try: + file # file is not available on py 3 + except: + pass + else: + tips = _pydev_imports_tipper.GenerateTip('%s.file' % BUILTIN_MOD) + t = self.assertIn('readlines' , tips) + self.assert_('->' in t[1] or 'sizehint' in t[1]) def testImports(self): ''' @@ -110,9 +116,9 @@ if sys.platform.find('java') == -1: self.assertIn('RuntimeError' , tip) self.assertIn('RuntimeWarning' , tip) - t = self.assertIn('cmp' , tip) - - self.CheckArgs(t, '(x, y)', '(object x, object y)', '(x: object, y: object)') #args + # Remove cmp as it's not available on py 3 + #t = self.assertIn('cmp' , tip) + #self.CheckArgs(t, '(x, y)', '(object x, object y)', '(x: object, y: object)') #args t = self.assertIn('isinstance' , tip) self.CheckArgs(t, '(object, class_or_type_or_tuple)', '(object o, type typeinfo)', '(o: object, typeinfo: type)') #args diff --git a/python/helpers/pydev/tests_mainloop/gui-glut.py b/python/helpers/pydev/tests_mainloop/gui-glut.py index f05a4bc0beaf..34a16b454171 100644 --- a/python/helpers/pydev/tests_mainloop/gui-glut.py +++ b/python/helpers/pydev/tests_mainloop/gui-glut.py @@ -9,42 +9,44 @@ To run this: 4) run: gl.glClearColor(1,1,1,1) """ -#!/usr/bin/env python -import sys -import OpenGL.GL as gl -import OpenGL.GLUT as glut +if __name__ == '__main__': -def close(): - glut.glutDestroyWindow(glut.glutGetWindow()) - -def display(): - gl.glClear (gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT) - glut.glutSwapBuffers() - -def resize(width,height): - gl.glViewport(0, 0, width, height+4) - gl.glMatrixMode(gl.GL_PROJECTION) - gl.glLoadIdentity() - gl.glOrtho(0, width, 0, height+4, -1, 1) - gl.glMatrixMode(gl.GL_MODELVIEW) - -if glut.glutGetWindow() > 0: - interactive = True - glut.glutInit(sys.argv) - glut.glutInitDisplayMode(glut.GLUT_DOUBLE | - glut.GLUT_RGBA | - glut.GLUT_DEPTH) -else: - interactive = False - -glut.glutCreateWindow('gui-glut') -glut.glutDisplayFunc(display) -glut.glutReshapeFunc(resize) -# This is necessary on osx to be able to close the window -# (else the close button is disabled) -if sys.platform == 'darwin' and not bool(glut.HAVE_FREEGLUT): - glut.glutWMCloseFunc(close) -gl.glClearColor(0,0,0,1) - -if not interactive: - glut.glutMainLoop() + #!/usr/bin/env python + import sys + import OpenGL.GL as gl + import OpenGL.GLUT as glut + + def close(): + glut.glutDestroyWindow(glut.glutGetWindow()) + + def display(): + gl.glClear (gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT) + glut.glutSwapBuffers() + + def resize(width,height): + gl.glViewport(0, 0, width, height+4) + gl.glMatrixMode(gl.GL_PROJECTION) + gl.glLoadIdentity() + gl.glOrtho(0, width, 0, height+4, -1, 1) + gl.glMatrixMode(gl.GL_MODELVIEW) + + if glut.glutGetWindow() > 0: + interactive = True + glut.glutInit(sys.argv) + glut.glutInitDisplayMode(glut.GLUT_DOUBLE | + glut.GLUT_RGBA | + glut.GLUT_DEPTH) + else: + interactive = False + + glut.glutCreateWindow('gui-glut') + glut.glutDisplayFunc(display) + glut.glutReshapeFunc(resize) + # This is necessary on osx to be able to close the window + # (else the close button is disabled) + if sys.platform == 'darwin' and not bool(glut.HAVE_FREEGLUT): + glut.glutWMCloseFunc(close) + gl.glClearColor(0,0,0,1) + + if not interactive: + glut.glutMainLoop() diff --git a/python/helpers/pydev/tests_mainloop/gui-gtk.py b/python/helpers/pydev/tests_mainloop/gui-gtk.py index 978f8f9a25f3..6df5c782e96a 100644 --- a/python/helpers/pydev/tests_mainloop/gui-gtk.py +++ b/python/helpers/pydev/tests_mainloop/gui-gtk.py @@ -8,27 +8,28 @@ To run this: interactive console """ -import pygtk -pygtk.require('2.0') -import gtk - - -def hello_world(wigdet, data=None): - print("Hello World") - -def delete_event(widget, event, data=None): - return False - -def destroy(widget, data=None): - gtk.main_quit() - -window = gtk.Window(gtk.WINDOW_TOPLEVEL) -window.connect("delete_event", delete_event) -window.connect("destroy", destroy) -button = gtk.Button("Hello World") -button.connect("clicked", hello_world, None) - -window.add(button) -button.show() -window.show() - +if __name__ == '__main__': + import pygtk + pygtk.require('2.0') + import gtk + + + def hello_world(wigdet, data=None): + print("Hello World") + + def delete_event(widget, event, data=None): + return False + + def destroy(widget, data=None): + gtk.main_quit() + + window = gtk.Window(gtk.WINDOW_TOPLEVEL) + window.connect("delete_event", delete_event) + window.connect("destroy", destroy) + button = gtk.Button("Hello World") + button.connect("clicked", hello_world, None) + + window.add(button) + button.show() + window.show() + diff --git a/python/helpers/pydev/tests_mainloop/gui-gtk3.py b/python/helpers/pydev/tests_mainloop/gui-gtk3.py index a787f7ee9e65..6351d5235ded 100644 --- a/python/helpers/pydev/tests_mainloop/gui-gtk3.py +++ b/python/helpers/pydev/tests_mainloop/gui-gtk3.py @@ -8,25 +8,26 @@ To run this: interactive console """ -from gi.repository import Gtk - - -def hello_world(wigdet, data=None): - print("Hello World") - -def delete_event(widget, event, data=None): - return False - -def destroy(widget, data=None): - Gtk.main_quit() - -window = Gtk.Window(Gtk.WindowType.TOPLEVEL) -window.connect("delete_event", delete_event) -window.connect("destroy", destroy) -button = Gtk.Button("Hello World") -button.connect("clicked", hello_world, None) - -window.add(button) -button.show() -window.show() - +if __name__ == '__main__': + from gi.repository import Gtk + + + def hello_world(wigdet, data=None): + print("Hello World") + + def delete_event(widget, event, data=None): + return False + + def destroy(widget, data=None): + Gtk.main_quit() + + window = Gtk.Window(Gtk.WindowType.TOPLEVEL) + window.connect("delete_event", delete_event) + window.connect("destroy", destroy) + button = Gtk.Button("Hello World") + button.connect("clicked", hello_world, None) + + window.add(button) + button.show() + window.show() + diff --git a/python/helpers/pydev/tests_mainloop/gui-pyglet.py b/python/helpers/pydev/tests_mainloop/gui-pyglet.py index b646093e0967..70f1a7f64724 100644 --- a/python/helpers/pydev/tests_mainloop/gui-pyglet.py +++ b/python/helpers/pydev/tests_mainloop/gui-pyglet.py @@ -8,20 +8,21 @@ To run this: interactive console """ -import pyglet - - -window = pyglet.window.Window() -label = pyglet.text.Label('Hello, world', - font_name='Times New Roman', - font_size=36, - x=window.width//2, y=window.height//2, - anchor_x='center', anchor_y='center') -@window.event -def on_close(): - window.close() - -@window.event -def on_draw(): - window.clear() - label.draw() +if __name__ == '__main__': + import pyglet + + + window = pyglet.window.Window() + label = pyglet.text.Label('Hello, world', + font_name='Times New Roman', + font_size=36, + x=window.width//2, y=window.height//2, + anchor_x='center', anchor_y='center') + @window.event + def on_close(): + window.close() + + @window.event + def on_draw(): + window.clear() + label.draw() diff --git a/python/helpers/pydev/tests_mainloop/gui-qt.py b/python/helpers/pydev/tests_mainloop/gui-qt.py index c27cbd6ff1e6..30fc48d3845a 100644 --- a/python/helpers/pydev/tests_mainloop/gui-qt.py +++ b/python/helpers/pydev/tests_mainloop/gui-qt.py @@ -10,26 +10,27 @@ To run this: Ref: Modified from http://zetcode.com/tutorials/pyqt4/firstprograms/ """ -import sys -from PyQt4 import QtGui, QtCore - -class SimpleWindow(QtGui.QWidget): - def __init__(self, parent=None): - QtGui.QWidget.__init__(self, parent) - - self.setGeometry(300, 300, 200, 80) - self.setWindowTitle('Hello World') - - quit = QtGui.QPushButton('Close', self) - quit.setGeometry(10, 10, 60, 35) - - self.connect(quit, QtCore.SIGNAL('clicked()'), - self, QtCore.SLOT('close()')) - if __name__ == '__main__': - app = QtCore.QCoreApplication.instance() - if app is None: - app = QtGui.QApplication([]) - - sw = SimpleWindow() - sw.show() + import sys + from PyQt4 import QtGui, QtCore + + class SimpleWindow(QtGui.QWidget): + def __init__(self, parent=None): + QtGui.QWidget.__init__(self, parent) + + self.setGeometry(300, 300, 200, 80) + self.setWindowTitle('Hello World') + + quit = QtGui.QPushButton('Close', self) + quit.setGeometry(10, 10, 60, 35) + + self.connect(quit, QtCore.SIGNAL('clicked()'), + self, QtCore.SLOT('close()')) + + if __name__ == '__main__': + app = QtCore.QCoreApplication.instance() + if app is None: + app = QtGui.QApplication([]) + + sw = SimpleWindow() + sw.show() diff --git a/python/helpers/pydev/tests_mainloop/gui-tk.py b/python/helpers/pydev/tests_mainloop/gui-tk.py index 69ceb0b9f053..4cef45f91a4a 100644 --- a/python/helpers/pydev/tests_mainloop/gui-tk.py +++ b/python/helpers/pydev/tests_mainloop/gui-tk.py @@ -8,24 +8,26 @@ To run this: interactive console """ -try: - from Tkinter import * -except: - # Python 3 - from tkinter import * - -class MyApp: - - def __init__(self, root): - frame = Frame(root) - frame.pack() - - self.button = Button(frame, text="Hello", command=self.hello_world) - self.button.pack(side=LEFT) - - def hello_world(self): - print("Hello World!") - -root = Tk() - -app = MyApp(root) +if __name__ == '__main__': + + try: + from Tkinter import * + except: + # Python 3 + from tkinter import * + + class MyApp: + + def __init__(self, root): + frame = Frame(root) + frame.pack() + + self.button = Button(frame, text="Hello", command=self.hello_world) + self.button.pack(side=LEFT) + + def hello_world(self): + print("Hello World!") + + root = Tk() + + app = MyApp(root) diff --git a/python/helpers/pydev/tests_mainloop/gui-wx.py b/python/helpers/pydev/tests_mainloop/gui-wx.py index 2101e7f214d4..b9c28bfc63ab 100644 --- a/python/helpers/pydev/tests_mainloop/gui-wx.py +++ b/python/helpers/pydev/tests_mainloop/gui-wx.py @@ -11,91 +11,93 @@ To run this: Ref: Modified from wxPython source code wxPython/samples/simple/simple.py """ -import wx - - -class MyFrame(wx.Frame): - """ - This is MyFrame. It just shows a few controls on a wxPanel, - and has a simple menu. - """ - def __init__(self, parent, title): - wx.Frame.__init__(self, parent, -1, title, - pos=(150, 150), size=(350, 200)) - - # Create the menubar - menuBar = wx.MenuBar() - - # and a menu - menu = wx.Menu() - - # add an item to the menu, using \tKeyName automatically - # creates an accelerator, the third param is some help text - # that will show up in the statusbar - menu.Append(wx.ID_EXIT, "E&xit\tAlt-X", "Exit this simple sample") - - # bind the menu event to an event handler - self.Bind(wx.EVT_MENU, self.OnTimeToClose, id=wx.ID_EXIT) - - # and put the menu on the menubar - menuBar.Append(menu, "&File") - self.SetMenuBar(menuBar) - - self.CreateStatusBar() - - # Now create the Panel to put the other controls on. - panel = wx.Panel(self) - - # and a few controls - text = wx.StaticText(panel, -1, "Hello World!") - text.SetFont(wx.Font(14, wx.SWISS, wx.NORMAL, wx.BOLD)) - text.SetSize(text.GetBestSize()) - btn = wx.Button(panel, -1, "Close") - funbtn = wx.Button(panel, -1, "Just for fun...") - - # bind the button events to handlers - self.Bind(wx.EVT_BUTTON, self.OnTimeToClose, btn) - self.Bind(wx.EVT_BUTTON, self.OnFunButton, funbtn) - - # Use a sizer to layout the controls, stacked vertically and with - # a 10 pixel border around each - sizer = wx.BoxSizer(wx.VERTICAL) - sizer.Add(text, 0, wx.ALL, 10) - sizer.Add(btn, 0, wx.ALL, 10) - sizer.Add(funbtn, 0, wx.ALL, 10) - panel.SetSizer(sizer) - panel.Layout() - - - def OnTimeToClose(self, evt): - """Event handler for the button click.""" - print("See ya later!") - self.Close() - - def OnFunButton(self, evt): - """Event handler for the button click.""" - print("Having fun yet?") - - -class MyApp(wx.App): - def OnInit(self): - frame = MyFrame(None, "Simple wxPython App") - self.SetTopWindow(frame) - - print("Print statements go to this stdout window by default.") - - frame.Show(True) - return True - - if __name__ == '__main__': - app = wx.GetApp() - if app is None: - app = MyApp(redirect=False, clearSigInt=False) - else: - frame = MyFrame(None, "Simple wxPython App") - app.SetTopWindow(frame) - print("Print statements go to this stdout window by default.") - frame.Show(True) - + import wx + + + class MyFrame(wx.Frame): + """ + This is MyFrame. It just shows a few controls on a wxPanel, + and has a simple menu. + """ + def __init__(self, parent, title): + wx.Frame.__init__(self, parent, -1, title, + pos=(150, 150), size=(350, 200)) + + # Create the menubar + menuBar = wx.MenuBar() + + # and a menu + menu = wx.Menu() + + # add an item to the menu, using \tKeyName automatically + # creates an accelerator, the third param is some help text + # that will show up in the statusbar + menu.Append(wx.ID_EXIT, "E&xit\tAlt-X", "Exit this simple sample") + + # bind the menu event to an event handler + self.Bind(wx.EVT_MENU, self.OnTimeToClose, id=wx.ID_EXIT) + + # and put the menu on the menubar + menuBar.Append(menu, "&File") + self.SetMenuBar(menuBar) + + self.CreateStatusBar() + + # Now create the Panel to put the other controls on. + panel = wx.Panel(self) + + # and a few controls + text = wx.StaticText(panel, -1, "Hello World!") + text.SetFont(wx.Font(14, wx.SWISS, wx.NORMAL, wx.BOLD)) + text.SetSize(text.GetBestSize()) + btn = wx.Button(panel, -1, "Close") + funbtn = wx.Button(panel, -1, "Just for fun...") + + # bind the button events to handlers + self.Bind(wx.EVT_BUTTON, self.OnTimeToClose, btn) + self.Bind(wx.EVT_BUTTON, self.OnFunButton, funbtn) + + # Use a sizer to layout the controls, stacked vertically and with + # a 10 pixel border around each + sizer = wx.BoxSizer(wx.VERTICAL) + sizer.Add(text, 0, wx.ALL, 10) + sizer.Add(btn, 0, wx.ALL, 10) + sizer.Add(funbtn, 0, wx.ALL, 10) + panel.SetSizer(sizer) + panel.Layout() + + + def OnTimeToClose(self, evt): + """Event handler for the button click.""" + print("See ya later!") + self.Close() + + def OnFunButton(self, evt): + """Event handler for the button click.""" + print("Having fun yet?") + + + class MyApp(wx.App): + def OnInit(self): + frame = MyFrame(None, "Simple wxPython App") + self.SetTopWindow(frame) + + print("Print statements go to this stdout window by default.") + + frame.Show(True) + return True + + + if __name__ == '__main__': + + app = wx.GetApp() + if app is None: + app = MyApp(redirect=False, clearSigInt=False) + else: + frame = MyFrame(None, "Simple wxPython App") + app.SetTopWindow(frame) + print("Print statements go to this stdout window by default.") + frame.Show(True) + diff --git a/python/helpers/pydev/tests_python/_debugger_case18.py b/python/helpers/pydev/tests_python/_debugger_case18.py index 8b34eedcfc59..c221039fd774 100644 --- a/python/helpers/pydev/tests_python/_debugger_case18.py +++ b/python/helpers/pydev/tests_python/_debugger_case18.py @@ -6,7 +6,7 @@ def m2(a): c = 30 def function2(): - print a + print(a) return a diff --git a/python/helpers/pydev/tests_python/_debugger_case19.py b/python/helpers/pydev/tests_python/_debugger_case19.py index aaf380c8a3f5..07ac951f05e0 100644 --- a/python/helpers/pydev/tests_python/_debugger_case19.py +++ b/python/helpers/pydev/tests_python/_debugger_case19.py @@ -5,6 +5,6 @@ class A: if __name__ == '__main__': a = A() - print a._A__var + print(a._A__var) # Evaluate 'a.__var' should give a._A__var_ print('TEST SUCEEDED') diff --git a/python/helpers/pydev/tests_python/_debugger_case7.py b/python/helpers/pydev/tests_python/_debugger_case7.py index 263110b1e990..499d8d76e650 100644 --- a/python/helpers/pydev/tests_python/_debugger_case7.py +++ b/python/helpers/pydev/tests_python/_debugger_case7.py @@ -5,4 +5,4 @@ def Call(): if __name__ == '__main__': Call() - print 'TEST SUCEEDED!' + print('TEST SUCEEDED!') diff --git a/python/helpers/pydev/tests_python/_debugger_case89.py b/python/helpers/pydev/tests_python/_debugger_case89.py index e6f32dd522bb..e22361d5a38d 100644 --- a/python/helpers/pydev/tests_python/_debugger_case89.py +++ b/python/helpers/pydev/tests_python/_debugger_case89.py @@ -1,16 +1,16 @@ def Method1(): - print 'm1' + print('m1') def Method2(): - print 'm2 before' + print('m2 before') Method1() - print 'm2 after' + print('m2 after') def Method3(): - print 'm3 before' + print('m3 before') Method2() - print 'm3 after' + print('m3 after') if __name__ == '__main__': Method3() - print 'TEST SUCEEDED!' + print('TEST SUCEEDED!') diff --git a/python/helpers/pydev/tests_python/_debugger_case_qthread2.py b/python/helpers/pydev/tests_python/_debugger_case_qthread2.py index f9dca5ffa34f..b2ce3159652c 100644 --- a/python/helpers/pydev/tests_python/_debugger_case_qthread2.py +++ b/python/helpers/pydev/tests_python/_debugger_case_qthread2.py @@ -1,4 +1,5 @@ import time +import sys try: from PySide import QtCore diff --git a/python/helpers/pydev/tests_python/_debugger_case_qthread3.py b/python/helpers/pydev/tests_python/_debugger_case_qthread3.py index d7a724cc0a1a..22b0c91d7f13 100644 --- a/python/helpers/pydev/tests_python/_debugger_case_qthread3.py +++ b/python/helpers/pydev/tests_python/_debugger_case_qthread3.py @@ -1,4 +1,5 @@ import time +import sys try: from PySide import QtCore diff --git a/python/helpers/pydev/tests_python/test_additional_thread_info.py b/python/helpers/pydev/tests_python/test_additional_thread_info.py index 6ae260d6a658..71dc35243257 100644 --- a/python/helpers/pydev/tests_python/test_additional_thread_info.py +++ b/python/helpers/pydev/tests_python/test_additional_thread_info.py @@ -1,10 +1,21 @@ import sys import os +import pydev_monkey sys.path.insert(0, os.path.split(os.path.split(__file__)[0])[0]) from pydevd_constants import Null import unittest +try: + import thread +except: + import _thread as thread + +try: + xrange +except: + xrange = range + #======================================================================================================================= # TestCase #======================================================================================================================= @@ -40,10 +51,7 @@ class TestCase(unittest.TestCase): def testStartNewThread(self): - import pydevd - import thread - original = thread.start_new_thread - thread.start_new_thread = pydevd.pydev_start_new_thread + pydev_monkey.patch_thread_modules() try: found = {} def function(a, b, *args, **kwargs): @@ -62,15 +70,11 @@ class TestCase(unittest.TestCase): self.assertEqual({'a': 1, 'b': 2, 'args': (3, 4), 'kwargs': {'e': 2, 'd': 1}}, found) finally: - thread.start_new_thread = original + pydev_monkey.undo_patch_thread_modules() def testStartNewThread2(self): - import pydevd - import thread - - original = thread.start_new_thread - thread.start_new_thread = pydevd.pydev_start_new_thread + pydev_monkey.patch_thread_modules() try: found = {} @@ -101,7 +105,7 @@ class TestCase(unittest.TestCase): self.assertEqual({'a': 1, 'b': 2, 'args': (3, 4), 'kwargs': {'e': 2, 'd': 1}}, found) finally: - thread.start_new_thread = original + pydev_monkey.undo_patch_thread_modules() #======================================================================================================================= diff --git a/python/helpers/pydev/tests_python/test_debugger.py b/python/helpers/pydev/tests_python/test_debugger.py index fe1fa74a3a6e..ea569ddce556 100644 --- a/python/helpers/pydev/tests_python/test_debugger.py +++ b/python/helpers/pydev/tests_python/test_debugger.py @@ -5,6 +5,18 @@ Note that it's a python script but it'll spawn a process to run as jython, ironpython and as python. ''' +SHOW_WRITES_AND_READS = False +SHOW_OTHER_DEBUG_INFO = False +SHOW_STDOUT = False + + + +from pydevd_constants import IS_PY3K +try: + from thread import start_new_thread +except: + from _thread import start_new_thread +CMD_SET_PROPERTY_TRACE, CMD_EVALUATE_CONSOLE_EXPRESSION, CMD_RUN_CUSTOM_OPERATION, CMD_ENABLE_DONT_TRACE = 133, 134, 135, 141 PYTHON_EXE = None IRONPYTHON_EXE = None JYTHON_JAR_LOCATION = None @@ -13,9 +25,13 @@ JAVA_LOCATION = None import unittest import pydev_localhost -import re port = None +try: + xrange +except: + xrange = range + def UpdatePort(): global port s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) @@ -24,29 +40,26 @@ def UpdatePort(): s.close() import os -def NormFile(filename): +def _get_debugger_test_file(filename): try: - rPath = os.path.realpath #@UndefinedVariable + rPath = os.path.realpath # @UndefinedVariable except: # jython does not support os.path.realpath # realpath is a no-op on systems without islink support rPath = os.path.abspath - return os.path.normcase(rPath(filename)) + + return os.path.normcase(rPath(os.path.join(os.path.dirname(__file__), filename))) + +import pydevd +PYDEVD_FILE = pydevd.__file__ -PYDEVD_FILE = NormFile('../pydevd.py') import sys -sys.path.append(os.path.dirname(PYDEVD_FILE)) - -SHOW_WRITES_AND_READS = False -SHOW_RESULT_STR = False -SHOW_OTHER_DEBUG_INFO = False - import subprocess import socket import threading import time -from urllib import quote_plus, quote +from pydev_imports import quote_plus, quote, unquote_plus #======================================================================================================================= @@ -58,13 +71,16 @@ class ReaderThread(threading.Thread): threading.Thread.__init__(self) self.setDaemon(True) self.sock = sock - self.lastReceived = None + self.lastReceived = '' def run(self): + last_printed = None try: buf = '' while True: l = self.sock.recv(1024) + if IS_PY3K: + l = l.decode('utf-8') buf += l if '\n' in buf: @@ -72,9 +88,11 @@ class ReaderThread(threading.Thread): buf = '' if SHOW_WRITES_AND_READS: - print 'Test Reader Thread Received %s' % self.lastReceived.strip() + if last_printed != self.lastReceived.strip(): + last_printed = self.lastReceived.strip() + print('Test Reader Thread Received %s' % last_printed) except: - pass #ok, finished it + pass # ok, finished it def DoKill(self): self.sock.close() @@ -89,18 +107,24 @@ class AbstractWriterThread(threading.Thread): self.setDaemon(True) self.finishedOk = False self._next_breakpoint_id = 0 + self.log = [] + def DoKill(self): if hasattr(self, 'readerThread'): - #if it's not created, it's not there... + # if it's not created, it's not there... self.readerThread.DoKill() self.sock.close() def Write(self, s): + last = self.readerThread.lastReceived if SHOW_WRITES_AND_READS: - print 'Test Writer Thread Written %s' % (s,) - self.sock.send(s + '\n') + print('Test Writer Thread Written %s' % (s,)) + msg = s + '\n' + if IS_PY3K: + msg = msg.encode('utf-8') + self.sock.send(msg) time.sleep(0.2) i = 0 @@ -111,24 +135,25 @@ class AbstractWriterThread(threading.Thread): def StartSocket(self): if SHOW_WRITES_AND_READS: - print 'StartSocket' + print('StartSocket') s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind(('', port)) s.listen(1) if SHOW_WRITES_AND_READS: - print 'Waiting in socket.accept()' + print('Waiting in socket.accept()') newSock, addr = s.accept() if SHOW_WRITES_AND_READS: - print 'Test Writer Thread Socket:', newSock, addr + print('Test Writer Thread Socket:', newSock, addr) readerThread = self.readerThread = ReaderThread(newSock) readerThread.start() self.sock = newSock self._sequence = -1 - #initial command is always the version + # initial command is always the version self.WriteVersion() + self.log.append('StartSocket') def NextBreakpointId(self): self._next_breakpoint_id += 1 @@ -141,14 +166,14 @@ class AbstractWriterThread(threading.Thread): def WaitForNewThread(self): i = 0 - #wait for hit breakpoint + # wait for hit breakpoint while not '= 15: raise AssertionError('After %s seconds, a thread was not created.' % i) - #we have something like + # we have something like splitted = self.readerThread.lastReceived.split('"') threadId = splitted[3] return threadId @@ -159,27 +184,32 @@ class AbstractWriterThread(threading.Thread): 109 is return 111 is breakpoint ''' + self.log.append('Start: WaitForBreakpointHit') i = 0 - #wait for hit breakpoint - while not ('stop_reason="%s"' % reason) in self.readerThread.lastReceived: + # wait for hit breakpoint + last = self.readerThread.lastReceived + while not ('stop_reason="%s"' % reason) in last: i += 1 time.sleep(1) + last = self.readerThread.lastReceived if i >= 10: raise AssertionError('After %s seconds, a break with reason: %s was not hit. Found: %s' % \ - (i, reason, self.readerThread.lastReceived)) + (i, reason, last)) - #we have something like = 10: - raise AssertionError('After %s seconds, the expected evaluation was not found. Last found:\n%s' % - (i, self.readerThread.lastReceived)) + return self._WaitFor(expected, 'the expected evaluation was not found') - return True def WaitForVars(self, expected): i = 0 - #wait for hit breakpoint + # wait for hit breakpoint while not expected in self.readerThread.lastReceived: i += 1 time.sleep(1) @@ -215,35 +237,49 @@ class AbstractWriterThread(threading.Thread): return True def WaitForVar(self, expected): + self._WaitFor(expected, 'the var was not found') + + def _WaitFor(self, expected, error_msg): + ''' + :param expected: + If a list we'll work with any of the choices. + ''' + if not isinstance(expected, (list, tuple)): + expected = [expected] + i = 0 - while not expected in self.readerThread.lastReceived: + found = False + while not found: + last = self.readerThread.lastReceived + for e in expected: + if e in last: + found = True + break + + last = unquote_plus(last) + for e in expected: + if e in last: + found = True + break + + if found: + break + i += 1 time.sleep(1) if i >= 10: - raise AssertionError('After %s seconds, the var was not found. Last found:\n%s' % - (i, self.readerThread.lastReceived)) - - return True - - def WaitForVarRE(self, expected_regular_expression): - i = 0 - pattern = re.compile(expected_regular_expression) - while not pattern.search(self.readerThread.lastReceived): - i += 1 - time.sleep(1) - if i >= 10: - raise AssertionError('After %s seconds, the var (using RE) was not found. Last found:\n%s' % - (i, self.readerThread.lastReceived)) + raise AssertionError('After %s seconds, %s. Last found:\n%s' % + (i, error_msg, last)) return True def WaitForMultipleVars(self, expected_vars): i = 0 - #wait for hit breakpoint + # wait for hit breakpoint while True: for expected in expected_vars: if expected not in self.readerThread.lastReceived: - break #Break out of loop (and don't get to else) + break # Break out of loop (and don't get to else) else: return True @@ -257,29 +293,29 @@ class AbstractWriterThread(threading.Thread): def WriteMakeInitialRun(self): self.Write("101\t%s\t" % self.NextSeq()) + self.log.append('WriteMakeInitialRun') def WriteVersion(self): - self.Write("501\t%s\t1.0" % self.NextSeq()) + self.Write("501\t%s\t1.0\tWINDOWS\tID" % self.NextSeq()) def WriteAddBreakpoint(self, line, func): ''' @param line: starts at 1 ''' breakpoint_id = self.NextBreakpointId() - if func is not None: - self.Write("111\t%s\t%s\t%s\t%s\t**FUNC**%s\tNone" % (self.NextSeq(), breakpoint_id, self.TEST_FILE, line, func)) - else: - self.Write("111\t%s\t%s\t%s\t%s\tNone" % (self.NextSeq(), breakpoint_id, self.TEST_FILE, line)) + self.Write("111\t%s\t%s\t%s\t%s\t%s\t%s\tNone\tNone" % (self.NextSeq(), breakpoint_id, 'python-line', self.TEST_FILE, line, func)) + self.log.append('WriteAddBreakpoint: %s line: %s func: %s' % (breakpoint_id, line, func)) return breakpoint_id def WriteRemoveBreakpoint(self, breakpoint_id): - self.Write("112\t%s\t%s\t%s" % (self.NextSeq(), breakpoint_id, self.TEST_FILE)) + self.Write("112\t%s\t%s\t%s\t%s" % (self.NextSeq(), 'python-line', self.TEST_FILE, breakpoint_id)) def WriteChangeVariable(self, thread_id, frame_id, varname, value): self.Write("117\t%s\t%s\t%s\t%s\t%s\t%s" % (self.NextSeq(), thread_id, frame_id, 'FRAME', varname, value)) def WriteGetFrame(self, threadId, frameId): self.Write("114\t%s\t%s\t%s\tFRAME" % (self.NextSeq(), threadId, frameId)) + self.log.append('WriteGetFrame') def WriteGetVariable(self, threadId, frameId, var_attrs): self.Write("110\t%s\t%s\t%s\tFRAME\t%s" % (self.NextSeq(), threadId, frameId, var_attrs)) @@ -297,26 +333,27 @@ class AbstractWriterThread(threading.Thread): self.Write("105\t%s\t%s" % (self.NextSeq(), threadId,)) def WriteRunThread(self, threadId): + self.log.append('WriteRunThread') self.Write("106\t%s\t%s" % (self.NextSeq(), threadId,)) def WriteKillThread(self, threadId): self.Write("104\t%s\t%s" % (self.NextSeq(), threadId,)) def WriteDebugConsoleExpression(self, locator): - self.Write("126\t%s\t%s" % (self.NextSeq(), locator)) + self.Write("%s\t%s\t%s" % (CMD_EVALUATE_CONSOLE_EXPRESSION, self.NextSeq(), locator)) def WriteCustomOperation(self, locator, style, codeOrFile, operation_fn_name): - self.Write("127\t%s\t%s||%s\t%s\t%s" % (self.NextSeq(), locator, style, codeOrFile, operation_fn_name)) + self.Write("%s\t%s\t%s||%s\t%s\t%s" % (CMD_RUN_CUSTOM_OPERATION, self.NextSeq(), locator, style, codeOrFile, operation_fn_name)) def WriteEvaluateExpression(self, locator, expression): - self.Write("113\t%s\t%s\t%s" % (self.NextSeq(), locator, expression)) + self.Write("113\t%s\t%s\t%s\t1" % (self.NextSeq(), locator, expression)) def WriteEnableDontTrace(self, enable): if enable: enable = 'true' else: enable = 'false' - self.Write("133\t%s\t%s" % (self.NextSeq(), enable)) + self.Write("%s\t%s\t%s" % (CMD_ENABLE_DONT_TRACE, self.NextSeq(), enable)) #======================================================================================================================= @@ -324,7 +361,7 @@ class AbstractWriterThread(threading.Thread): #====================================================================================================================== class WriterThreadCase19(AbstractWriterThread): - TEST_FILE = NormFile('_debugger_case19.py') + TEST_FILE = _get_debugger_test_file('_debugger_case19.py') def run(self): self.StartSocket() @@ -348,7 +385,7 @@ class WriterThreadCase19(AbstractWriterThread): #====================================================================================================================== class WriterThreadCase18(AbstractWriterThread): - TEST_FILE = NormFile('_debugger_case18.py') + TEST_FILE = _get_debugger_test_file('_debugger_case18.py') def run(self): self.StartSocket() @@ -368,7 +405,7 @@ class WriterThreadCase18(AbstractWriterThread): #====================================================================================================================== class WriterThreadCase17(AbstractWriterThread): - TEST_FILE = NormFile('_debugger_case17.py') + TEST_FILE = _get_debugger_test_file('_debugger_case17.py') def run(self): self.StartSocket() @@ -396,7 +433,7 @@ class WriterThreadCase17(AbstractWriterThread): #====================================================================================================================== class WriterThreadCase16(AbstractWriterThread): - TEST_FILE = NormFile('_debugger_case16.py') + TEST_FILE = _get_debugger_test_file('_debugger_case16.py') def run(self): self.StartSocket() @@ -416,34 +453,48 @@ class WriterThreadCase16(AbstractWriterThread): # For each variable, check each of the resolved (meta data) attributes... self.WriteGetVariable(threadId, frameId, 'smallarray') - self.WaitForVar('') - self.WaitForVar('') - self.WaitForVar('') - self.WaitForVar('') - self.WaitForVar('') + self.WaitForVar('') + self.WaitForVar('') #TODO: When on a 32 bit python we get an int32 (which makes this test fail). - self.WaitForVar('') - self.WaitForVar('') - self.WaitForVar('') - self.WaitForVar('') + self.WaitForVar([ + '', + '', + '', + ]) + self.WaitForVar([ + '', + '', + '') + self.WaitForVar('') - self.WaitForVar('') - self.WaitForVar('') - self.WaitForVar('') - self.WaitForVar('') + self.WaitForVar([ + '', + '', + ]) + self.WaitForVar([ + '', + '', + ]) + self.WaitForVar('') + self.WaitForVar('False', '%27Black%27']) + self.WaitForVar(['False', '%27Black%27']) assert 7 == self._sequence, 'Expected 9. Had: %s' % self._sequence # Change some variable self.WriteDebugConsoleExpression("%s\t%s\tEVALUATE\tcarObj.color='Red'" % (threadId, frameId)) self.WriteDebugConsoleExpression("%s\t%s\tEVALUATE\tcarObj.color" % (threadId, frameId)) - self.WaitForMultipleVars(['False', '%27Red%27']) + self.WaitForVar(['False', '%27Red%27']) assert 11 == self._sequence, 'Expected 13. Had: %s' % self._sequence # Iterate some loop self.WriteDebugConsoleExpression("%s\t%s\tEVALUATE\tfor i in range(3):" % (threadId, frameId)) - self.WaitForVars('True') - self.WriteDebugConsoleExpression("%s\t%s\tEVALUATE\t print i" % (threadId, frameId)) + self.WaitForVar(['True', '1']) + self.WriteDebugConsoleExpression("%s\t%s\tEVALUATE\t print(i)" % (threadId, frameId)) self.WriteDebugConsoleExpression("%s\t%s\tEVALUATE\t" % (threadId, frameId)) - self.WaitForVars('False') + self.WaitForVar( + [ + 'False', + '0' + ] + ) assert 17 == self._sequence, 'Expected 19. Had: %s' % self._sequence self.WriteRunThread(threadId) @@ -519,12 +577,12 @@ class WriterThreadCase14(AbstractWriterThread): #====================================================================================================================== class WriterThreadCase13(AbstractWriterThread): - TEST_FILE = NormFile('_debugger_case13.py') + TEST_FILE = _get_debugger_test_file('_debugger_case13.py') def run(self): self.StartSocket() self.WriteAddBreakpoint(35, 'main') - self.Write("124\t%s\t%s" % (self.NextSeq(), "true;false;false;true")) + self.Write("%s\t%s\t%s" % (CMD_SET_PROPERTY_TRACE, self.NextSeq(), "true;false;false;true")) self.WriteMakeInitialRun() threadId, frameId, line = self.WaitForBreakpointHit('111', True) @@ -547,14 +605,14 @@ class WriterThreadCase13(AbstractWriterThread): threadId, frameId, line = self.WaitForBreakpointHit('107', True) # Disable property tracing - self.Write("124\t%s\t%s" % (self.NextSeq(), "true;true;true;true")) + self.Write("%s\t%s\t%s" % (CMD_SET_PROPERTY_TRACE, self.NextSeq(), "true;true;true;true")) self.WriteStepIn(threadId) threadId, frameId, line = self.WaitForBreakpointHit('107', True) # Should Skip step into properties setter assert line == 39, 'Expected return to be in line 39, was: %s' % line # Enable property tracing - self.Write("124\t%s\t%s" % (self.NextSeq(), "true;false;false;true")) + self.Write("%s\t%s\t%s" % (CMD_SET_PROPERTY_TRACE, self.NextSeq(), "true;false;false;true")) self.WriteStepIn(threadId) threadId, frameId, line = self.WaitForBreakpointHit('107', True) # Should go inside getter method @@ -569,11 +627,11 @@ class WriterThreadCase13(AbstractWriterThread): #====================================================================================================================== class WriterThreadCase12(AbstractWriterThread): - TEST_FILE = NormFile('_debugger_case10.py') + TEST_FILE = _get_debugger_test_file('_debugger_case10.py') def run(self): self.StartSocket() - self.WriteAddBreakpoint(2, '') #Should not be hit: setting empty function (not None) should only hit global. + self.WriteAddBreakpoint(2, '') # Should not be hit: setting empty function (not None) should only hit global. self.WriteAddBreakpoint(6, 'Method1a') self.WriteAddBreakpoint(11, 'Method2') self.WriteMakeInitialRun() @@ -584,7 +642,7 @@ class WriterThreadCase12(AbstractWriterThread): self.WriteStepReturn(threadId) - threadId, frameId, line = self.WaitForBreakpointHit('111', True) #not a return (it stopped in the other breakpoint) + threadId, frameId, line = self.WaitForBreakpointHit('111', True) # not a return (it stopped in the other breakpoint) assert line == 6, 'Expected return to be in line 6, was: %s' % line @@ -601,7 +659,7 @@ class WriterThreadCase12(AbstractWriterThread): #====================================================================================================================== class WriterThreadCase11(AbstractWriterThread): - TEST_FILE = NormFile('_debugger_case10.py') + TEST_FILE = _get_debugger_test_file('_debugger_case10.py') def run(self): self.StartSocket() @@ -642,11 +700,11 @@ class WriterThreadCase11(AbstractWriterThread): #====================================================================================================================== class WriterThreadCase10(AbstractWriterThread): - TEST_FILE = NormFile('_debugger_case10.py') + TEST_FILE = _get_debugger_test_file('_debugger_case10.py') def run(self): self.StartSocket() - self.WriteAddBreakpoint(2, 'None') #None or Method should make hit. + self.WriteAddBreakpoint(2, 'None') # None or Method should make hit. self.WriteMakeInitialRun() threadId, frameId = self.WaitForBreakpointHit('111') @@ -676,7 +734,7 @@ class WriterThreadCase10(AbstractWriterThread): #====================================================================================================================== class WriterThreadCase9(AbstractWriterThread): - TEST_FILE = NormFile('_debugger_case89.py') + TEST_FILE = _get_debugger_test_file('_debugger_case89.py') def run(self): self.StartSocket() @@ -709,7 +767,7 @@ class WriterThreadCase9(AbstractWriterThread): #====================================================================================================================== class WriterThreadCase8(AbstractWriterThread): - TEST_FILE = NormFile('_debugger_case89.py') + TEST_FILE = _get_debugger_test_file('_debugger_case89.py') def run(self): self.StartSocket() @@ -738,7 +796,7 @@ class WriterThreadCase8(AbstractWriterThread): #====================================================================================================================== class WriterThreadCase7(AbstractWriterThread): - TEST_FILE = NormFile('_debugger_case7.py') + TEST_FILE = _get_debugger_test_file('_debugger_case7.py') def run(self): self.StartSocket() @@ -749,7 +807,7 @@ class WriterThreadCase7(AbstractWriterThread): self.WriteGetFrame(threadId, frameId) - self.WaitForVars('') #no vars at this point + self.WaitForVars('') # no vars at this point self.WriteStepOver(threadId) @@ -776,7 +834,7 @@ class WriterThreadCase7(AbstractWriterThread): #======================================================================================================================= class WriterThreadCase6(AbstractWriterThread): - TEST_FILE = NormFile('_debugger_case56.py') + TEST_FILE = _get_debugger_test_file('_debugger_case56.py') def run(self): self.StartSocket() @@ -797,7 +855,7 @@ class WriterThreadCase6(AbstractWriterThread): threadId, frameId, line = self.WaitForBreakpointHit('107', True) - #goes to line 4 in jython (function declaration line) + # goes to line 4 in jython (function declaration line) assert line in (4, 5), 'Expecting it to go to line 4 or 5. Went to: %s' % line self.WriteRunThread(threadId) @@ -811,7 +869,7 @@ class WriterThreadCase6(AbstractWriterThread): #======================================================================================================================= class WriterThreadCase5(AbstractWriterThread): - TEST_FILE = NormFile('_debugger_case56.py') + TEST_FILE = _get_debugger_test_file('_debugger_case56.py') def run(self): self.StartSocket() @@ -834,7 +892,7 @@ class WriterThreadCase5(AbstractWriterThread): threadId, frameId, line = self.WaitForBreakpointHit('107', True) - #goes to line 4 in jython (function declaration line) + # goes to line 4 in jython (function declaration line) assert line in (4, 5), 'Expecting it to go to line 4 or 5. Went to: %s' % line self.WriteRunThread(threadId) @@ -849,7 +907,7 @@ class WriterThreadCase5(AbstractWriterThread): #======================================================================================================================= class WriterThreadCase4(AbstractWriterThread): - TEST_FILE = NormFile('_debugger_case4.py') + TEST_FILE = _get_debugger_test_file('_debugger_case4.py') def run(self): self.StartSocket() @@ -859,7 +917,7 @@ class WriterThreadCase4(AbstractWriterThread): self.WriteSuspendThread(threadId) - time.sleep(4) #wait for time enough for the test to finish if it wasn't suspended + time.sleep(4) # wait for time enough for the test to finish if it wasn't suspended self.WriteRunThread(threadId) @@ -871,14 +929,14 @@ class WriterThreadCase4(AbstractWriterThread): #======================================================================================================================= class WriterThreadCase3(AbstractWriterThread): - TEST_FILE = NormFile('_debugger_case3.py') + TEST_FILE = _get_debugger_test_file('_debugger_case3.py') def run(self): self.StartSocket() self.WriteMakeInitialRun() - time.sleep(1) + time.sleep(.5) breakpoint_id = self.WriteAddBreakpoint(4, '') - self.WriteAddBreakpoint(5, 'FuncNotAvailable') #Check that it doesn't get hit in the global when a function is available + self.WriteAddBreakpoint(5, 'FuncNotAvailable') # Check that it doesn't get hit in the global when a function is available threadId, frameId = self.WaitForBreakpointHit() @@ -903,11 +961,11 @@ class WriterThreadCase3(AbstractWriterThread): #======================================================================================================================= class WriterThreadCase2(AbstractWriterThread): - TEST_FILE = NormFile('_debugger_case2.py') + TEST_FILE = _get_debugger_test_file('_debugger_case2.py') def run(self): self.StartSocket() - self.WriteAddBreakpoint(3, 'Call4') #seq = 3 + self.WriteAddBreakpoint(3, 'Call4') # seq = 3 self.WriteMakeInitialRun() threadId, frameId = self.WaitForBreakpointHit() @@ -924,8 +982,79 @@ class WriterThreadCase2(AbstractWriterThread): self.WriteRunThread(threadId) + self.log.append('Checking sequence. Found: %s' % (self._sequence)) assert 15 == self._sequence, 'Expected 15. Had: %s' % self._sequence + self.log.append('Marking finished ok.') + self.finishedOk = True + +#======================================================================================================================= +# WriterThreadCaseQThread1 +#======================================================================================================================= +class WriterThreadCaseQThread1(AbstractWriterThread): + + TEST_FILE = _get_debugger_test_file('_debugger_case_qthread1.py') + + def run(self): + self.StartSocket() + breakpoint_id = self.WriteAddBreakpoint(16, 'run') + self.WriteMakeInitialRun() + + threadId, frameId = self.WaitForBreakpointHit() + + self.WriteRemoveBreakpoint(breakpoint_id) + self.WriteRunThread(threadId) + + self.log.append('Checking sequence. Found: %s' % (self._sequence)) + assert 9 == self._sequence, 'Expected 9. Had: %s' % self._sequence + + self.log.append('Marking finished ok.') + self.finishedOk = True + +#======================================================================================================================= +# WriterThreadCaseQThread2 +#======================================================================================================================= +class WriterThreadCaseQThread2(AbstractWriterThread): + + TEST_FILE = _get_debugger_test_file('_debugger_case_qthread2.py') + + def run(self): + self.StartSocket() + breakpoint_id = self.WriteAddBreakpoint(18, 'longRunning') + self.WriteMakeInitialRun() + + threadId, frameId = self.WaitForBreakpointHit() + + self.WriteRemoveBreakpoint(breakpoint_id) + self.WriteRunThread(threadId) + + self.log.append('Checking sequence. Found: %s' % (self._sequence)) + assert 9 == self._sequence, 'Expected 9. Had: %s' % self._sequence + + self.log.append('Marking finished ok.') + self.finishedOk = True + +#======================================================================================================================= +# WriterThreadCaseQThread3 +#======================================================================================================================= +class WriterThreadCaseQThread3(AbstractWriterThread): + + TEST_FILE = _get_debugger_test_file('_debugger_case_qthread3.py') + + def run(self): + self.StartSocket() + breakpoint_id = self.WriteAddBreakpoint(19, 'run') + self.WriteMakeInitialRun() + + threadId, frameId = self.WaitForBreakpointHit() + + self.WriteRemoveBreakpoint(breakpoint_id) + self.WriteRunThread(threadId) + + self.log.append('Checking sequence. Found: %s' % (self._sequence)) + assert 9 == self._sequence, 'Expected 9. Had: %s' % self._sequence + + self.log.append('Marking finished ok.') self.finishedOk = True #======================================================================================================================= @@ -933,24 +1062,39 @@ class WriterThreadCase2(AbstractWriterThread): #======================================================================================================================= class WriterThreadCase1(AbstractWriterThread): - TEST_FILE = NormFile('_debugger_case1.py') + TEST_FILE = _get_debugger_test_file('_debugger_case1.py') def run(self): self.StartSocket() + + self.log.append('writing add breakpoint') self.WriteAddBreakpoint(6, 'SetUp') + + self.log.append('making initial run') self.WriteMakeInitialRun() + self.log.append('waiting for breakpoint hit') threadId, frameId = self.WaitForBreakpointHit() + self.log.append('get frame') self.WriteGetFrame(threadId, frameId) + self.log.append('step over') self.WriteStepOver(threadId) + self.log.append('get frame') self.WriteGetFrame(threadId, frameId) + self.log.append('run thread') self.WriteRunThread(threadId) - assert 13 == self._sequence, 'Expected 13. Had: %s' % self._sequence + self.log.append('asserting') + try: + assert 13 == self._sequence, 'Expected 13. Had: %s' % self._sequence + except: + self.log.append('assert failed!') + raise + self.log.append('asserted') self.finishedOk = True @@ -966,6 +1110,7 @@ class DebuggerBase(object): UpdatePort() writerThread = writerThreadClass() writerThread.start() + time.sleep(1) localhost = pydev_localhost.get_localhost() args = self.getCommandLine() @@ -981,59 +1126,74 @@ class DebuggerBase(object): ] if SHOW_OTHER_DEBUG_INFO: - print 'executing', ' '.join(args) + print('executing', ' '.join(args)) process = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=os.path.dirname(PYDEVD_FILE)) - class ProcessReadThread(threading.Thread): - def run(self): - self.resultStr = None - self.resultStr = process.stdout.read() - process.stdout.close() - def DoKill(self): - process.stdout.close() + stdout = [] + stderr = [] - processReadThread = ProcessReadThread() - processReadThread.setDaemon(True) - processReadThread.start() + def read(stream, buffer): + for line in stream.readlines(): + if IS_PY3K: + line = line.decode('utf-8') + + if SHOW_STDOUT: + print(line) + buffer.append(line) + + start_new_thread(read, (process.stdout, stdout)) + + if SHOW_OTHER_DEBUG_INFO: - print 'Both processes started' + print('Both processes started') - #polls can fail (because the process may finish and the thread still not -- so, we give it some more chances to - #finish successfully). - pools_failed = 0 - while writerThread.isAlive(): + # polls can fail (because the process may finish and the thread still not -- so, we give it some more chances to + # finish successfully). + check = 0 + while True: if process.poll() is not None: - pools_failed += 1 - time.sleep(.2) - if pools_failed == 10: break - - if process.poll() is None: - for i in range(10): - if processReadThread.resultStr is None: - time.sleep(.5) - else: - break else: - writerThread.DoKill() - - else: - if process.poll() < 0: - self.fail("The other process exited with error code: " + str(process.poll()) + " result:" + processReadThread.resultStr) + if not writerThread.isAlive(): + check += 1 + if check == 20: + print('Warning: writer thread exited and process still did not.') + if check == 100: + self.fail_with_message( + "The other process should've exited but still didn't (timeout for process to exit).", + stdout, stderr, writerThread + ) + time.sleep(.2) + + + poll = process.poll() + if poll < 0: + self.fail_with_message( + "The other process exited with error code: " + str(poll), stdout, stderr, writerThread) - if SHOW_RESULT_STR: - print processReadThread.resultStr + if stdout is None: + self.fail_with_message( + "The other process may still be running -- and didn't give any output.", stdout, stderr, writerThread) - if processReadThread.resultStr is None: - self.fail("The other process may still be running -- and didn't give any output") - - if 'TEST SUCEEDED' not in processReadThread.resultStr: - self.fail(processReadThread.resultStr) + if 'TEST SUCEEDED' not in ''.join(stdout): + self.fail_with_message("TEST SUCEEDED not found in stdout.", stdout, stderr, writerThread) + for i in xrange(100): + if not writerThread.finishedOk: + time.sleep(.1) + if not writerThread.finishedOk: - self.fail("The thread that was doing the tests didn't finish successfully. Output: %s" % processReadThread.resultStr) + self.fail_with_message( + "The thread that was doing the tests didn't finish successfully.", stdout, stderr, writerThread) + + def fail_with_message(self, msg, stdout, stderr, writerThread): + self.fail(msg+ + "\nStdout: \n"+'\n'.join(stdout)+ + "\nStderr:"+'\n'.join(stderr)+ + "\nLog:\n"+'\n'.join(getattr(writerThread, 'log', []))) + def testCase1(self): self.CheckCase(WriterThreadCase1) @@ -1091,6 +1251,30 @@ class DebuggerBase(object): def testCase19(self): self.CheckCase(WriterThreadCase19) + + def _has_qt(self): + try: + from PySide import QtCore + return True + except: + try: + from PyQt4 import QtCore + return True + except: + pass + return False + + def testCaseQthread1(self): + if self._has_qt(): + self.CheckCase(WriterThreadCaseQThread1) + + def testCaseQthread2(self): + if self._has_qt(): + self.CheckCase(WriterThreadCaseQThread2) + + def testCaseQthread3(self): + if self._has_qt(): + self.CheckCase(WriterThreadCaseQThread3) class TestPython(unittest.TestCase, DebuggerBase): @@ -1106,20 +1290,20 @@ class TestJython(unittest.TestCase, DebuggerBase): 'org.python.util.jython' ] - #This case requires decorators to work (which are not present on Jython 2.1), so, this test is just removed from the jython run. + # This case requires decorators to work (which are not present on Jython 2.1), so, this test is just removed from the jython run. def testCase13(self): self.skipTest("Unsupported Decorators") - def testCase16(self): - self.skipTest("Unsupported numpy") - - #This case requires decorators to work (which are not present on Jython 2.1), so, this test is just removed from the jython run. + # This case requires decorators to work (which are not present on Jython 2.1), so, this test is just removed from the jython run. def testCase17(self): self.skipTest("Unsupported Decorators") def testCase18(self): self.skipTest("Unsupported assign to local") + def testCase16(self): + self.skipTest("Unsupported numpy") + class TestIronPython(unittest.TestCase, DebuggerBase): def getCommandLine(self): return [ @@ -1127,8 +1311,22 @@ class TestIronPython(unittest.TestCase, DebuggerBase): '-X:Frames' ] + def testCase3(self): + self.skipTest("Timing issues") # This test fails once in a while due to timing issues on IronPython, so, skipping it. + + def testCase7(self): + # This test checks that we start without variables and at each step a new var is created, but on ironpython, + # the variables exist all at once (with None values), so, we can't test it properly. + self.skipTest("Different behavior on IronPython") + + def testCase13(self): + self.skipTest("Unsupported Decorators") # Not sure why it doesn't work on IronPython, but it's not so common, so, leave it be. + def testCase16(self): self.skipTest("Unsupported numpy") + + def testCase18(self): + self.skipTest("Unsupported assign to local") def GetLocationFromLine(line): @@ -1150,44 +1348,90 @@ def SplitLine(line): + import platform sysname = platform.system().lower() test_dependent = os.path.join('../../../', 'org.python.pydev.core', 'tests', 'org', 'python', 'pydev', 'core', 'TestDependent.' + sysname + '.properties') -f = open(test_dependent) -try: - for line in f.readlines(): - var, loc = SplitLine(line) - if 'PYTHON_EXE' == var: - PYTHON_EXE = loc - if 'IRONPYTHON_EXE' == var: - IRONPYTHON_EXE = loc - - if 'JYTHON_JAR_LOCATION' == var: - JYTHON_JAR_LOCATION = loc - - if 'JAVA_LOCATION' == var: - JAVA_LOCATION = loc -finally: - f.close() - -assert PYTHON_EXE, 'PYTHON_EXE not found in %s' % (test_dependent,) -assert IRONPYTHON_EXE, 'IRONPYTHON_EXE not found in %s' % (test_dependent,) -assert JYTHON_JAR_LOCATION, 'JYTHON_JAR_LOCATION not found in %s' % (test_dependent,) -assert JAVA_LOCATION, 'JAVA_LOCATION not found in %s' % (test_dependent,) -assert os.path.exists(PYTHON_EXE), 'The location: %s is not valid' % (PYTHON_EXE,) -assert os.path.exists(IRONPYTHON_EXE), 'The location: %s is not valid' % (IRONPYTHON_EXE,) -assert os.path.exists(JYTHON_JAR_LOCATION), 'The location: %s is not valid' % (JYTHON_JAR_LOCATION,) -assert os.path.exists(JAVA_LOCATION), 'The location: %s is not valid' % (JAVA_LOCATION,) - -if False: - suite = unittest.TestSuite() - suite.addTest(TestPython('testCase19')) - #suite.addTest(Test('testCase10a')) - unittest.TextTestRunner(verbosity=3).run(suite) +if os.path.exists(test_dependent): + f = open(test_dependent) + try: + for line in f.readlines(): + var, loc = SplitLine(line) + if 'PYTHON_EXE' == var: + PYTHON_EXE = loc -# suite = unittest.makeSuite(TestPython) -# unittest.TextTestRunner(verbosity=3).run(suite) -# -# suite = unittest.makeSuite(TestJython) -# unittest.TextTestRunner(verbosity=3).run(suite) + if 'IRONPYTHON_EXE' == var: + IRONPYTHON_EXE = loc + + if 'JYTHON_JAR_LOCATION' == var: + JYTHON_JAR_LOCATION = loc + + if 'JAVA_LOCATION' == var: + JAVA_LOCATION = loc + finally: + f.close() +else: + pass + +if IRONPYTHON_EXE is None: + sys.stderr.write('Warning: not running IronPython tests.\n') + class TestIronPython(unittest.TestCase): + pass + +if JAVA_LOCATION is None: + sys.stderr.write('Warning: not running Jython tests.\n') + class TestJython(unittest.TestCase): + pass + +# if PYTHON_EXE is None: +PYTHON_EXE = sys.executable + + +if __name__ == '__main__': + if False: + assert PYTHON_EXE, 'PYTHON_EXE not found in %s' % (test_dependent,) + assert IRONPYTHON_EXE, 'IRONPYTHON_EXE not found in %s' % (test_dependent,) + assert JYTHON_JAR_LOCATION, 'JYTHON_JAR_LOCATION not found in %s' % (test_dependent,) + assert JAVA_LOCATION, 'JAVA_LOCATION not found in %s' % (test_dependent,) + assert os.path.exists(PYTHON_EXE), 'The location: %s is not valid' % (PYTHON_EXE,) + assert os.path.exists(IRONPYTHON_EXE), 'The location: %s is not valid' % (IRONPYTHON_EXE,) + assert os.path.exists(JYTHON_JAR_LOCATION), 'The location: %s is not valid' % (JYTHON_JAR_LOCATION,) + assert os.path.exists(JAVA_LOCATION), 'The location: %s is not valid' % (JAVA_LOCATION,) + + if True: + #try: + # os.remove(r'X:\pydev\plugins\org.python.pydev\pysrc\pydevd.pyc') + #except: + # pass + suite = unittest.TestSuite() + +# suite.addTests(unittest.makeSuite(TestJython)) # Note: Jython should be 2.2.1 +# +# suite.addTests(unittest.makeSuite(TestIronPython)) +# +# suite.addTests(unittest.makeSuite(TestPython)) + + + + +# suite.addTest(TestIronPython('testCase18')) +# suite.addTest(TestIronPython('testCase17')) +# suite.addTest(TestIronPython('testCase3')) +# suite.addTest(TestIronPython('testCase7')) +# + suite.addTest(TestPython('testCaseQthread1')) + suite.addTest(TestPython('testCaseQthread2')) + suite.addTest(TestPython('testCaseQthread3')) + +# suite.addTest(TestPython('testCase4')) + + +# suite.addTest(TestJython('testCase1')) +# suite.addTest(TestPython('testCase2')) +# unittest.TextTestRunner(verbosity=3).run(suite) + # suite.addTest(TestPython('testCase17')) + # suite.addTest(TestPython('testCase18')) + # suite.addTest(TestPython('testCase19')) + + unittest.TextTestRunner(verbosity=3).run(suite) diff --git a/python/helpers/pydev/tests_python/test_pydev_monkey.py b/python/helpers/pydev/tests_python/test_pydev_monkey.py index 022efcbf39b1..be1312a066d3 100644 --- a/python/helpers/pydev/tests_python/test_pydev_monkey.py +++ b/python/helpers/pydev/tests_python/test_pydev_monkey.py @@ -1,16 +1,32 @@ import unittest import pydev_monkey import sys +from pydevd import SetupHolder +from pydev_monkey import pydev_src_dir + class TestCase(unittest.TestCase): def test_monkey(self): - check='''C:\\bin\\python.exe -u -c " + original = SetupHolder.setup + + try: + SetupHolder.setup = {'client':'127.0.0.1', 'port': '0'} + check='''C:\\bin\\python.exe -u -c " connect(\\"127.0.0.1\\") "''' - sys.original_argv = [] - self.assertEqual('"-u" "-c" "\nconnect(\\"127.0.0.1\\")\n"', pydev_monkey.patch_arg_str_win(check)) + sys.original_argv = [] + self.assertEqual( + '"C:\\bin\\python.exe" "-u" "-c" "import sys; ' + 'sys.path.append(r\'%s\'); ' + 'import pydevd; pydevd.settrace(host=\'127.0.0.1\', port=0, suspend=False, ' + 'trace_only_current_thread=False, patch_multiprocessing=True); ' + '\nconnect(\\"127.0.0.1\\")\n"' % pydev_src_dir, + pydev_monkey.patch_arg_str_win(check) + ) + finally: + SetupHolder.setup = original def test_str_to_args_windows(self): diff --git a/python/helpers/pydev/tests_python/test_save_locals.py b/python/helpers/pydev/tests_python/test_save_locals.py index fe65d4d438d1..a3beb567d305 100644 --- a/python/helpers/pydev/tests_python/test_save_locals.py +++ b/python/helpers/pydev/tests_python/test_save_locals.py @@ -16,7 +16,7 @@ def use_save_locals(name, value): save_locals(frame) -def test_method(fn): +def check_method(fn): """ A harness for testing methods that attempt to modify the values of locals on the stack. """ @@ -36,7 +36,7 @@ class TestSetLocals(unittest.TestCase): def test_set_locals_using_save_locals(self): - x = test_method(use_save_locals) + x = check_method(use_save_locals) self.assertEqual(x, 2) # Expected to succeed @@ -65,7 +65,7 @@ class TestSetLocals(unittest.TestCase): def check_co_vars(a): frame = sys._getframe() def function2(): - print a + print(a) assert 'a' in frame.f_code.co_cellvars frame = sys._getframe() diff --git a/python/helpers/pydev/tests_runfiles/test_runfiles.py b/python/helpers/pydev/tests_runfiles/test_runfiles.py index 7ca6efe9e7fc..fb34c40c80a7 100644 --- a/python/helpers/pydev/tests_runfiles/test_runfiles.py +++ b/python/helpers/pydev/tests_runfiles/test_runfiles.py @@ -192,7 +192,7 @@ class RunfilesTest(unittest.TestCase): files_with_tests = [1 for t in self.all_tests if len(t._tests) > 0] self.assertNotEquals(len(self.files), len(files_with_tests)) - def count_tests(self, tests): + def count_suite(self, tests=None): total = 0 for t in tests: total += t.countTestCases() @@ -208,60 +208,60 @@ class RunfilesTest(unittest.TestCase): def test_finding_tests_from_modules_with_bad_filter_returns_0_tests(self): self._setup_scenario(self.file_dir, ["NO_TESTS_ARE_SURE_TO_HAVE_THIS_NAME"]) - self.assertEquals(0, self.count_tests(self.all_tests)) + self.assertEquals(0, self.count_suite(self.all_tests)) def test_finding_test_with_unique_name_returns_1_test(self): self._setup_scenario(self.file_dir, include_tests=["test_i_am_a_unique_test_name"]) filtered_tests = self.MyTestRunner.filter_tests(self.all_tests) - self.assertEquals(1, self.count_tests(filtered_tests)) + self.assertEquals(1, self.count_suite(filtered_tests)) def test_finding_test_with_non_unique_name(self): self._setup_scenario(self.file_dir, include_tests=["test_non_unique_name"]) filtered_tests = self.MyTestRunner.filter_tests(self.all_tests) - self.assertEquals(1, self.count_tests(filtered_tests) > 2) + self.assertEquals(1, self.count_suite(filtered_tests) > 2) def test_finding_tests_with_regex_filters(self): self._setup_scenario(self.file_dir, include_tests=["test_non*"]) filtered_tests = self.MyTestRunner.filter_tests(self.all_tests) - self.assertEquals(1, self.count_tests(filtered_tests) > 2) + self.assertEquals(1, self.count_suite(filtered_tests) > 2) self._setup_scenario(self.file_dir, ["^$"]) filtered_tests = self.MyTestRunner.filter_tests(self.all_tests) - self.assertEquals(0, self.count_tests(filtered_tests)) + self.assertEquals(0, self.count_suite(filtered_tests)) self._setup_scenario(self.file_dir, None, exclude_tests=["*"]) filtered_tests = self.MyTestRunner.filter_tests(self.all_tests) - self.assertEquals(0, self.count_tests(filtered_tests)) + self.assertEquals(0, self.count_suite(filtered_tests)) def test_matching_tests(self): self._setup_scenario(self.file_dir, None, ['StillYetAnotherSampleTest']) filtered_tests = self.MyTestRunner.filter_tests(self.all_tests) - self.assertEqual(1, self.count_tests(filtered_tests)) + self.assertEqual(1, self.count_suite(filtered_tests)) self._setup_scenario(self.file_dir, None, ['SampleTest.test_xxxxxx1']) filtered_tests = self.MyTestRunner.filter_tests(self.all_tests) - self.assertEqual(1, self.count_tests(filtered_tests)) + self.assertEqual(1, self.count_suite(filtered_tests)) self._setup_scenario(self.file_dir, None, ['SampleTest']) filtered_tests = self.MyTestRunner.filter_tests(self.all_tests) - self.assertEqual(8, self.count_tests(filtered_tests)) + self.assertEqual(8, self.count_suite(filtered_tests)) self._setup_scenario(self.file_dir, None, ['AnotherSampleTest.todo_not_tested']) filtered_tests = self.MyTestRunner.filter_tests(self.all_tests) - self.assertEqual(1, self.count_tests(filtered_tests)) + self.assertEqual(1, self.count_suite(filtered_tests)) self._setup_scenario(self.file_dir, None, ['StillYetAnotherSampleTest', 'SampleTest.test_xxxxxx1']) filtered_tests = self.MyTestRunner.filter_tests(self.all_tests) - self.assertEqual(2, self.count_tests(filtered_tests)) + self.assertEqual(2, self.count_suite(filtered_tests)) self._setup_scenario(self.file_dir, None, exclude_tests=['*']) filtered_tests = self.MyTestRunner.filter_tests(self.all_tests) - self.assertEqual(self.count_tests(filtered_tests), 0) + self.assertEqual(self.count_suite(filtered_tests), 0) self._setup_scenario(self.file_dir, None, exclude_tests=['*a*']) filtered_tests = self.MyTestRunner.filter_tests(self.all_tests) - self.assertEqual(self.count_tests(filtered_tests), 6) + self.assertEqual(self.count_suite(filtered_tests), 6) self.assertEqual( set(self.MyTestRunner.list_test_names(filtered_tests)), @@ -270,7 +270,7 @@ class RunfilesTest(unittest.TestCase): self._setup_scenario(self.file_dir, None, exclude_tests=['*a*', '*x*']) filtered_tests = self.MyTestRunner.filter_tests(self.all_tests) - self.assertEqual(self.count_tests(filtered_tests), 2) + self.assertEqual(self.count_suite(filtered_tests), 2) self.assertEqual( set(self.MyTestRunner.list_test_names(filtered_tests)), @@ -363,17 +363,43 @@ class RunfilesTest(unittest.TestCase): ('notifyTest', 'ok', '', '', simple_test, 'SampleTest.test_xxxxxx2'), ('notifyTest', 'ok', '', '', simple_test2, 'YetAnotherSampleTest.test_abc'), ] + if not IS_JYTHON: - expected.append(('notifyTest', 'error', '', 'ValueError: This is an INTENTIONAL value error in setUpClass.', - simpleClass_test.replace('/', os.path.sep), 'samples.simpleClass_test.SetUpClassTest ')) - expected.append(('notifyTest', 'error', '', 'ValueError: This is an INTENTIONAL value error in setUpModule.', - simpleModule_test.replace('/', os.path.sep), 'samples.simpleModule_test ')) + if 'samples.simpleClass_test' in str(notifications): + expected.append(('notifyTest', 'error', '', 'ValueError: This is an INTENTIONAL value error in setUpClass.', + simpleClass_test.replace('/', os.path.sep), 'samples.simpleClass_test.SetUpClassTest ')) + expected.append(('notifyTest', 'error', '', 'ValueError: This is an INTENTIONAL value error in setUpModule.', + simpleModule_test.replace('/', os.path.sep), 'samples.simpleModule_test ')) + else: + expected.append(('notifyTest', 'error', '', 'ValueError: This is an INTENTIONAL value error in setUpClass.', + simpleClass_test.replace('/', os.path.sep), 'simpleClass_test.SetUpClassTest ')) + expected.append(('notifyTest', 'error', '', 'ValueError: This is an INTENTIONAL value error in setUpModule.', + simpleModule_test.replace('/', os.path.sep), 'simpleModule_test ')) else: expected.append(('notifyTest', 'ok', '', '', simpleClass_test, 'SetUpClassTest.test_blank')) expected.append(('notifyTest', 'ok', '', '', simpleModule_test, 'SetUpModuleTest.test_blank')) expected.append(('notifyTestRunFinished',)) expected.sort() + new_notifications = [] + for notification in expected: + try: + if len(notification) == 6: + # Some are binary on Py3. + new_notifications.append(( + notification[0], + notification[1], + notification[2].encode('latin1'), + notification[3].encode('latin1'), + notification[4], + notification[5], + )) + else: + new_notifications.append(notification) + except: + raise + expected = new_notifications + notifications.sort() self.assertEqual( expected,