diff options
Diffstat (limited to 'python/helpers/pydev')
26 files changed, 2892 insertions, 552 deletions
diff --git a/python/helpers/pydev/django_debug.py b/python/helpers/pydev/django_debug.py deleted file mode 100644 index 2b17864db47e..000000000000 --- a/python/helpers/pydev/django_debug.py +++ /dev/null @@ -1,124 +0,0 @@ -import inspect -from django_frame import DjangoTemplateFrame -from pydevd_comm import CMD_SET_BREAK -from pydevd_constants import DJANGO_SUSPEND, GetThreadId, DictContains -from pydevd_file_utils import NormFileToServer -from pydevd_breakpoints import LineBreakpoint -import pydevd_vars -import traceback - -class DjangoLineBreakpoint(LineBreakpoint): - - def __init__(self, file, line, condition, func_name, expression): - self.file = file - LineBreakpoint.__init__(self, line, condition, func_name, expression) - - def is_triggered(self, template_frame_file, template_frame_line): - return self.file == template_frame_file and self.line == template_frame_line - - def __str__(self): - return "DjangoLineBreakpoint: %s-%d" %(self.file, self.line) - - -def inherits(cls, *names): - if cls.__name__ in names: - return True - inherits_node = False - for base in inspect.getmro(cls): - if base.__name__ in names: - inherits_node = True - break - return inherits_node - - -def is_django_render_call(frame): - try: - name = frame.f_code.co_name - if name != 'render': - return False - - if not DictContains(frame.f_locals, 'self'): - return False - - cls = frame.f_locals['self'].__class__ - - inherits_node = inherits(cls, 'Node') - - if not inherits_node: - return False - - clsname = cls.__name__ - return clsname != 'TextNode' and clsname != 'NodeList' - except: - traceback.print_exc() - return False - - -def is_django_context_get_call(frame): - try: - if not DictContains(frame.f_locals, 'self'): - return False - - cls = frame.f_locals['self'].__class__ - - return inherits(cls, 'BaseContext') - except: - traceback.print_exc() - return False - - -def is_django_resolve_call(frame): - try: - name = frame.f_code.co_name - if name != '_resolve_lookup': - return False - - if not DictContains(frame.f_locals, 'self'): - return False - - cls = frame.f_locals['self'].__class__ - - clsname = cls.__name__ - return clsname == 'Variable' - except: - traceback.print_exc() - return False - - -def is_django_suspended(thread): - return thread.additionalInfo.suspend_type == DJANGO_SUSPEND - - -def suspend_django(py_db_frame, mainDebugger, thread, frame, cmd=CMD_SET_BREAK): - frame = DjangoTemplateFrame(frame) - - if frame.f_lineno is None: - return None - - #try: - # if thread.additionalInfo.filename == frame.f_code.co_filename and thread.additionalInfo.line == frame.f_lineno: - # return None # don't stay twice on the same line - #except AttributeError: - # pass - - pydevd_vars.addAdditionalFrameById(GetThreadId(thread), {id(frame): frame}) - - py_db_frame.setSuspend(thread, cmd) - thread.additionalInfo.suspend_type = DJANGO_SUSPEND - - thread.additionalInfo.filename = frame.f_code.co_filename - thread.additionalInfo.line = frame.f_lineno - - return frame - - -def find_django_render_frame(frame): - while frame is not None and not is_django_render_call(frame): - frame = frame.f_back - - return frame - - - - - diff --git a/python/helpers/pydev/django_frame.py b/python/helpers/pydev/django_frame.py deleted file mode 100644 index 4181572aed3c..000000000000 --- a/python/helpers/pydev/django_frame.py +++ /dev/null @@ -1,132 +0,0 @@ -from pydevd_file_utils import GetFileNameAndBaseFromFile -import pydev_log -import traceback -from pydevd_constants import DictContains - -def read_file(filename): - f = open(filename, "r") - try: - s = f.read() - finally: - f.close() - return s - - -def offset_to_line_number(text, offset): - curLine = 1 - curOffset = 0 - while curOffset < offset: - if curOffset == len(text): - return -1 - c = text[curOffset] - if c == '\n': - curLine += 1 - elif c == '\r': - curLine += 1 - if curOffset < len(text) and text[curOffset + 1] == '\n': - curOffset += 1 - - curOffset += 1 - - return curLine - - -def get_source(frame): - try: - node = frame.f_locals['self'] - if hasattr(node, 'source'): - return node.source - else: - pydev_log.error_once( - "WARNING: Template path is not available. Please set TEMPLATE_DEBUG=True " - "in your settings.py to make django template breakpoints working") - return None - - except: - pydev_log.debug(traceback.format_exc()) - return None - - -def get_template_file_name(frame): - try: - source = get_source(frame) - if source is None: - pydev_log.debug("Source is None\n") - return None - fname = source[0].name - - if fname == '<unknown source>': - pydev_log.debug("Source name is %s\n" % fname) - return None - else: - filename, base = GetFileNameAndBaseFromFile(fname) - return filename - except: - pydev_log.debug(traceback.format_exc()) - return None - - -def get_template_line(frame, template_frame_file): - source = get_source(frame) - try: - return offset_to_line_number(read_file(template_frame_file), source[1][0]) - except: - return None - - -class DjangoTemplateFrame: - 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 is_django_exception_break_context(frame): - try: - return frame.f_code.co_name in ['_resolve_lookup', 'find_template'] - except: - return False - - -def just_raised(trace): - if trace is None: - return False - return trace.tb_next is None - diff --git a/python/helpers/pydev/pydev_log.py b/python/helpers/pydev/pydev_log.py index 229784b76a91..b5e65b3102e6 100644 --- a/python/helpers/pydev/pydev_log.py +++ b/python/helpers/pydev/pydev_log.py @@ -2,6 +2,8 @@ import sys from pydevd_constants import DebugInfoHolder from pydevd_constants import DictContains +import traceback + WARN_ONCE_MAP = {} def stderr_write(message): @@ -18,11 +20,16 @@ def warn(message): if DebugInfoHolder.DEBUG_TRACE_LEVEL>1: stderr_write(message) + def info(message): stderr_write(message) -def error(message): + +def error(message, tb=False): stderr_write(message) + if tb: + traceback.print_exc() + def error_once(message): if not DictContains(WARN_ONCE_MAP, message): diff --git a/python/helpers/pydev/pydev_monkey_qt.py b/python/helpers/pydev/pydev_monkey_qt.py index 9c62686173dd..2675e9e55708 100644 --- a/python/helpers/pydev/pydev_monkey_qt.py +++ b/python/helpers/pydev/pydev_monkey_qt.py @@ -11,7 +11,7 @@ def set_trace_in_qt(): _patched_qt = False def patch_qt(): ''' - This method patches qt (PySide or PyQt4) so that we have hooks to set the tracing for QThread. + This method patches qt (PySide, PyQt4, PyQt5) so that we have hooks to set the tracing for QThread. ''' # Avoid patching more than once @@ -27,7 +27,10 @@ def patch_qt(): try: from PyQt4 import QtCore except: - return + try: + from PyQt5 import QtCore + except: + return _original_thread_init = QtCore.QThread.__init__ _original_runnable_init = QtCore.QRunnable.__init__ diff --git a/python/helpers/pydev/pydev_run_in_console.py b/python/helpers/pydev/pydev_run_in_console.py index 1b8e1d230175..731ead67115e 100644 --- a/python/helpers/pydev/pydev_run_in_console.py +++ b/python/helpers/pydev/pydev_run_in_console.py @@ -2,6 +2,7 @@ from pydevconsole import * import pydev_imports +from pydevd_utils import save_main_module def run_file(file, globals=None, locals=None): @@ -11,22 +12,8 @@ def run_file(file, globals=None, locals=None): file = new_target if globals is None: - # patch provided by: Scott Schlesier - when script is run, it does not - # use globals from pydevd: - # This will prevent the pydevd script from contaminating the namespace for the script to be debugged - - # pretend pydevd is not the main module, and - # convince the file to be debugged that it was loaded as main - sys.modules['pydevd'] = sys.modules['__main__'] - sys.modules['pydevd'].__name__ = 'pydevd' - - from imp import new_module - m = new_module('__main__') - sys.modules['__main__'] = m - if hasattr(sys.modules['pydevd'], '__loader__'): - setattr(m, '__loader__', getattr(sys.modules['pydevd'], '__loader__')) - - m.__file__ = file + m = save_main_module(file, 'pydev_run_in_console') + globals = m.__dict__ try: globals['__builtins__'] = __builtins__ diff --git a/python/helpers/pydev/pydevconsole.py b/python/helpers/pydev/pydevconsole.py index 8d4375f5a5aa..444aa2d1c48b 100644 --- a/python/helpers/pydev/pydevconsole.py +++ b/python/helpers/pydev/pydevconsole.py @@ -80,10 +80,18 @@ try: from pydev_imports import execfile __builtin__.execfile = execfile - except: pass +# Pull in runfile, the interface to UMD that wraps execfile +from pydev_umd import runfile, _set_globals_function +try: + import builtins + builtins.runfile = runfile +except: + import __builtin__ + __builtin__.runfile = runfile + #======================================================================================================================= # InterpreterInterface @@ -264,6 +272,9 @@ def start_server(host, port, interpreter): sys.stderr.write('Error starting server with host: %s, port: %s, client_port: %s\n' % (host, port, client_port)) raise + # Tell UMD the proper default namespace + _set_globals_function(interpreter.getNamespace) + server.register_function(interpreter.execLine) server.register_function(interpreter.execMultipleLines) server.register_function(interpreter.getCompletions) diff --git a/python/helpers/pydev/pydevd.py b/python/helpers/pydev/pydevd.py index 9d0da096c07d..8d68cea9876c 100644 --- a/python/helpers/pydev/pydevd.py +++ b/python/helpers/pydev/pydevd.py @@ -3,12 +3,15 @@ from __future__ import nested_scopes # Jython 2.1 support from pydevd_constants import * # @UnusedWildImport import pydev_monkey_qt +from pydevd_utils import save_main_module + pydev_monkey_qt.patch_qt() import traceback -from django_debug import DjangoLineBreakpoint -from pydevd_frame import add_exception_to_frame +from pydevd_plugin_utils import PluginManager + +from pydevd_frame_utils import add_exception_to_frame import pydev_imports from pydevd_breakpoints import * #@UnusedWildImport import fix_getpass @@ -110,13 +113,18 @@ DONT_TRACE = { 'linecache.py':1, 'threading.py':1, + # thirs party libs that we don't want to trace + 'pluginbase.py':1, + 'pkgutil_old.py':1, + 'uuid_old.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, + 'jinja2_debug.py':1, 'pydev_log.py':1, 'pydev_monkey.py':1 , 'pydevd.py':1 , @@ -301,17 +309,15 @@ class PyDB: self._cmd_queue = {} # the hash of Queues. Key is thread id, value is thread self.breakpoints = {} - self.django_breakpoints = {} self.file_to_id_to_line_breakpoint = {} - self.file_to_id_to_django_breakpoint = {} + self.file_to_id_to_plugin_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 = _pydev_thread.allocate_lock() self._lock_running_thread_ids = _pydev_thread.allocate_lock() @@ -344,6 +350,8 @@ class PyDB: # This attribute holds the file-> lines which have an @IgnoreException. self.filename_to_lines_where_exceptions_are_ignored = {} + #working with plugins + self.plugin = PluginManager(self) def haveAliveThreads(self): for t in threadingEnumerate(): @@ -568,12 +576,16 @@ class PyDB: notify_on_terminate, notify_on_first_raise_only, ): - eb = ExceptionBreakpoint( - exception, - notify_always, - notify_on_terminate, - notify_on_first_raise_only, - ) + try: + eb = ExceptionBreakpoint( + exception, + notify_always, + notify_on_terminate, + notify_on_first_raise_only, + ) + except ImportError: + pydev_log.error("Error unable to add break on exception for: %s (exception could not be imported)\n" % (exception,)) + return None if eb.notify_on_terminate: cp = self.break_on_uncaught_exceptions.copy() @@ -839,15 +851,22 @@ class PyDB: if len(expression) <= 0 or expression is None or expression == "None": expression = None + supported_type = False if type == 'python-line': breakpoint = LineBreakpoint(line, condition, func_name, expression) breakpoints = self.breakpoints file_to_id_to_breakpoint = self.file_to_id_to_line_breakpoint - elif type == 'django-line': - breakpoint = DjangoLineBreakpoint(file, line, condition, func_name, expression) - breakpoints = self.django_breakpoints - file_to_id_to_breakpoint = self.file_to_id_to_django_breakpoint + supported_type = True else: + result = self.plugin.add_breakpoint('add_line_breakpoint', self, type, file, line, condition, expression, func_name) + if result is not None: + supported_type = True + breakpoint, breakpoints = result + file_to_id_to_breakpoint = self.file_to_id_to_plugin_breakpoint + else: + supported_type = False + + if not supported_type: raise NameError(type) if DebugInfoHolder.DEBUG_TRACE_BREAKPOINTS > 0: @@ -880,27 +899,31 @@ class PyDB: pydev_log.error('Error removing breakpoint. Expected breakpoint_id to be an int. Found: %s' % (breakpoint_id,)) else: + file_to_id_to_breakpoint = None if breakpoint_type == 'python-line': breakpoints = self.breakpoints file_to_id_to_breakpoint = self.file_to_id_to_line_breakpoint - elif breakpoint_type == 'django-line': - breakpoints = self.django_breakpoints - file_to_id_to_breakpoint = self.file_to_id_to_django_breakpoint else: - raise NameError(breakpoint_type) + result = self.plugin.get_breakpoints(self, breakpoint_type) + if result is not None: + file_to_id_to_breakpoint = self.file_to_id_to_plugin_breakpoint + breakpoints = result - try: - 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' % ( - file, existing.line, existing.func_name.encode('utf-8'), breakpoint_id)) + if file_to_id_to_breakpoint is None: + pydev_log.error('Error removing breakpoint. Cant handle breakpoint of type %s' % breakpoint_type) + else: + try: + 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' % ( + file, existing.line, existing.func_name.encode('utf-8'), breakpoint_id)) - del id_to_pybreakpoint[breakpoint_id] - self.consolidate_breakpoints(file, id_to_pybreakpoint, breakpoints) - except KeyError: - pydev_log.error("Error removing breakpoint: Breakpoint id not found: %s id: %s. Available ids: %s\n" % ( - file, breakpoint_id, DictKeys(id_to_pybreakpoint))) + del id_to_pybreakpoint[breakpoint_id] + self.consolidate_breakpoints(file, id_to_pybreakpoint, breakpoints) + except KeyError: + pydev_log.error("Error removing breakpoint: Breakpoint id not found: %s id: %s. Available ids: %s\n" % ( + file, breakpoint_id, DictKeys(id_to_pybreakpoint))) elif cmd_id == CMD_EVALUATE_EXPRESSION or cmd_id == CMD_EXEC_EXPRESSION: @@ -963,6 +986,8 @@ class PyDB: notify_on_terminate=break_on_uncaught, notify_on_first_raise_only=False, ) + if exception_breakpoint is None: + continue added.append(exception_breakpoint) self.update_after_exceptions_added(added) @@ -1013,28 +1038,58 @@ class PyDB: pass elif cmd_id == CMD_ADD_EXCEPTION_BREAK: - exception, notify_always, notify_on_terminate = text.split('\t', 2) - exception_breakpoint = self.add_break_on_exception( - exception, - notify_always=int(notify_always) > 0, - notify_on_terminate = int(notify_on_terminate) == 1, - notify_on_first_raise_only=int(notify_always) == 2 - ) - self.update_after_exceptions_added([exception_breakpoint]) + if text.find('\t') != -1: + exception, notify_always, notify_on_terminate = text.split('\t', 2) + else: + exception, notify_always, notify_on_terminate = text, 0, 0 + + if exception.find('-') != -1: + type, exception = exception.split('-') + else: + type = 'python' + + if type == 'python': + exception_breakpoint = self.add_break_on_exception( + exception, + notify_always=int(notify_always) > 0, + notify_on_terminate = int(notify_on_terminate) == 1, + notify_on_first_raise_only=int(notify_always) == 2 + ) + + if exception_breakpoint is not None: + self.update_after_exceptions_added([exception_breakpoint]) + else: + supported_type = self.plugin.add_breakpoint('add_exception_breakpoint', self, type, exception) + + if not supported_type: + raise NameError(type) + + elif cmd_id == CMD_REMOVE_EXCEPTION_BREAK: exception = text - try: - cp = self.break_on_uncaught_exceptions.copy() - DictPop(cp, exception, None) - self.break_on_uncaught_exceptions = cp + if exception.find('-') != -1: + type, exception = exception.split('-') + else: + type = 'python' - 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) + if type == 'python': + try: + 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) + else: + supported_type = self.plugin.remove_exception_breakpoint(self, type, exception) + + if not supported_type: + raise NameError(type) elif cmd_id == CMD_LOAD_SOURCE: path = text @@ -1048,16 +1103,13 @@ class PyDB: elif cmd_id == CMD_ADD_DJANGO_EXCEPTION_BREAK: exception = text - self.django_exception_break[exception] = True - self.setTracingForUntracedContexts() + self.plugin.add_breakpoint('add_exception_breakpoint', self, 'django', exception) + elif cmd_id == CMD_REMOVE_DJANGO_EXCEPTION_BREAK: exception = text - try: - del self.django_exception_break[exception] - except : - pass + self.plugin.remove_exception_breakpoint(self, 'django', exception) elif cmd_id == CMD_EVALUATE_CONSOLE_EXPRESSION: # Command which takes care for the debug console communication @@ -1492,22 +1544,7 @@ class PyDB: file = new_target if globals is None: - # patch provided by: Scott Schlesier - when script is run, it does not - # use globals from pydevd: - # This will prevent the pydevd script from contaminating the namespace for the script to be debugged - - # pretend pydevd is not the main module, and - # convince the file to be debugged that it was loaded as main - sys.modules['pydevd'] = sys.modules['__main__'] - sys.modules['pydevd'].__name__ = 'pydevd' - - from imp import new_module - m = new_module('__main__') - sys.modules['__main__'] = m - if hasattr(sys.modules['pydevd'], '__loader__'): - setattr(m, '__loader__', getattr(sys.modules['pydevd'], '__loader__')) - - m.__file__ = file + m = save_main_module(file, 'pydevd') globals = m.__dict__ try: globals['__builtins__'] = __builtins__ @@ -1546,8 +1583,6 @@ class PyDB: pydev_imports.execfile(file, globals, locals) # execute the script - return globals - def exiting(self): sys.stdout.flush() sys.stderr.flush() @@ -2061,10 +2096,6 @@ if __name__ == '__main__': 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__) diff --git a/python/helpers/pydev/pydevd_breakpoints.py b/python/helpers/pydev/pydevd_breakpoints.py index 1171157257e9..693823917a2f 100644 --- a/python/helpers/pydev/pydevd_breakpoints.py +++ b/python/helpers/pydev/pydevd_breakpoints.py @@ -40,8 +40,8 @@ class ExceptionBreakpoint: def __str__(self): return self.qname -class LineBreakpoint: +class LineBreakpoint(object): def __init__(self, line, condition, func_name, expression): self.line = line self.condition = condition diff --git a/python/helpers/pydev/pydevd_constants.py b/python/helpers/pydev/pydevd_constants.py index e878d3b48ead..5e7a7a926bfb 100644 --- a/python/helpers/pydev/pydevd_constants.py +++ b/python/helpers/pydev/pydevd_constants.py @@ -5,7 +5,6 @@ STATE_RUN = 1 STATE_SUSPEND = 2 PYTHON_SUSPEND = 1 -DJANGO_SUSPEND = 2 try: __setFalse = False diff --git a/python/helpers/pydev/pydevd_frame.py b/python/helpers/pydev/pydevd_frame.py index 5d1e78458391..922133ba1512 100644 --- a/python/helpers/pydev/pydevd_frame.py +++ b/python/helpers/pydev/pydevd_frame.py @@ -3,18 +3,15 @@ import os.path import re import traceback # @Reimport -from django_debug import find_django_render_frame -from django_debug import is_django_render_call, is_django_suspended, suspend_django, is_django_resolve_call, is_django_context_get_call -from django_frame import DjangoTemplateFrame -from django_frame import is_django_exception_break_context -from django_frame import just_raised, get_template_file_name, get_template_line import pydev_log from pydevd_breakpoints import get_exception_breakpoint, get_exception_name -from pydevd_comm import CMD_ADD_DJANGO_EXCEPTION_BREAK, \ - CMD_STEP_CAUGHT_EXCEPTION, CMD_STEP_RETURN, CMD_STEP_OVER, CMD_SET_BREAK, \ +from pydevd_comm import CMD_STEP_CAUGHT_EXCEPTION, CMD_STEP_RETURN, CMD_STEP_OVER, CMD_SET_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_frame_utils import add_exception_to_frame, just_raised + try: from pydevd_signature import sendSignatureCallTrace except ImportError: @@ -55,17 +52,6 @@ class PyDBFrame: def doWaitSuspend(self, *args, **kwargs): self._args[0].doWaitSuspend(*args, **kwargs) - def _is_django_render_call(self, frame): - try: - return self._cached_is_django_render_call - except: - # Calculate lazily: note that a PyDBFrame always deals with the same - # frame over and over, so, we can cache this. - # -- although we can't cache things which change over time (such as - # the breakpoints for the file). - ret = self._cached_is_django_render_call = is_django_render_call(frame) - return ret - def trace_exception(self, frame, event, arg): if event == 'exception': flag, frame = self.should_stop_on_exception(frame, event, arg) @@ -97,22 +83,11 @@ class PyDBFrame: flag = False else: try: - if mainDebugger.django_exception_break and get_exception_name(exception) in [ - 'VariableDoesNotExist', 'TemplateDoesNotExist', 'TemplateSyntaxError'] \ - and just_raised(trace) and is_django_exception_break_context(frame): - - render_frame = find_django_render_frame(frame) - if render_frame: - suspend_frame = suspend_django( - self, mainDebugger, thread, render_frame, CMD_ADD_DJANGO_EXCEPTION_BREAK) - - if suspend_frame: - add_exception_to_frame(suspend_frame, (exception, value, trace)) - flag = True - thread.additionalInfo.message = 'VariableDoesNotExist' - suspend_frame.f_back = frame - frame = suspend_frame - except : + result = mainDebugger.plugin.exception_break(mainDebugger, self, frame, self._args, arg) + if result: + (flag, frame) = result + + except: flag = False return flag, frame @@ -253,7 +228,8 @@ class PyDBFrame: sendSignatureCallTrace(main_debugger, frame, filename) is_exception_event = event == 'exception' - has_exception_breakpoints = main_debugger.break_on_caught_exceptions or main_debugger.django_exception_break + has_exception_breakpoints = main_debugger.break_on_caught_exceptions \ + or main_debugger.plugin.has_exception_breaks(main_debugger) if is_exception_event: if has_exception_breakpoints: @@ -293,9 +269,8 @@ class PyDBFrame: can_skip = (step_cmd is None and stop_frame is None)\ or (step_cmd in (CMD_STEP_RETURN, CMD_STEP_OVER) and stop_frame is not frame) - check_stop_on_django_render_call = main_debugger.django_breakpoints and self._is_django_render_call(frame) - if check_stop_on_django_render_call: - can_skip = False + if can_skip: + can_skip = not main_debugger.plugin.can_not_skip(main_debugger, self, frame) # Let's check to see if we are in a function that has a breakpoint. If we don't have a breakpoint, # we will return nothing for the next trace @@ -334,29 +309,35 @@ class PyDBFrame: try: line = frame.f_lineno - - flag = False - if event == 'call' and info.pydev_state != STATE_SUSPEND and check_stop_on_django_render_call: - flag, frame = self.should_stop_on_django_breakpoint(frame, event, arg) - #return is not taken into account for breakpoint hit because we'd have a double-hit in this case #(one for the line and the other for the return). - if not flag and event != 'return' and info.pydev_state != STATE_SUSPEND and breakpoints_for_file is not None\ - and DictContains(breakpoints_for_file, line): - #ok, hit breakpoint, now, we have to discover if it is a conditional breakpoint - # lets do the conditional stuff here + stop_info = {} + breakpoint = None + exist_result = False + stop_info['stop'] = False + if not flag and event != 'return' and info.pydev_state != STATE_SUSPEND and breakpoints_for_file is not None \ + and DictContains(breakpoints_for_file, line): breakpoint = breakpoints_for_file[line] - - stop = True + new_frame = frame + stop_info['stop'] = True if step_cmd == CMD_STEP_OVER and stop_frame is frame and event in ('line', 'return'): - stop = False #we don't stop on breakpoint if we have to stop by step-over (it will be processed later) - else: + stop_info['stop'] = False #we don't stop on breakpoint if we have to stop by step-over (it will be processed later) + else: + result = main_debugger.plugin.get_breakpoint(main_debugger, self, frame, event, self._args) + if result: + exist_result = True + (flag, breakpoint, new_frame) = result + + if breakpoint: + #ok, hit breakpoint, now, we have to discover if it is a conditional breakpoint + # lets do the conditional stuff here + if stop_info['stop'] or exist_result: condition = breakpoint.condition if condition is not None: try: - val = eval(condition, frame.f_globals, frame.f_locals) + val = eval(condition, new_frame.f_globals, new_frame.f_locals) if not val: return self.trace_dispatch @@ -371,7 +352,7 @@ class PyDBFrame: if not main_debugger.suspend_on_breakpoint_exception: return self.trace_dispatch else: - stop = True + stop_info['stop'] = True try: additional_info = None try: @@ -395,18 +376,21 @@ class PyDBFrame: except: traceback.print_exc() - if breakpoint.expression is not None: - try: + if breakpoint.expression is not None: try: - val = eval(breakpoint.expression, frame.f_globals, frame.f_locals) - except: - val = sys.exc_info()[1] - finally: - if val is not None: - thread.additionalInfo.message = val - - if stop: - self.setSuspend(thread, CMD_SET_BREAK) + try: + val = eval(breakpoint.expression, new_frame.f_globals, new_frame.f_locals) + except: + val = sys.exc_info()[1] + finally: + if val is not None: + thread.additionalInfo.message = val + if stop_info['stop']: + self.setSuspend(thread, CMD_SET_BREAK) + elif flag: + result = main_debugger.plugin.suspend(main_debugger, thread, frame) + if result: + frame = result # if thread has a suspend flag, we suspend with a busy wait if info.pydev_state == STATE_SUSPEND: @@ -419,8 +403,6 @@ class PyDBFrame: #step handling. We stop when we hit the right frame try: - django_stop = False - should_skip = False if pydevd_dont_trace.should_trace_hook is not None: if not hasattr(self, 'should_skip'): @@ -432,34 +414,18 @@ class PyDBFrame: should_skip = self.should_skip if should_skip: - stop = False + stop_info['stop'] = False elif step_cmd == CMD_STEP_INTO: - stop = event in ('line', 'return') - - if is_django_suspended(thread): - #django_stop = event == 'call' and is_django_render_call(frame) - stop = stop and is_django_resolve_call(frame.f_back) and not is_django_context_get_call(frame) - if stop: - info.pydev_django_resolve_frame = 1 #we remember that we've go into python code from django rendering frame + stop_info['stop'] = event in ('line', 'return') + main_debugger.plugin.cmd_step_into(main_debugger, frame, event, self._args, stop_info) elif step_cmd == CMD_STEP_OVER: - if is_django_suspended(thread): - django_stop = event == 'call' and self._is_django_render_call(frame) - - stop = False - else: - if event == 'return' and info.pydev_django_resolve_frame is not None and is_django_resolve_call(frame.f_back): - #we return to Django suspend mode and should not stop before django rendering frame - stop_frame = info.pydev_step_stop = info.pydev_django_resolve_frame - info.pydev_django_resolve_frame = None - thread.additionalInfo.suspend_type = DJANGO_SUSPEND - - - stop = stop_frame is frame and event in ('line', 'return') + stop_info['stop'] = stop_frame is frame and event in ('line', 'return') + main_debugger.plugin.cmd_step_over(main_debugger, frame, event, self._args, stop_info) elif step_cmd == CMD_SMART_STEP_INTO: - stop = False + stop_info['stop'] = False if info.pydev_smart_step_stop is frame: info.pydev_func_name = None info.pydev_smart_step_stop = None @@ -472,13 +438,13 @@ class PyDBFrame: curr_func_name = '' if curr_func_name == info.pydev_func_name: - stop = True + stop_info['stop'] = True elif step_cmd == CMD_STEP_RETURN: - stop = event == 'return' and stop_frame is frame + stop_info['stop'] = event == 'return' and stop_frame is frame elif step_cmd == CMD_RUN_TO_LINE or step_cmd == CMD_SET_NEXT_STATEMENT: - stop = False + stop_info['stop'] = False if event == 'line' or event == 'exception': #Yes, we can only act on line events (weird hum?) @@ -493,50 +459,47 @@ class PyDBFrame: if curr_func_name == info.pydev_func_name: line = info.pydev_next_line if frame.f_lineno == line: - stop = True + stop_info['stop'] = True else: if frame.f_trace is None: frame.f_trace = self.trace_dispatch frame.f_lineno = line frame.f_trace = None - stop = True + stop_info['stop'] = True else: - stop = False - - if django_stop: - frame = suspend_django(self, main_debugger, thread, frame) - if frame: - self.doWaitSuspend(thread, frame, event, arg) - elif stop: - #event is always == line or return at this point - if event == 'line': - self.setSuspend(thread, step_cmd) - self.doWaitSuspend(thread, frame, event, arg) - else: #return event - back = frame.f_back - if back is not None: - #When we get to the pydevd run function, the debugging has actually finished for the main thread - #(note that it can still go on for other threads, but for this one, we just make it finish) - #So, just setting it to None should be OK - base = basename(back.f_code.co_filename) - if base == 'pydevd.py' and back.f_code.co_name == 'run': - back = None - - elif base == 'pydevd_traceproperty.py': - # We dont want to trace the return event of pydevd_traceproperty (custom property for debugging) - #if we're in a return, we want it to appear to the user in the previous frame! - return None + stop_info['stop'] = False - if back is not None: - #if we're in a return, we want it to appear to the user in the previous frame! + if True in DictIterValues(stop_info): + stopped_on_plugin = main_debugger.plugin.stop(main_debugger, frame, event, self._args, stop_info, arg, step_cmd) + if DictContains(stop_info, 'stop') and stop_info['stop'] and not stopped_on_plugin: + if event == 'line': self.setSuspend(thread, step_cmd) - self.doWaitSuspend(thread, back, event, arg) - else: - #in jython we may not have a back frame - info.pydev_step_stop = None - info.pydev_step_cmd = None - info.pydev_state = STATE_RUN + self.doWaitSuspend(thread, frame, event, arg) + else: #return event + back = frame.f_back + if back is not None: + #When we get to the pydevd run function, the debugging has actually finished for the main thread + #(note that it can still go on for other threads, but for this one, we just make it finish) + #So, just setting it to None should be OK + base = basename(back.f_code.co_filename) + if base == 'pydevd.py' and back.f_code.co_name == 'run': + back = None + + elif base == 'pydevd_traceproperty.py': + # We dont want to trace the return event of pydevd_traceproperty (custom property for debugging) + #if we're in a return, we want it to appear to the user in the previous frame! + return None + + if back is not None: + #if we're in a return, we want it to appear to the user in the previous frame! + self.setSuspend(thread, step_cmd) + self.doWaitSuspend(thread, back, event, arg) + else: + #in jython we may not have a back frame + info.pydev_step_stop = None + info.pydev_step_cmd = None + info.pydev_state = STATE_RUN except: @@ -562,61 +525,4 @@ class PyDBFrame: except ImportError: if hasattr(sys, 'exc_clear'): #jython does not have it sys.exc_clear() #don't keep the traceback - pass #ok, psyco not available - - def should_stop_on_django_breakpoint(self, frame, event, arg): - mainDebugger = self._args[0] - thread = self._args[3] - flag = False - template_frame_file = get_template_file_name(frame) - - #pydev_log.debug("Django is rendering a template: %s\n" % template_frame_file) - - django_breakpoints_for_file = mainDebugger.django_breakpoints.get(template_frame_file) - if django_breakpoints_for_file: - - #pydev_log.debug("Breakpoints for that file: %s\n" % django_breakpoints_for_file) - - template_frame_line = get_template_line(frame, template_frame_file) - - #pydev_log.debug("Tracing template line: %d\n" % template_frame_line) - - if DictContains(django_breakpoints_for_file, template_frame_line): - django_breakpoint = django_breakpoints_for_file[template_frame_line] - - if django_breakpoint.is_triggered(template_frame_file, template_frame_line): - - #pydev_log.debug("Breakpoint is triggered.\n") - - flag = True - new_frame = DjangoTemplateFrame( - frame, - template_frame_file=template_frame_file, - template_frame_line=template_frame_line, - ) - - if django_breakpoint.condition is not None: - try: - val = eval(django_breakpoint.condition, new_frame.f_globals, new_frame.f_locals) - if not val: - flag = False - pydev_log.debug("Condition '%s' is evaluated to %s. Not suspending.\n" % (django_breakpoint.condition, val)) - except: - pydev_log.info( - 'Error while evaluating condition \'%s\': %s\n' % (django_breakpoint.condition, sys.exc_info()[1])) - - if django_breakpoint.expression is not None: - try: - try: - val = eval(django_breakpoint.expression, new_frame.f_globals, new_frame.f_locals) - except: - val = sys.exc_info()[1] - finally: - if val is not None: - thread.additionalInfo.message = val - if flag: - frame = suspend_django(self, mainDebugger, thread, frame) - return flag, frame - -def add_exception_to_frame(frame, exception_info): - frame.f_locals['__exception__'] = exception_info
\ No newline at end of file + pass #ok, psyco not available
\ No newline at end of file diff --git a/python/helpers/pydev/pydevd_frame_utils.py b/python/helpers/pydev/pydevd_frame_utils.py index 23becca0570d..0c9e8446d19c 100644 --- a/python/helpers/pydev/pydevd_frame_utils.py +++ b/python/helpers/pydev/pydevd_frame_utils.py @@ -1,11 +1,11 @@ -class Frame: +class Frame(object): def __init__( self, f_back, f_fileno, f_code, f_locals, - f_globals={}, + f_globals=None, f_trace=None): self.f_back = f_back self.f_lineno = f_fileno @@ -14,8 +14,31 @@ class Frame: self.f_globals = f_globals self.f_trace = f_trace + if self.f_globals is None: + self.f_globals = {} -class FCode: + +class FCode(object): def __init__(self, name, filename): self.co_name = name - self.co_filename = filename
\ No newline at end of file + self.co_filename = filename + + +def add_exception_to_frame(frame, exception_info): + frame.f_locals['__exception__'] = exception_info + + +def just_raised(trace): + if trace is None: + return False + return trace.tb_next is None + + +def cached_call(obj, func, *args): + cached_name = '_cached_' + func.__name__ + if not hasattr(obj, cached_name): + setattr(obj, cached_name, func(*args)) + + return getattr(obj, cached_name) + + diff --git a/python/helpers/pydev/pydevd_plugin_utils.py b/python/helpers/pydev/pydevd_plugin_utils.py new file mode 100644 index 000000000000..5b106b8234db --- /dev/null +++ b/python/helpers/pydev/pydevd_plugin_utils.py @@ -0,0 +1,85 @@ +import os +import types + +import pydev_log +import pydevd_trace_api +from third_party.pluginbase import PluginBase + +def load_plugins(package): + plugin_base = PluginBase(package=package) + plugin_source = plugin_base.make_plugin_source(searchpath=[os.path.dirname(os.path.realpath(__file__)) + '/' + package], persist=True) + plugins = [] + for plugin in plugin_source.list_plugins(): + loaded_plugin = None + try: + loaded_plugin = plugin_source.load_plugin(plugin) + except: + pydev_log.error("Failed to load plugin %s" % plugin, True) + if loaded_plugin: + plugins.append(loaded_plugin) + + return plugins + + +def bind_func_to_method(func, obj, method_name): + foo = types.MethodType(func, obj) + + setattr(obj, method_name, foo) + return foo + + +class PluginManager(object): + def __init__(self, main_debugger): + self.plugins = load_plugins('pydevd_plugins') + self.active_plugins = [] + self.main_debugger = main_debugger + self.rebind_methods() + + def add_breakpoint(self, func_name, *args, **kwargs): + # add breakpoint for plugin and remember which plugin to use in tracing + for plugin in self.plugins: + if hasattr(plugin, func_name): + func = getattr(plugin, func_name) + result = func(self, *args, **kwargs) + if result: + self.activate(plugin) + + return result + return None + + def activate(self, plugin): + self.active_plugins.append(plugin) + self.rebind_methods() + + def rebind_methods(self): + if len(self.active_plugins) == 0: + self.bind_functions(pydevd_trace_api, getattr, pydevd_trace_api) + elif len(self.active_plugins) == 1: + self.bind_functions(pydevd_trace_api, getattr, self.active_plugins[0]) + else: + self.bind_functions(pydevd_trace_api, create_dispatch, self.active_plugins) + + def bind_functions(self, interface, function_factory, arg): + for name in dir(interface): + func = function_factory(arg, name) + if type(func) == types.FunctionType: + bind_func_to_method(func, self, name) + + +def create_dispatch(obj, name): + def dispatch(self, *args, **kwargs): + result = None + for p in self.active_plugins: + r = getattr(p, name)(self, *args, **kwargs) + if not result: + result = r + return result + return dispatch + + + + + + + + diff --git a/python/helpers/pydev/pydevd_plugins/__init__.py b/python/helpers/pydev/pydevd_plugins/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/python/helpers/pydev/pydevd_plugins/__init__.py diff --git a/python/helpers/pydev/pydevd_plugins/django_debug.py b/python/helpers/pydev/pydevd_plugins/django_debug.py new file mode 100644 index 000000000000..ac23d40bd381 --- /dev/null +++ b/python/helpers/pydev/pydevd_plugins/django_debug.py @@ -0,0 +1,357 @@ +from pydevd_comm import CMD_SET_BREAK, CMD_ADD_EXCEPTION_BREAK +import inspect +from pydevd_constants import STATE_SUSPEND, GetThreadId, DictContains +from pydevd_file_utils import NormFileToServer, GetFileNameAndBaseFromFile +from pydevd_breakpoints import LineBreakpoint, get_exception_name +import pydevd_vars +import traceback +import pydev_log +from pydevd_frame_utils import add_exception_to_frame, FCode, cached_call, just_raised + +DJANGO_SUSPEND = 2 + +class DjangoLineBreakpoint(LineBreakpoint): + def __init__(self, file, line, condition, func_name, expression): + self.file = file + LineBreakpoint.__init__(self, line, condition, func_name, expression) + + def is_triggered(self, template_frame_file, template_frame_line): + return self.file == template_frame_file and self.line == template_frame_line + + def __str__(self): + return "DjangoLineBreakpoint: %s-%d" %(self.file, self.line) + + +def add_line_breakpoint(plugin, pydb, type, file, line, condition, expression, func_name): + if type == 'django-line': + breakpoint = DjangoLineBreakpoint(file, line, condition, func_name, expression) + if not hasattr(pydb, 'django_breakpoints'): + pydb.django_breakpoints = {} + return breakpoint, pydb.django_breakpoints + return None + +def add_exception_breakpoint(plugin, pydb, type, exception): + if type == 'django': + if not hasattr(pydb, 'django_exception_break'): + pydb.django_exception_break = {} + pydb.django_exception_break[exception] = True + pydb.setTracingForUntracedContexts() + return True + return False + + +def remove_exception_breakpoint(plugin, pydb, type, exception): + if type == 'django': + try: + del pydb.django_exception_break[exception] + return True + except: + pass + return False + +def get_breakpoints(plugin, pydb, type): + if type == 'django-line': + return pydb.django_breakpoints + return None + +def _inherits(cls, *names): + if cls.__name__ in names: + return True + inherits_node = False + for base in inspect.getmro(cls): + if base.__name__ in names: + inherits_node = True + break + return inherits_node + + +def _is_django_render_call(frame): + try: + name = frame.f_code.co_name + if name != 'render': + return False + + if not DictContains(frame.f_locals, 'self'): + return False + + cls = frame.f_locals['self'].__class__ + + inherits_node = _inherits(cls, 'Node') + + if not inherits_node: + return False + + clsname = cls.__name__ + return clsname != 'TextNode' and clsname != 'NodeList' + except: + traceback.print_exc() + return False + + +def _is_django_context_get_call(frame): + try: + if not DictContains(frame.f_locals, 'self'): + return False + + cls = frame.f_locals['self'].__class__ + + return _inherits(cls, 'BaseContext') + except: + traceback.print_exc() + return False + + +def _is_django_resolve_call(frame): + try: + name = frame.f_code.co_name + if name != '_resolve_lookup': + return False + + if not DictContains(frame.f_locals, 'self'): + return False + + cls = frame.f_locals['self'].__class__ + + clsname = cls.__name__ + return clsname == 'Variable' + except: + traceback.print_exc() + return False + + +def _is_django_suspended(thread): + return thread.additionalInfo.suspend_type == DJANGO_SUSPEND + + +def suspend_django(mainDebugger, thread, frame, cmd=CMD_SET_BREAK): + frame = DjangoTemplateFrame(frame) + + if frame.f_lineno is None: + return None + + #try: + # if thread.additionalInfo.filename == frame.f_code.co_filename and thread.additionalInfo.line == frame.f_lineno: + # return None # don't stay twice on the same line + #except AttributeError: + # pass + + pydevd_vars.addAdditionalFrameById(GetThreadId(thread), {id(frame): frame}) + + mainDebugger.setSuspend(thread, cmd) + thread.additionalInfo.suspend_type = DJANGO_SUSPEND + + thread.additionalInfo.filename = frame.f_code.co_filename + thread.additionalInfo.line = frame.f_lineno + + return frame + + +def _find_django_render_frame(frame): + while frame is not None and not _is_django_render_call(frame): + frame = frame.f_back + + return frame + +#======================================================================================================================= +# Django Frame +#======================================================================================================================= + +def _read_file(filename): + f = open(filename, "r") + s = f.read() + f.close() + return s + + +def _offset_to_line_number(text, offset): + curLine = 1 + curOffset = 0 + while curOffset < offset: + if curOffset == len(text): + return -1 + c = text[curOffset] + if c == '\n': + curLine += 1 + elif c == '\r': + curLine += 1 + if curOffset < len(text) and text[curOffset + 1] == '\n': + curOffset += 1 + + curOffset += 1 + + return curLine + + +def _get_source(frame): + try: + node = frame.f_locals['self'] + if hasattr(node, 'source'): + return node.source + else: + pydev_log.error_once("WARNING: Template path is not available. Please set TEMPLATE_DEBUG=True in your settings.py to make " + " django template breakpoints working") + return None + + except: + pydev_log.debug(traceback.format_exc()) + return None + + +def _get_template_file_name(frame): + try: + source = _get_source(frame) + if source is None: + pydev_log.debug("Source is None\n") + return None + fname = source[0].name + + if fname == '<unknown source>': + pydev_log.debug("Source name is %s\n" % fname) + return None + else: + filename, base = GetFileNameAndBaseFromFile(fname) + return filename + except: + pydev_log.debug(traceback.format_exc()) + return None + + +def _get_template_line(frame): + source = _get_source(frame) + file_name = _get_template_file_name(frame) + try: + return _offset_to_line_number(_read_file(file_name), source[1][0]) + except: + return None + + +class DjangoTemplateFrame: + def __init__(self, frame): + file_name = _get_template_file_name(frame) + self.back_context = frame.f_locals['context'] + self.f_code = FCode('Django Template', file_name) + self.f_lineno = _get_template_line(frame) + self.f_back = frame + self.f_globals = {} + self.f_locals = self.collect_context(self.back_context) + self.f_trace = None + + def collect_context(self, context): + res = {} + try: + for d in context.dicts: + for k, v in d.items(): + res[k] = v + except AttributeError: + pass + return res + + def changeVariable(self, name, value): + for d in self.back_context.dicts: + for k, v in d.items(): + if k == name: + d[k] = value + +def _is_django_exception_break_context(frame): + try: + name = frame.f_code.co_name + except: + name = None + return name in ['_resolve_lookup', 'find_template'] + + +#======================================================================================================================= +# Django Step Commands +#======================================================================================================================= + +def can_not_skip(plugin, mainDebugger, pydb_frame, frame): + if hasattr(mainDebugger, 'django_breakpoints') and mainDebugger.django_breakpoints and cached_call(pydb_frame, _is_django_render_call, frame): + filename = _get_template_file_name(frame) + django_breakpoints_for_file = mainDebugger.django_breakpoints.get(filename) + if django_breakpoints_for_file: + return True + return False + +def has_exception_breaks(plugin, mainDebugger): + return hasattr(mainDebugger, 'django_exception_break') and mainDebugger.django_exception_break + + +def cmd_step_into(plugin, mainDebugger, frame, event, args, stop_info): + mainDebugger, filename, info, thread = args + if _is_django_suspended(thread): + #stop_info['django_stop'] = event == 'call' and cached_call(frame, is_django_render_call) + stop_info['stop'] = stop_info['stop'] and _is_django_resolve_call(frame.f_back) and not _is_django_context_get_call(frame) + if stop_info['stop']: + info.pydev_django_resolve_frame = 1 #we remember that we've go into python code from django rendering frame + + +def cmd_step_over(plugin, mainDebugger, frame, event, args, stop_info): + mainDebugger, filename, info, thread = args + if _is_django_suspended(thread): + stop_info['django_stop'] = event == 'call' and _is_django_render_call(frame) + stop_info['stop'] = False + return True + else: + if event == 'return' and info.pydev_django_resolve_frame is not None and _is_django_resolve_call(frame.f_back): + #we return to Django suspend mode and should not stop before django rendering frame + info.pydev_step_stop = info.pydev_django_resolve_frame + info.pydev_django_resolve_frame = None + thread.additionalInfo.suspend_type = DJANGO_SUSPEND + stop_info['stop'] = info.pydev_step_stop is frame and event in ('line', 'return') + + return False + + +def stop(plugin, mainDebugger, frame, event, args, stop_info, arg, step_cmd): + mainDebugger, filename, info, thread = args + if DictContains(stop_info, 'django_stop') and stop_info['django_stop']: + frame = suspend_django(mainDebugger, thread, frame, step_cmd) + if frame: + mainDebugger.doWaitSuspend(thread, frame, event, arg) + return True + return False + + +def get_breakpoint(plugin, mainDebugger, pydb_frame, frame, event, args): + mainDebugger, filename, info, thread = args + flag = False + django_breakpoint = None + new_frame = None + + if event == 'call' and info.pydev_state != STATE_SUSPEND and hasattr(mainDebugger, 'django_breakpoints') and \ + mainDebugger.django_breakpoints and cached_call(pydb_frame, _is_django_render_call, frame): + filename = _get_template_file_name(frame) + pydev_log.debug("Django is rendering a template: %s\n" % filename) + django_breakpoints_for_file = mainDebugger.django_breakpoints.get(filename) + if django_breakpoints_for_file: + pydev_log.debug("Breakpoints for that file: %s\n" % django_breakpoints_for_file) + template_line = _get_template_line(frame) + pydev_log.debug("Tracing template line: %d\n" % template_line) + + if DictContains(django_breakpoints_for_file, template_line): + django_breakpoint = django_breakpoints_for_file[template_line] + flag = True + new_frame = DjangoTemplateFrame(frame) + return flag, django_breakpoint, new_frame + + +def suspend(plugin, mainDebugger, thread, frame): + return suspend_django(mainDebugger, thread, frame) + +def exception_break(plugin, mainDebugger, pydb_frame, frame, args, arg): + mainDebugger, filename, info, thread = args + exception, value, trace = arg + if hasattr(mainDebugger, 'django_exception_break') and mainDebugger.django_exception_break and \ + get_exception_name(exception) in ['VariableDoesNotExist', 'TemplateDoesNotExist', 'TemplateSyntaxError'] and \ + just_raised(trace) and _is_django_exception_break_context(frame): + render_frame = _find_django_render_frame(frame) + if render_frame: + suspend_frame = suspend_django(mainDebugger, thread, render_frame, CMD_ADD_EXCEPTION_BREAK) + if suspend_frame: + add_exception_to_frame(suspend_frame, (exception, value, trace)) + flag = True + thread.additionalInfo.message = 'VariableDoesNotExist' + suspend_frame.f_back = frame + frame = suspend_frame + return (flag, frame) + return None
\ No newline at end of file diff --git a/python/helpers/pydev/pydevd_plugins/jinja2_debug.py b/python/helpers/pydev/pydevd_plugins/jinja2_debug.py new file mode 100644 index 000000000000..9968a81d0043 --- /dev/null +++ b/python/helpers/pydev/pydevd_plugins/jinja2_debug.py @@ -0,0 +1,341 @@ +import traceback +from pydevd_breakpoints import LineBreakpoint, get_exception_name +from pydevd_constants import GetThreadId, STATE_SUSPEND, DictContains +from pydevd_comm import CMD_SET_BREAK, CMD_STEP_OVER, CMD_ADD_EXCEPTION_BREAK +import pydevd_vars +from pydevd_file_utils import GetFileNameAndBaseFromFile +from pydevd_frame_utils import add_exception_to_frame, FCode, cached_call + +JINJA2_SUSPEND = 3 + +class Jinja2LineBreakpoint(LineBreakpoint): + + def __init__(self, file, line, condition, func_name, expression): + self.file = file + LineBreakpoint.__init__(self, line, condition, func_name, expression) + + def is_triggered(self, template_frame_file, template_frame_line): + return self.file == template_frame_file and self.line == template_frame_line + + def __str__(self): + return "Jinja2LineBreakpoint: %s-%d" %(self.file, self.line) + + +def add_line_breakpoint(plugin, pydb, type, file, line, condition, func_name, expression): + result = None + if type == 'jinja2-line': + breakpoint = Jinja2LineBreakpoint(file, line, condition, func_name, expression) + if not hasattr(pydb, 'jinja2_breakpoints'): + pydb.jinja2_breakpoints = {} + result = breakpoint, pydb.jinja2_breakpoints + return result + return result + +def add_exception_breakpoint(plugin, pydb, type, exception): + if type == 'jinja2': + if not hasattr(pydb, 'jinja2_exception_break'): + pydb.jinja2_exception_break = {} + pydb.jinja2_exception_break[exception] = True + pydb.setTracingForUntracedContexts() + return True + return False + +def remove_exception_breakpoint(plugin, pydb, type, exception): + if type == 'jinja2': + try: + del pydb.jinja2_exception_break[exception] + return True + except: + pass + return False + +def get_breakpoints(plugin, pydb, type): + if type == 'jinja2-line': + return pydb.jinja2_breakpoints + return None + + +def is_jinja2_render_call(frame): + try: + name = frame.f_code.co_name + if DictContains(frame.f_globals, "__jinja_template__") and name in ("root", "loop", "macro") or name.startswith("block_"): + return True + return False + except: + traceback.print_exc() + return False + + +def suspend_jinja2(pydb, thread, frame, cmd=CMD_SET_BREAK): + frame = Jinja2TemplateFrame(frame) + + if frame.f_lineno is None: + return None + + pydevd_vars.addAdditionalFrameById(GetThreadId(thread), {id(frame): frame}) + pydb.setSuspend(thread, cmd) + + thread.additionalInfo.suspend_type = JINJA2_SUSPEND + thread.additionalInfo.filename = frame.f_code.co_filename + thread.additionalInfo.line = frame.f_lineno + + return frame + +def is_jinja2_suspended(thread): + return thread.additionalInfo.suspend_type == JINJA2_SUSPEND + +def is_jinja2_context_call(frame): + return DictContains(frame.f_locals, "_Context__obj") + +def is_jinja2_internal_function(frame): + return DictContains(frame.f_locals, 'self') and frame.f_locals['self'].__class__.__name__ in \ + ('LoopContext', 'TemplateReference', 'Macro', 'BlockReference') + +def find_jinja2_render_frame(frame): + while frame is not None and not is_jinja2_render_call(frame): + frame = frame.f_back + + return frame + +def change_variable(plugin, pydb, frame, attr, expression): + if isinstance(frame, Jinja2TemplateFrame): + result = eval(expression, frame.f_globals, frame.f_locals) + frame.changeVariable(attr, result) + + +#======================================================================================================================= +# Jinja2 Frame +#======================================================================================================================= + +class Jinja2TemplateFrame: + + def __init__(self, frame): + file_name = get_jinja2_template_filename(frame) + self.back_context = None + if 'context' in frame.f_locals: + #sometimes we don't have 'context', e.g. in macros + self.back_context = frame.f_locals['context'] + self.f_code = FCode('template', file_name) + self.f_lineno = get_jinja2_template_line(frame) + self.f_back = find_render_function_frame(frame) + self.f_globals = {} + self.f_locals = self.collect_context(frame) + self.f_trace = None + + def collect_context(self, frame): + res = {} + if self.back_context is not None: + for k, v in self.back_context.items(): + res[k] = v + for k, v in frame.f_locals.items(): + if not k.startswith('l_'): + if not k in res: + #local variables should shadow globals from context + res[k] = v + elif v and not is_missing(v): + res[k[2:]] = v + return res + + def changeVariable(self, name, value): + for k, v in self.back_context.items(): + if k == name: + self.back_context.vars[k] = value + +def is_missing(item): + if item.__class__.__name__ == 'MissingType': + return True + return False + +def find_render_function_frame(frame): + #in order to hide internal rendering functions + old_frame = frame + try: + while not (DictContains(frame.f_locals, 'self') and frame.f_locals['self'].__class__.__name__ == 'Template' and \ + frame.f_code.co_name == 'render'): + frame = frame.f_back + if frame is None: + return old_frame + return frame + except: + return old_frame + +def get_jinja2_template_line(frame): + debug_info = None + if DictContains(frame.f_globals,'__jinja_template__'): + _debug_info = frame.f_globals['__jinja_template__']._debug_info + if _debug_info != '': + #sometimes template contains only plain text + debug_info = frame.f_globals['__jinja_template__'].debug_info + + if debug_info is None: + return None + + lineno = frame.f_lineno + + for pair in debug_info: + if pair[1] == lineno: + return pair[0] + + return None + +def get_jinja2_template_filename(frame): + if DictContains(frame.f_globals, '__jinja_template__'): + fname = frame.f_globals['__jinja_template__'].filename + filename, base = GetFileNameAndBaseFromFile(fname) + return filename + return None + + +#======================================================================================================================= +# Jinja2 Step Commands +#======================================================================================================================= + + +def has_exception_breaks(plugin, pydb): + return hasattr(pydb, 'jinja2_exception_break') and pydb.jinja2_exception_break + +def can_not_skip(plugin, pydb, pydb_frame, frame): + if hasattr(pydb, 'jinja2_breakpoints') and pydb.jinja2_breakpoints and cached_call(pydb_frame, is_jinja2_render_call, frame): + filename = get_jinja2_template_filename(frame) + jinja2_breakpoints_for_file = pydb.jinja2_breakpoints.get(filename) + if jinja2_breakpoints_for_file: + return True + return False + + +def cmd_step_into(plugin, pydb, frame, event, args, stop_info): + pydb, filename, info, thread = args + if not hasattr(info, 'pydev_call_from_jinja2'): + info.pydev_call_from_jinja2 = None + if not hasattr(info, 'pydev_call_inside_jinja2'): + info.pydev_call_inside_jinja2 = None + if is_jinja2_suspended(thread): + stop_info['jinja2_stop'] = event in ('call', 'line') and is_jinja2_render_call(frame) + stop_info['stop'] = False + if info.pydev_call_from_jinja2 is not None: + if is_jinja2_internal_function(frame): + #if internal Jinja2 function was called, we sould continue debugging inside template + info.pydev_call_from_jinja2 = None + else: + #we go into python code from Jinja2 rendering frame + stop_info['stop'] = True + + if event == 'call' and is_jinja2_context_call(frame.f_back): + #we called function from context, the next step will be in function + info.pydev_call_from_jinja2 = 1 + + if event == 'return' and is_jinja2_context_call(frame.f_back): + #we return from python code to Jinja2 rendering frame + info.pydev_step_stop = info.pydev_call_from_jinja2 + info.pydev_call_from_jinja2 = None + thread.additionalInfo.suspend_type = JINJA2_SUSPEND + stop_info['stop'] = False + + #print "info.pydev_call_from_jinja2", info.pydev_call_from_jinja2, "stop_info", stop_info, \ + # "thread.additionalInfo.suspend_type", thread.additionalInfo.suspend_type + #print "event", event, "farme.locals", frame.f_locals + + +def cmd_step_over(plugin, pydb, frame, event, args, stop_info): + pydb, filename, info, thread = args + if not hasattr(info, 'pydev_call_from_jinja2'): + info.pydev_call_from_jinja2 = None + if not hasattr(info, 'pydev_call_inside_jinja2'): + info.pydev_call_inside_jinja2 = None + if is_jinja2_suspended(thread): + stop_info['stop'] = False + + if info.pydev_call_inside_jinja2 is None: + if is_jinja2_render_call(frame): + if event == 'call': + info.pydev_call_inside_jinja2 = frame.f_back + if event in ('line', 'return'): + info.pydev_call_inside_jinja2 = frame + else: + if event == 'line': + if is_jinja2_render_call(frame) and info.pydev_call_inside_jinja2 is frame: + stop_info['jinja2_stop'] = True + if event == 'return': + if frame is info.pydev_call_inside_jinja2 and not DictContains(frame.f_back.f_locals,'event'): + info.pydev_call_inside_jinja2 = find_jinja2_render_frame(frame.f_back) + return True + else: + if event == 'return' and is_jinja2_context_call(frame.f_back): + #we return from python code to Jinja2 rendering frame + info.pydev_call_from_jinja2 = None + info.pydev_call_inside_jinja2 = find_jinja2_render_frame(frame) + thread.additionalInfo.suspend_type = JINJA2_SUSPEND + stop_info['stop'] = False + return True + #print "info.pydev_call_from_jinja2", info.pydev_call_from_jinja2, "stop", stop, "jinja_stop", jinja2_stop, \ + # "thread.additionalInfo.suspend_type", thread.additionalInfo.suspend_type + #print "event", event, "info.pydev_call_inside_jinja2", info.pydev_call_inside_jinja2 + #print "frame", frame, "frame.f_back", frame.f_back, "step_stop", info.pydev_step_stop + #print "is_context_call", is_jinja2_context_call(frame) + #print "render", is_jinja2_render_call(frame) + #print "-------------" + return False + + +def stop(plugin, pydb, frame, event, args, stop_info, arg, step_cmd): + pydb, filename, info, thread = args + if DictContains(stop_info, 'jinja2_stop') and stop_info['jinja2_stop']: + frame = suspend_jinja2(pydb, thread, frame, step_cmd) + if frame: + pydb.doWaitSuspend(thread, frame, event, arg) + return True + return False + + +def get_breakpoint(plugin, pydb, pydb_frame, frame, event, args): + pydb, filename, info, thread = args + new_frame = None + jinja2_breakpoint = None + flag = False + if event in ('line', 'call') and info.pydev_state != STATE_SUSPEND and hasattr(pydb, 'jinja2_breakpoints') and \ + pydb.jinja2_breakpoints and cached_call(pydb_frame, is_jinja2_render_call, frame): + filename = get_jinja2_template_filename(frame) + jinja2_breakpoints_for_file = pydb.jinja2_breakpoints.get(filename) + new_frame = Jinja2TemplateFrame(frame) + + if jinja2_breakpoints_for_file: + lineno = frame.f_lineno + template_lineno = get_jinja2_template_line(frame) + if template_lineno is not None and DictContains(jinja2_breakpoints_for_file, template_lineno): + jinja2_breakpoint = jinja2_breakpoints_for_file[template_lineno] + flag = True + new_frame = Jinja2TemplateFrame(frame) + + return flag, jinja2_breakpoint, new_frame + + +def suspend(plugin, pydb, thread, frame): + return suspend_jinja2(pydb, thread, frame) + + +def exception_break(plugin, pydb, pydb_frame, frame, args, arg): + pydb, filename, info, thread = args + exception, value, trace = arg + if hasattr(pydb, 'jinja2_exception_break') and pydb.jinja2_exception_break: + if get_exception_name(exception) in ('UndefinedError', 'TemplateNotFound', 'TemplatesNotFound'): + #errors in rendering + render_frame = find_jinja2_render_frame(frame) + if render_frame: + suspend_frame = suspend_jinja2(pydb, thread, render_frame, CMD_ADD_EXCEPTION_BREAK) + if suspend_frame: + add_exception_to_frame(suspend_frame, (exception, value, trace)) + flag = True + suspend_frame.f_back = frame + frame = suspend_frame + return flag, frame + elif get_exception_name(exception) in ('TemplateSyntaxError', 'TemplateAssertionError'): + #errors in compile time + name = frame.f_code.co_name + if name in ('template', 'top-level template code') or name.startswith('block '): + #Jinja2 translates exception info and creates fake frame on his own + pydb_frame.setSuspend(thread, CMD_ADD_EXCEPTION_BREAK) + add_exception_to_frame(frame, (exception, value, trace)) + thread.additionalInfo.suspend_type = JINJA2_SUSPEND + flag = True + return flag, frame + return None
\ No newline at end of file diff --git a/python/helpers/pydev/pydevd_resolver.py b/python/helpers/pydev/pydevd_resolver.py index ad49bd881ba0..444dead4cce7 100644 --- a/python/helpers/pydev/pydevd_resolver.py +++ b/python/helpers/pydev/pydevd_resolver.py @@ -409,6 +409,17 @@ class NdArrayResolver: return obj.dtype if attribute == 'size': return obj.size + if attribute.startswith('['): + container = NdArrayItemsContainer() + i = 0 + format_str = '%0' + str(int(len(str(len(obj))))) + 'd' + for item in obj: + setattr(container, format_str % i, item) + i += 1 + if i > MAX_ITEMS_TO_HANDLE: + setattr(container, TOO_LARGE_ATTR, TOO_LARGE_MSG) + break + return container return None def getDictionary(self, obj): @@ -427,9 +438,10 @@ class NdArrayResolver: ret['shape'] = obj.shape ret['dtype'] = obj.dtype ret['size'] = obj.size + ret['[0:%s]' % (len(obj))] = list(obj) return ret - +class NdArrayItemsContainer: pass #======================================================================================================================= # FrameResolver #======================================================================================================================= diff --git a/python/helpers/pydev/pydevd_trace_api.py b/python/helpers/pydev/pydevd_trace_api.py new file mode 100644 index 000000000000..5d2f30e98a3a --- /dev/null +++ b/python/helpers/pydev/pydevd_trace_api.py @@ -0,0 +1,35 @@ +def add_line_breakpoint(plugin, pydb, type, file, line, condition, expression, func_name): + return None + +def add_exception_breakpoint(plugin, pydb, type, exception): + return False + +def remove_exception_breakpoint(plugin, pydb, type, exception): + return False + +def get_breakpoints(plugin, pydb): + return None + +def can_not_skip(plugin, pydb, pydb_frame, frame): + return False + +def has_exception_breaks(plugin, pydb): + return False + +def cmd_step_into(plugin, pydb, frame, event, args, stop_info): + return False + +def cmd_step_over(plugin, pydb, frame, event, args, stop_info): + return False + +def stop(plugin, pydb, frame, event, args, stop_info, arg, step_cmd): + return False + +def get_breakpoint(plugin, pydb, pydb_frame, frame, event, args): + return None + +def suspend(plugin, pydb, thread, frame): + return None + +def exception_break(plugin, pydb, pydb_frame, frame, args, arg): + return None
\ No newline at end of file diff --git a/python/helpers/pydev/pydevd_utils.py b/python/helpers/pydev/pydevd_utils.py index 134b190881ac..753263ba4f31 100644 --- a/python/helpers/pydev/pydevd_utils.py +++ b/python/helpers/pydev/pydevd_utils.py @@ -6,7 +6,28 @@ except: from urllib.parse import quote import pydevd_constants -import pydev_log +import sys + + +def save_main_module(file, module_name): + # patch provided by: Scott Schlesier - when script is run, it does not + # use globals from pydevd: + # This will prevent the pydevd script from contaminating the namespace for the script to be debugged + # pretend pydevd is not the main module, and + # convince the file to be debugged that it was loaded as main + sys.modules[module_name] = sys.modules['__main__'] + sys.modules[module_name].__name__ = module_name + from imp import new_module + + m = new_module('__main__') + sys.modules['__main__'] = m + if hasattr(sys.modules[module_name], '__loader__'): + setattr(m, '__loader__', + getattr(sys.modules[module_name], '__loader__')) + m.__file__ = file + + return m + def to_number(x): if is_string(x): diff --git a/python/helpers/pydev/pydevd_vars.py b/python/helpers/pydev/pydevd_vars.py index 3baea5b61b99..e1aa436b8946 100644 --- a/python/helpers/pydev/pydevd_vars.py +++ b/python/helpers/pydev/pydevd_vars.py @@ -2,7 +2,6 @@ resolution/conversion to XML. """ import pickle -from django_frame import DjangoTemplateFrame from pydevd_constants import * #@UnusedWildImport from types import * #@UnusedWildImport @@ -360,10 +359,10 @@ def changeAttrExpression(thread_id, frame_id, attr, expression): try: expression = expression.replace('@LINE@', '\n') - if isinstance(frame, DjangoTemplateFrame): - result = eval(expression, frame.f_globals, frame.f_locals) - frame.changeVariable(attr, result) - return + # if isinstance(frame, DjangoTemplateFrame): # TODO: implemente for plugins + # result = eval(expression, frame.f_globals, frame.f_locals) + # frame.changeVariable(attr, result) + # return result if attr[:7] == "Globals": attr = attr[8:] @@ -374,7 +373,7 @@ def changeAttrExpression(thread_id, frame_id, attr, expression): if pydevd_save_locals.is_save_locals_available(): frame.f_locals[attr] = eval(expression, frame.f_globals, frame.f_locals) pydevd_save_locals.save_locals(frame) - return + return frame.f_locals[attr] #default way (only works for changing it in the topmost frame) result = eval(expression, frame.f_globals, frame.f_locals) diff --git a/python/helpers/pydev/test_debug.py b/python/helpers/pydev/test_debug.py index 2196ca6f9540..27da09b5593d 100644 --- a/python/helpers/pydev/test_debug.py +++ b/python/helpers/pydev/test_debug.py @@ -5,16 +5,18 @@ import os test_data_path = os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), '..', '..', 'testData', 'debug')) + class PyDevTestCase(unittest.TestCase): def testZipFileExits(self): from pydevd_file_utils import exists - self.assertTrue(exists(test_data_path +'/zipped_lib.zip/zipped_module.py')) + self.assertTrue(exists(test_data_path + '/zipped_lib.zip/zipped_module.py')) self.assertFalse(exists(test_data_path + '/zipped_lib.zip/zipped_module2.py')) self.assertFalse(exists(test_data_path + '/zipped_lib2.zip/zipped_module.py')) def testEggFileExits(self): from pydevd_file_utils import exists + self.assertTrue(exists(test_data_path + '/pycharm-debug.egg/pydev/pydevd.py')) self.assertFalse(exists(test_data_path + '/pycharm-debug.egg/pydev/pydevd2.py')) diff --git a/python/helpers/pydev/tests/check_pydevconsole.py b/python/helpers/pydev/tests/check_pydevconsole.py new file mode 100644 index 000000000000..0ff1a8618a24 --- /dev/null +++ b/python/helpers/pydev/tests/check_pydevconsole.py @@ -0,0 +1,110 @@ +import sys +import os + +# Put pydevconsole in the path. +sys.argv[0] = os.path.dirname(sys.argv[0]) +sys.path.insert(1, os.path.join(os.path.dirname(sys.argv[0]))) + +print('Running tests with:', sys.executable) +print('PYTHONPATH:') +print('\n'.join(sorted(sys.path))) + +import threading +import unittest + +import pydevconsole +from pydev_imports import xmlrpclib, SimpleXMLRPCServer + +try: + raw_input + raw_input_name = 'raw_input' +except NameError: + raw_input_name = 'input' + +#======================================================================================================================= +# Test +#======================================================================================================================= +class Test(unittest.TestCase): + def startClientThread(self, client_port): + class ClientThread(threading.Thread): + def __init__(self, client_port): + threading.Thread.__init__(self) + self.client_port = client_port + + def run(self): + class HandleRequestInput: + def RequestInput(self): + return 'RequestInput: OK' + + handle_request_input = HandleRequestInput() + + import pydev_localhost + + print('Starting client with:', pydev_localhost.get_localhost(), self.client_port) + client_server = SimpleXMLRPCServer((pydev_localhost.get_localhost(), self.client_port), logRequests=False) + client_server.register_function(handle_request_input.RequestInput) + client_server.serve_forever() + + client_thread = ClientThread(client_port) + client_thread.setDaemon(True) + client_thread.start() + return client_thread + + + def getFreeAddresses(self): + import socket + + s = socket.socket() + s.bind(('', 0)) + port0 = s.getsockname()[1] + + s1 = socket.socket() + s1.bind(('', 0)) + port1 = s1.getsockname()[1] + s.close() + s1.close() + return port0, port1 + + + 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 + + print('Starting server with:', pydev_localhost.get_localhost(), self.server_port, self.client_port) + 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("import sys; print('Running with: %s %s' % (sys.executable or sys.platform, sys.version))") + server.addExec('class Foo:') + server.addExec(' pass') + server.addExec('') + server.addExec('foo = Foo()') + server.addExec('a = %s()' % raw_input_name) + server.addExec('print (a)') + +#======================================================================================================================= +# main +#======================================================================================================================= +if __name__ == '__main__': + unittest.main() + diff --git a/python/helpers/pydev/tests/test_pydev_ipython_010.py b/python/helpers/pydev/tests/test_pydev_ipython_010.py new file mode 100644 index 000000000000..1822763d4b29 --- /dev/null +++ b/python/helpers/pydev/tests/test_pydev_ipython_010.py @@ -0,0 +1,80 @@ +# TODO: This test no longer works (check if it should be fixed or removed altogether). + +#import unittest +#import sys +#import os +##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]))) +# +#from pydev_localhost import get_localhost +# +# +#IS_JYTHON = sys.platform.find('java') != -1 +# +##======================================================================================================================= +## TestCase +##======================================================================================================================= +#class TestCase(unittest.TestCase): +# +# def setUp(self): +# unittest.TestCase.setUp(self) +# +# def tearDown(self): +# unittest.TestCase.tearDown(self) +# +# def testIPython(self): +# try: +# from pydev_ipython_console import PyDevFrontEnd +# except: +# if IS_JYTHON: +# return +# front_end = PyDevFrontEnd(get_localhost(), 0) +# +# front_end.input_buffer = 'if True:' +# self.assert_(not front_end._on_enter()) +# +# front_end.input_buffer = 'if True:\n' + \ +# front_end.continuation_prompt() + ' a = 10\n' +# self.assert_(not front_end._on_enter()) +# +# +# front_end.input_buffer = 'if True:\n' + \ +# front_end.continuation_prompt() + ' a = 10\n\n' +# self.assert_(front_end._on_enter()) +# +# +## front_end.input_buffer = ' print a' +## self.assert_(not front_end._on_enter()) +## front_end.input_buffer = '' +## self.assert_(front_end._on_enter()) +# +# +## front_end.input_buffer = 'a.' +## front_end.complete_current_input() +## front_end.input_buffer = 'if True:' +## front_end._on_enter() +# front_end.input_buffer = 'a = 30' +# front_end._on_enter() +# front_end.input_buffer = 'print a' +# front_end._on_enter() +# front_end.input_buffer = 'a?' +# front_end._on_enter() +# print front_end.complete('%') +# print front_end.complete('%e') +# print front_end.complete('cd c:/t') +# print front_end.complete('cd c:/temp/') +## front_end.input_buffer = 'print raw_input("press enter\\n")' +## front_end._on_enter() +## +# +##======================================================================================================================= +## main +##======================================================================================================================= +#if __name__ == '__main__': +# if sys.platform.find('java') == -1: +# #IPython not available for Jython +# unittest.main() +# else: +# print('not supported on Jython') diff --git a/python/helpers/pydev/tests_python/_debugger_case_qthread3.py b/python/helpers/pydev/tests_python/_debugger_case_qthread3.py index 22b0c91d7f13..9b326db7ccde 100644 --- a/python/helpers/pydev/tests_python/_debugger_case_qthread3.py +++ b/python/helpers/pydev/tests_python/_debugger_case_qthread3.py @@ -26,4 +26,5 @@ app = QtCore.QCoreApplication([]) runnable = Runnable() QtCore.QThreadPool.globalInstance().start(runnable) app.exec_() +QtCore.QThreadPool.globalInstance().waitForDone() print('TEST SUCEEDED!')
\ No newline at end of file diff --git a/python/helpers/pydev/third_party/pkgutil_old.py b/python/helpers/pydev/third_party/pkgutil_old.py new file mode 100644 index 000000000000..ce072ec9ef75 --- /dev/null +++ b/python/helpers/pydev/third_party/pkgutil_old.py @@ -0,0 +1,591 @@ +"""Utilities to support packages.""" + +# NOTE: This module must remain compatible with Python 2.3, as it is shared +# by setuptools for distribution with Python 2.3 and up. + +import os +import sys +import imp +import os.path +from types import ModuleType + +__all__ = [ + 'get_importer', 'iter_importers', 'get_loader', 'find_loader', + 'walk_packages', 'iter_modules', 'get_data', + 'ImpImporter', 'ImpLoader', 'read_code', 'extend_path', +] + +def read_code(stream): + # This helper is needed in order for the PEP 302 emulation to + # correctly handle compiled files + import marshal + + magic = stream.read(4) + if magic != imp.get_magic(): + return None + + stream.read(4) # Skip timestamp + return marshal.load(stream) + + +def simplegeneric(func): + """Make a trivial single-dispatch generic function""" + registry = {} + def wrapper(*args, **kw): + ob = args[0] + try: + cls = ob.__class__ + except AttributeError: + cls = type(ob) + try: + mro = cls.__mro__ + except AttributeError: + try: + class cls(cls, object): + pass + mro = cls.__mro__[1:] + except TypeError: + mro = object, # must be an ExtensionClass or some such :( + for t in mro: + if t in registry: + return registry[t](*args, **kw) + else: + return func(*args, **kw) + try: + wrapper.__name__ = func.__name__ + except (TypeError, AttributeError): + pass # Python 2.3 doesn't allow functions to be renamed + + def register(typ, func=None): + if func is None: + return lambda f: register(typ, f) + registry[typ] = func + return func + + wrapper.__dict__ = func.__dict__ + wrapper.__doc__ = func.__doc__ + wrapper.register = register + return wrapper + + +def walk_packages(path=None, prefix='', onerror=None): + """Yields (module_loader, name, ispkg) for all modules recursively + on path, or, if path is None, all accessible modules. + + 'path' should be either None or a list of paths to look for + modules in. + + 'prefix' is a string to output on the front of every module name + on output. + + Note that this function must import all *packages* (NOT all + modules!) on the given path, in order to access the __path__ + attribute to find submodules. + + 'onerror' is a function which gets called with one argument (the + name of the package which was being imported) if any exception + occurs while trying to import a package. If no onerror function is + supplied, ImportErrors are caught and ignored, while all other + exceptions are propagated, terminating the search. + + Examples: + + # list all modules python can access + walk_packages() + + # list all submodules of ctypes + walk_packages(ctypes.__path__, ctypes.__name__+'.') + """ + + def seen(p, m={}): + if p in m: + return True + m[p] = True + + for importer, name, ispkg in iter_modules(path, prefix): + yield importer, name, ispkg + + if ispkg: + try: + __import__(name) + except ImportError: + if onerror is not None: + onerror(name) + except Exception: + if onerror is not None: + onerror(name) + else: + raise + else: + path = getattr(sys.modules[name], '__path__', None) or [] + + # don't traverse path items we've seen before + path = [p for p in path if not seen(p)] + + for item in walk_packages(path, name+'.', onerror): + yield item + + +def iter_modules(path=None, prefix=''): + """Yields (module_loader, name, ispkg) for all submodules on path, + or, if path is None, all top-level modules on sys.path. + + 'path' should be either None or a list of paths to look for + modules in. + + 'prefix' is a string to output on the front of every module name + on output. + """ + + if path is None: + importers = iter_importers() + else: + importers = map(get_importer, path) + + yielded = {} + for i in importers: + for name, ispkg in iter_importer_modules(i, prefix): + if name not in yielded: + yielded[name] = 1 + yield i, name, ispkg + + +#@simplegeneric +def iter_importer_modules(importer, prefix=''): + if not hasattr(importer, 'iter_modules'): + return [] + return importer.iter_modules(prefix) + +iter_importer_modules = simplegeneric(iter_importer_modules) + + +class ImpImporter: + """PEP 302 Importer that wraps Python's "classic" import algorithm + + ImpImporter(dirname) produces a PEP 302 importer that searches that + directory. ImpImporter(None) produces a PEP 302 importer that searches + the current sys.path, plus any modules that are frozen or built-in. + + Note that ImpImporter does not currently support being used by placement + on sys.meta_path. + """ + + def __init__(self, path=None): + self.path = path + + def find_module(self, fullname, path=None): + # Note: we ignore 'path' argument since it is only used via meta_path + subname = fullname.split(".")[-1] + if subname != fullname and self.path is None: + return None + if self.path is None: + path = None + else: + path = [os.path.realpath(self.path)] + try: + file, filename, etc = imp.find_module(subname, path) + except ImportError: + return None + return ImpLoader(fullname, file, filename, etc) + + def iter_modules(self, prefix=''): + if self.path is None or not os.path.isdir(self.path): + return + + yielded = {} + import inspect + try: + filenames = os.listdir(self.path) + except OSError: + # ignore unreadable directories like import does + filenames = [] + filenames.sort() # handle packages before same-named modules + + for fn in filenames: + modname = inspect.getmodulename(fn) + if modname=='__init__' or modname in yielded: + continue + + path = os.path.join(self.path, fn) + ispkg = False + + if not modname and os.path.isdir(path) and '.' not in fn: + modname = fn + try: + dircontents = os.listdir(path) + except OSError: + # ignore unreadable directories like import does + dircontents = [] + for fn in dircontents: + subname = inspect.getmodulename(fn) + if subname=='__init__': + ispkg = True + break + else: + continue # not a package + + if modname and '.' not in modname: + yielded[modname] = 1 + yield prefix + modname, ispkg + + +class ImpLoader: + """PEP 302 Loader that wraps Python's "classic" import algorithm + """ + code = source = None + + def __init__(self, fullname, file, filename, etc): + self.file = file + self.filename = filename + self.fullname = fullname + self.etc = etc + + def load_module(self, fullname): + self._reopen() + try: + mod = imp.load_module(fullname, self.file, self.filename, self.etc) + finally: + if self.file: + self.file.close() + # Note: we don't set __loader__ because we want the module to look + # normal; i.e. this is just a wrapper for standard import machinery + return mod + + def get_data(self, pathname): + return open(pathname, "rb").read() + + def _reopen(self): + if self.file and self.file.closed: + mod_type = self.etc[2] + if mod_type==imp.PY_SOURCE: + self.file = open(self.filename, 'rU') + elif mod_type in (imp.PY_COMPILED, imp.C_EXTENSION): + self.file = open(self.filename, 'rb') + + def _fix_name(self, fullname): + if fullname is None: + fullname = self.fullname + elif fullname != self.fullname: + raise ImportError("Loader for module %s cannot handle " + "module %s" % (self.fullname, fullname)) + return fullname + + def is_package(self, fullname): + fullname = self._fix_name(fullname) + return self.etc[2]==imp.PKG_DIRECTORY + + def get_code(self, fullname=None): + fullname = self._fix_name(fullname) + if self.code is None: + mod_type = self.etc[2] + if mod_type==imp.PY_SOURCE: + source = self.get_source(fullname) + self.code = compile(source, self.filename, 'exec') + elif mod_type==imp.PY_COMPILED: + self._reopen() + try: + self.code = read_code(self.file) + finally: + self.file.close() + elif mod_type==imp.PKG_DIRECTORY: + self.code = self._get_delegate().get_code() + return self.code + + def get_source(self, fullname=None): + fullname = self._fix_name(fullname) + if self.source is None: + mod_type = self.etc[2] + if mod_type==imp.PY_SOURCE: + self._reopen() + try: + self.source = self.file.read() + finally: + self.file.close() + elif mod_type==imp.PY_COMPILED: + if os.path.exists(self.filename[:-1]): + f = open(self.filename[:-1], 'rU') + self.source = f.read() + f.close() + elif mod_type==imp.PKG_DIRECTORY: + self.source = self._get_delegate().get_source() + return self.source + + + def _get_delegate(self): + return ImpImporter(self.filename).find_module('__init__') + + def get_filename(self, fullname=None): + fullname = self._fix_name(fullname) + mod_type = self.etc[2] + if self.etc[2]==imp.PKG_DIRECTORY: + return self._get_delegate().get_filename() + elif self.etc[2] in (imp.PY_SOURCE, imp.PY_COMPILED, imp.C_EXTENSION): + return self.filename + return None + + +try: + import zipimport + from zipimport import zipimporter + + def iter_zipimport_modules(importer, prefix=''): + dirlist = zipimport._zip_directory_cache[importer.archive].keys() + dirlist.sort() + _prefix = importer.prefix + plen = len(_prefix) + yielded = {} + import inspect + for fn in dirlist: + if not fn.startswith(_prefix): + continue + + fn = fn[plen:].split(os.sep) + + if len(fn)==2 and fn[1].startswith('__init__.py'): + if fn[0] not in yielded: + yielded[fn[0]] = 1 + yield fn[0], True + + if len(fn)!=1: + continue + + modname = inspect.getmodulename(fn[0]) + if modname=='__init__': + continue + + if modname and '.' not in modname and modname not in yielded: + yielded[modname] = 1 + yield prefix + modname, False + + iter_importer_modules.register(zipimporter, iter_zipimport_modules) + +except ImportError: + pass + + +def get_importer(path_item): + """Retrieve a PEP 302 importer for the given path item + + The returned importer is cached in sys.path_importer_cache + if it was newly created by a path hook. + + If there is no importer, a wrapper around the basic import + machinery is returned. This wrapper is never inserted into + the importer cache (None is inserted instead). + + The cache (or part of it) can be cleared manually if a + rescan of sys.path_hooks is necessary. + """ + try: + importer = sys.path_importer_cache[path_item] + except KeyError: + for path_hook in sys.path_hooks: + try: + importer = path_hook(path_item) + break + except ImportError: + pass + else: + importer = None + sys.path_importer_cache.setdefault(path_item, importer) + + if importer is None: + try: + importer = ImpImporter(path_item) + except ImportError: + importer = None + return importer + + +def iter_importers(fullname=""): + """Yield PEP 302 importers for the given module name + + If fullname contains a '.', the importers will be for the package + containing fullname, otherwise they will be importers for sys.meta_path, + sys.path, and Python's "classic" import machinery, in that order. If + the named module is in a package, that package is imported as a side + effect of invoking this function. + + Non PEP 302 mechanisms (e.g. the Windows registry) used by the + standard import machinery to find files in alternative locations + are partially supported, but are searched AFTER sys.path. Normally, + these locations are searched BEFORE sys.path, preventing sys.path + entries from shadowing them. + + For this to cause a visible difference in behaviour, there must + be a module or package name that is accessible via both sys.path + and one of the non PEP 302 file system mechanisms. In this case, + the emulation will find the former version, while the builtin + import mechanism will find the latter. + + Items of the following types can be affected by this discrepancy: + imp.C_EXTENSION, imp.PY_SOURCE, imp.PY_COMPILED, imp.PKG_DIRECTORY + """ + if fullname.startswith('.'): + raise ImportError("Relative module names not supported") + if '.' in fullname: + # Get the containing package's __path__ + pkg = '.'.join(fullname.split('.')[:-1]) + if pkg not in sys.modules: + __import__(pkg) + path = getattr(sys.modules[pkg], '__path__', None) or [] + else: + for importer in sys.meta_path: + yield importer + path = sys.path + for item in path: + yield get_importer(item) + if '.' not in fullname: + yield ImpImporter() + +def get_loader(module_or_name): + """Get a PEP 302 "loader" object for module_or_name + + If the module or package is accessible via the normal import + mechanism, a wrapper around the relevant part of that machinery + is returned. Returns None if the module cannot be found or imported. + If the named module is not already imported, its containing package + (if any) is imported, in order to establish the package __path__. + + This function uses iter_importers(), and is thus subject to the same + limitations regarding platform-specific special import locations such + as the Windows registry. + """ + if module_or_name in sys.modules: + module_or_name = sys.modules[module_or_name] + if isinstance(module_or_name, ModuleType): + module = module_or_name + loader = getattr(module, '__loader__', None) + if loader is not None: + return loader + fullname = module.__name__ + else: + fullname = module_or_name + return find_loader(fullname) + +def find_loader(fullname): + """Find a PEP 302 "loader" object for fullname + + If fullname contains dots, path must be the containing package's __path__. + Returns None if the module cannot be found or imported. This function uses + iter_importers(), and is thus subject to the same limitations regarding + platform-specific special import locations such as the Windows registry. + """ + for importer in iter_importers(fullname): + loader = importer.find_module(fullname) + if loader is not None: + return loader + + return None + + +def extend_path(path, name): + """Extend a package's path. + + Intended use is to place the following code in a package's __init__.py: + + from pkgutil import extend_path + __path__ = extend_path(__path__, __name__) + + This will add to the package's __path__ all subdirectories of + directories on sys.path named after the package. This is useful + if one wants to distribute different parts of a single logical + package as multiple directories. + + It also looks for *.pkg files beginning where * matches the name + argument. This feature is similar to *.pth files (see site.py), + except that it doesn't special-case lines starting with 'import'. + A *.pkg file is trusted at face value: apart from checking for + duplicates, all entries found in a *.pkg file are added to the + path, regardless of whether they are exist the filesystem. (This + is a feature.) + + If the input path is not a list (as is the case for frozen + packages) it is returned unchanged. The input path is not + modified; an extended copy is returned. Items are only appended + to the copy at the end. + + It is assumed that sys.path is a sequence. Items of sys.path that + are not (unicode or 8-bit) strings referring to existing + directories are ignored. Unicode items of sys.path that cause + errors when used as filenames may cause this function to raise an + exception (in line with os.path.isdir() behavior). + """ + + if not isinstance(path, list): + # This could happen e.g. when this is called from inside a + # frozen package. Return the path unchanged in that case. + return path + + pname = os.path.join(*name.split('.')) # Reconstitute as relative path + # Just in case os.extsep != '.' + sname = os.extsep.join(name.split('.')) + sname_pkg = sname + os.extsep + "pkg" + init_py = "__init__" + os.extsep + "py" + + path = path[:] # Start with a copy of the existing path + + for dir in sys.path: + if not isinstance(dir, basestring) or not os.path.isdir(dir): + continue + subdir = os.path.join(dir, pname) + # XXX This may still add duplicate entries to path on + # case-insensitive filesystems + initfile = os.path.join(subdir, init_py) + if subdir not in path and os.path.isfile(initfile): + path.append(subdir) + # XXX Is this the right thing for subpackages like zope.app? + # It looks for a file named "zope.app.pkg" + pkgfile = os.path.join(dir, sname_pkg) + if os.path.isfile(pkgfile): + try: + f = open(pkgfile) + except IOError, msg: + sys.stderr.write("Can't open %s: %s\n" % + (pkgfile, msg)) + else: + for line in f: + line = line.rstrip('\n') + if not line or line.startswith('#'): + continue + path.append(line) # Don't check for existence! + f.close() + + return path + +def get_data(package, resource): + """Get a resource from a package. + + This is a wrapper round the PEP 302 loader get_data API. The package + argument should be the name of a package, in standard module format + (foo.bar). The resource argument should be in the form of a relative + filename, using '/' as the path separator. The parent directory name '..' + is not allowed, and nor is a rooted name (starting with a '/'). + + The function returns a binary string, which is the contents of the + specified resource. + + For packages located in the filesystem, which have already been imported, + this is the rough equivalent of + + d = os.path.dirname(sys.modules[package].__file__) + data = open(os.path.join(d, resource), 'rb').read() + + If the package cannot be located or loaded, or it uses a PEP 302 loader + which does not support get_data(), then None is returned. + """ + + loader = get_loader(package) + if loader is None or not hasattr(loader, 'get_data'): + return None + mod = sys.modules.get(package) or loader.load_module(package) + if mod is None or not hasattr(mod, '__file__'): + return None + + # Modify the resource name to be compatible with the loader.get_data + # signature - an os.path format "filename" starting with the dirname of + # the package's __file__ + parts = resource.split('/') + parts.insert(0, os.path.dirname(mod.__file__)) + resource_name = os.path.join(*parts) + return loader.get_data(resource_name) diff --git a/python/helpers/pydev/third_party/pluginbase.py b/python/helpers/pydev/third_party/pluginbase.py new file mode 100644 index 000000000000..0ad6404eee00 --- /dev/null +++ b/python/helpers/pydev/third_party/pluginbase.py @@ -0,0 +1,454 @@ +# -*- coding: utf-8 -*- +""" + pluginbase + ~~~~~~~~~~ + + Pluginbase is a module for Python that provides a system for building + plugin based applications. + + :copyright: (c) Copyright 2014 by Armin Ronacher. + :license: BSD, see LICENSE for more details. +""" +import os +import sys + +from pydevd_constants import IS_PY24, IS_PY3K + + +if IS_PY24: + from third_party.uuid_old import uuid4 +else: + from uuid import uuid4 + +if IS_PY3K: + import pkgutil +else: + import pkgutil_old as pkgutil + +import errno +try: + from hashlib import md5 +except ImportError: + from md5 import md5 +import threading + +from types import ModuleType +from weakref import ref as weakref + + +PY2 = sys.version_info[0] == 2 +if PY2: + text_type = unicode + string_types = (unicode, str) + from cStringIO import StringIO as NativeBytesIO +else: + text_type = str + string_types = (str,) + from io import BytesIO as NativeBytesIO + + +_local = threading.local() + +_internalspace = ModuleType(__name__ + '._internalspace') +_internalspace.__path__ = [] +sys.modules[_internalspace.__name__] = _internalspace + + +def get_plugin_source(module=None, stacklevel=None): + """Returns the :class:`PluginSource` for the current module or the given + module. The module can be provided by name (in which case an import + will be attempted) or as a module object. + + If no plugin source can be discovered, the return value from this method + is `None`. + + This function can be very useful if additional data has been attached + to the plugin source. For instance this could allow plugins to get + access to a back reference to the application that created them. + + :param module: optionally the module to locate the plugin source of. + :param stacklevel: defines how many levels up the module should search + for before it discovers the plugin frame. The + default is 0. This can be useful for writing wrappers + around this function. + """ + if module is None: + frm = sys._getframe((stacklevel or 0) + 1) + name = frm.f_globals['__name__'] + glob = frm.f_globals + elif isinstance(module, string_types): + frm = sys._getframe(1) + name = module + glob = __import__(module, frm.f_globals, + frm.f_locals, ['__dict__']).__dict__ + else: + name = module.__name__ + glob = module.__dict__ + return _discover_space(name, glob) + + +def _discover_space(name, globals): + try: + return _local.space_stack[-1] + except (AttributeError, IndexError): + pass + + if '__pluginbase_state__' in globals: + return globals['__pluginbase_state__'].source + + mod_name = globals.get('__name__') + if mod_name is not None and \ + mod_name.startswith(_internalspace.__name__ + '.'): + end = mod_name.find('.', len(_internalspace.__name__) + 1) + space = sys.modules.get(mod_name[:end]) + if space is not None: + return space.__pluginbase_state__.source + + +def _shutdown_module(mod): + members = list(mod.__dict__.items()) + for key, value in members: + if key[:1] != '_': + setattr(mod, key, None) + for key, value in members: + setattr(mod, key, None) + + +def _to_bytes(s): + if isinstance(s, text_type): + return s.encode('utf-8') + return s + + +class _IntentionallyEmptyModule(ModuleType): + + def __getattr__(self, name): + try: + return ModuleType.__getattr__(self, name) + except AttributeError: + if name[:2] == '__': + raise + raise RuntimeError( + 'Attempted to import from a plugin base module (%s) without ' + 'having a plugin source activated. To solve this error ' + 'you have to move the import into a "with" block of the ' + 'associated plugin source.' % self.__name__) + + +class _PluginSourceModule(ModuleType): + + def __init__(self, source): + modname = '%s.%s' % (_internalspace.__name__, source.spaceid) + ModuleType.__init__(self, modname) + self.__pluginbase_state__ = PluginBaseState(source) + + @property + def __path__(self): + try: + ps = self.__pluginbase_state__.source + except AttributeError: + return [] + return ps.searchpath + ps.base.searchpath + + +def _setup_base_package(module_name): + try: + mod = __import__(module_name, None, None, ['__name__']) + except ImportError: + mod = None + if '.' in module_name: + parent_mod = __import__(module_name.rsplit('.', 1)[0], + None, None, ['__name__']) + else: + parent_mod = None + + if mod is None: + mod = _IntentionallyEmptyModule(module_name) + if parent_mod is not None: + setattr(parent_mod, module_name.rsplit('.', 1)[-1], mod) + sys.modules[module_name] = mod + + +class PluginBase(object): + """The plugin base acts as a control object around a dummy Python + package that acts as a container for plugins. Usually each + application creates exactly one base object for all plugins. + + :param package: the name of the package that acts as the plugin base. + Usually this module does not exist. Unless you know + what you are doing you should not create this module + on the file system. + :param searchpath: optionally a shared search path for modules that + will be used by all plugin sources registered. + """ + + def __init__(self, package, searchpath=None): + #: the name of the dummy package. + self.package = package + if searchpath is None: + searchpath = [] + #: the default search path shared by all plugins as list. + self.searchpath = searchpath + _setup_base_package(package) + + def make_plugin_source(self, *args, **kwargs): + """Creats a plugin source for this plugin base and returns it. + All parameters are forwarded to :class:`PluginSource`. + """ + return PluginSource(self, *args, **kwargs) + + +class PluginSource(object): + """The plugin source is what ultimately decides where plugins are + loaded from. Plugin bases can have multiple plugin sources which act + as isolation layer. While this is not a security system it generally + is not possible for plugins from different sources to accidentally + cross talk. + + Once a plugin source has been created it can be used in a ``with`` + statement to change the behavior of the ``import`` statement in the + block to define which source to load the plugins from:: + + plugin_source = plugin_base.make_plugin_source( + searchpath=['./path/to/plugins', './path/to/more/plugins']) + + with plugin_source: + from myapplication.plugins import my_plugin + + :param base: the base this plugin source belongs to. + :param identifier: optionally a stable identifier. If it's not defined + a random identifier is picked. It's useful to set this + to a stable value to have consistent tracebacks + between restarts and to support pickle. + :param searchpath: a list of paths where plugins are looked for. + :param persist: optionally this can be set to `True` and the plugins + will not be cleaned up when the plugin source gets + garbage collected. + """ + # Set these here to false by default so that a completely failing + # constructor does not fuck up the destructor. + persist = False + mod = None + + def __init__(self, base, identifier=None, searchpath=None, + persist=False): + #: indicates if this plugin source persists or not. + self.persist = persist + if identifier is None: + identifier = str(uuid4()) + #: the identifier for this source. + self.identifier = identifier + #: A reference to the plugin base that created this source. + self.base = base + #: a list of paths where plugins are searched in. + self.searchpath = searchpath + #: The internal module name of the plugin source as it appears + #: in the :mod:`pluginsource._internalspace`. + div = None + self.spaceid = '_sp' + md5( + _to_bytes(self.base.package) + _to_bytes('|') + + _to_bytes(self.identifier) + ).hexdigest() + #: a reference to the module on the internal + #: :mod:`pluginsource._internalspace`. + self.mod = _PluginSourceModule(self) + + if hasattr(_internalspace, self.spaceid): + raise RuntimeError('This plugin source already exists.') + sys.modules[self.mod.__name__] = self.mod + setattr(_internalspace, self.spaceid, self.mod) + + def __del__(self): + if not self.persist: + self.cleanup() + + def list_plugins(self): + """Returns a sorted list of all plugins that are available in this + plugin source. This can be useful to automatically discover plugins + that are available and is usually used together with + :meth:`load_plugin`. + """ + rv = [] + for _, modname, ispkg in pkgutil.iter_modules(self.mod.__path__): + rv.append(modname) + return sorted(rv) + + def load_plugin(self, name): + """This automatically loads a plugin by the given name from the + current source and returns the module. This is a convenient + alternative to the import statement and saves you from invoking + ``__import__`` or a similar function yourself. + + :param name: the name of the plugin to load. + """ + if '.' in name: + raise ImportError('Plugin names cannot contain dots.') + + #with self: + # return __import__(self.base.package + '.' + name, + # globals(), {}, ['__name__']) + + self.__assert_not_cleaned_up() + _local.__dict__.setdefault('space_stack', []).append(self) + try: + res = __import__(self.base.package + '.' + name, + globals(), {}, ['__name__']) + return res + finally: + try: + _local.space_stack.pop() + except (AttributeError, IndexError): + pass + + def open_resource(self, plugin, filename): + """This function locates a resource inside the plugin and returns + a byte stream to the contents of it. If the resource cannot be + loaded an :exc:`IOError` will be raised. Only plugins that are + real Python packages can contain resources. Plain old Python + modules do not allow this for obvious reasons. + + .. versionadded:: 0.3 + + :param plugin: the name of the plugin to open the resource of. + :param filename: the name of the file within the plugin to open. + """ + mod = self.load_plugin(plugin) + fn = getattr(mod, '__file__', None) + if fn is not None: + if fn.endswith(('.pyc', '.pyo')): + fn = fn[:-1] + if os.path.isfile(fn): + return open(os.path.join(os.path.dirname(fn), filename), 'rb') + buf = pkgutil.get_data(self.mod.__name__ + '.' + plugin, filename) + if buf is None: + raise IOError(errno.ENOEXITS, 'Could not find resource') + return NativeBytesIO(buf) + + def cleanup(self): + """Cleans up all loaded plugins manually. This is necessary to + call only if :attr:`persist` is enabled. Otherwise this happens + automatically when the source gets garbage collected. + """ + self.__cleanup() + + def __cleanup(self, _sys=sys, _shutdown_module=_shutdown_module): + # The default parameters are necessary because this can be fired + # from the destructor and so late when the interpreter shuts down + # that these functions and modules might be gone. + if self.mod is None: + return + modname = self.mod.__name__ + self.mod.__pluginbase_state__ = None + self.mod = None + try: + delattr(_internalspace, self.spaceid) + except AttributeError: + pass + prefix = modname + '.' + _sys.modules.pop(modname) + for key, value in list(_sys.modules.items()): + if not key.startswith(prefix): + continue + mod = _sys.modules.pop(key, None) + if mod is None: + continue + _shutdown_module(mod) + + def __assert_not_cleaned_up(self): + if self.mod is None: + raise RuntimeError('The plugin source was already cleaned up.') + + def __enter__(self): + self.__assert_not_cleaned_up() + _local.__dict__.setdefault('space_stack', []).append(self) + return self + + def __exit__(self, exc_type, exc_value, tb): + try: + _local.space_stack.pop() + except (AttributeError, IndexError): + pass + + def _rewrite_module_path(self, modname): + self.__assert_not_cleaned_up() + if modname == self.base.package: + return self.mod.__name__ + elif modname.startswith(self.base.package + '.'): + pieces = modname.split('.') + return self.mod.__name__ + '.' + '.'.join( + pieces[self.base.package.count('.') + 1:]) + + +class PluginBaseState(object): + __slots__ = ('_source',) + + def __init__(self, source): + if source.persist: + self._source = lambda: source + else: + self._source = weakref(source) + + @property + def source(self): + rv = self._source() + if rv is None: + raise AttributeError('Plugin source went away') + return rv + + +class _ImportHook(ModuleType): + + def __init__(self, name, system_import): + ModuleType.__init__(self, name) + self._system_import = system_import + self.enabled = True + + def enable(self): + """Enables the import hook which drives the plugin base system. + This is the default. + """ + self.enabled = True + + def disable(self): + """Disables the import hook and restores the default import system + behavior. This effectively breaks pluginbase but can be useful + for testing purposes. + """ + self.enabled = False + + def plugin_import(self, name, globals=None, locals=None, + fromlist=None, level=-2): + import_name = name + if self.enabled: + ref_globals = globals + if ref_globals is None: + ref_globals = sys._getframe(1).f_globals + space = _discover_space(name, ref_globals) + if space is not None: + actual_name = space._rewrite_module_path(name) + if actual_name is not None: + import_name = actual_name + if level == -2: + # fake impossible value; default value depends on version + if IS_PY24: + # the level parameter was added in version 2.5 + return self._system_import(import_name, globals, locals, fromlist) + elif IS_PY3K: + # default value for level parameter in python 3 + level = 0 + else: + # default value for level parameter in other versions + level = -1 + return self._system_import(import_name, globals, locals, + fromlist, level) + + +try: + import __builtin__ as builtins +except ImportError: + import builtins + +import_hook = _ImportHook(__name__ + '.import_hook', builtins.__import__) +builtins.__import__ = import_hook.plugin_import +sys.modules[import_hook.__name__] = import_hook +del builtins diff --git a/python/helpers/pydev/third_party/uuid_old.py b/python/helpers/pydev/third_party/uuid_old.py new file mode 100644 index 000000000000..ae3da25ca557 --- /dev/null +++ b/python/helpers/pydev/third_party/uuid_old.py @@ -0,0 +1,541 @@ +r"""UUID objects (universally unique identifiers) according to RFC 4122. + +This module provides immutable UUID objects (class UUID) and the functions +uuid1(), uuid3(), uuid4(), uuid5() for generating version 1, 3, 4, and 5 +UUIDs as specified in RFC 4122. + +If all you want is a unique ID, you should probably call uuid1() or uuid4(). +Note that uuid1() may compromise privacy since it creates a UUID containing +the computer's network address. uuid4() creates a random UUID. + +Typical usage: + + >>> import uuid + + # make a UUID based on the host ID and current time + >>> uuid.uuid1() + UUID('a8098c1a-f86e-11da-bd1a-00112444be1e') + + # make a UUID using an MD5 hash of a namespace UUID and a name + >>> uuid.uuid3(uuid.NAMESPACE_DNS, 'python.org') + UUID('6fa459ea-ee8a-3ca4-894e-db77e160355e') + + # make a random UUID + >>> uuid.uuid4() + UUID('16fd2706-8baf-433b-82eb-8c7fada847da') + + # make a UUID using a SHA-1 hash of a namespace UUID and a name + >>> uuid.uuid5(uuid.NAMESPACE_DNS, 'python.org') + UUID('886313e1-3b8a-5372-9b90-0c9aee199e5d') + + # make a UUID from a string of hex digits (braces and hyphens ignored) + >>> x = uuid.UUID('{00010203-0405-0607-0809-0a0b0c0d0e0f}') + + # convert a UUID to a string of hex digits in standard form + >>> str(x) + '00010203-0405-0607-0809-0a0b0c0d0e0f' + + # get the raw 16 bytes of the UUID + >>> x.bytes + '\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f' + + # make a UUID from a 16-byte string + >>> uuid.UUID(bytes=x.bytes) + UUID('00010203-0405-0607-0809-0a0b0c0d0e0f') +""" + +__author__ = 'Ka-Ping Yee <ping@zesty.ca>' + +RESERVED_NCS, RFC_4122, RESERVED_MICROSOFT, RESERVED_FUTURE = [ + 'reserved for NCS compatibility', 'specified in RFC 4122', + 'reserved for Microsoft compatibility', 'reserved for future definition'] + +class UUID(object): + """Instances of the UUID class represent UUIDs as specified in RFC 4122. + UUID objects are immutable, hashable, and usable as dictionary keys. + Converting a UUID to a string with str() yields something in the form + '12345678-1234-1234-1234-123456789abc'. The UUID constructor accepts + five possible forms: a similar string of hexadecimal digits, or a tuple + of six integer fields (with 32-bit, 16-bit, 16-bit, 8-bit, 8-bit, and + 48-bit values respectively) as an argument named 'fields', or a string + of 16 bytes (with all the integer fields in big-endian order) as an + argument named 'bytes', or a string of 16 bytes (with the first three + fields in little-endian order) as an argument named 'bytes_le', or a + single 128-bit integer as an argument named 'int'. + + UUIDs have these read-only attributes: + + bytes the UUID as a 16-byte string (containing the six + integer fields in big-endian byte order) + + bytes_le the UUID as a 16-byte string (with time_low, time_mid, + and time_hi_version in little-endian byte order) + + fields a tuple of the six integer fields of the UUID, + which are also available as six individual attributes + and two derived attributes: + + time_low the first 32 bits of the UUID + time_mid the next 16 bits of the UUID + time_hi_version the next 16 bits of the UUID + clock_seq_hi_variant the next 8 bits of the UUID + clock_seq_low the next 8 bits of the UUID + node the last 48 bits of the UUID + + time the 60-bit timestamp + clock_seq the 14-bit sequence number + + hex the UUID as a 32-character hexadecimal string + + int the UUID as a 128-bit integer + + urn the UUID as a URN as specified in RFC 4122 + + variant the UUID variant (one of the constants RESERVED_NCS, + RFC_4122, RESERVED_MICROSOFT, or RESERVED_FUTURE) + + version the UUID version number (1 through 5, meaningful only + when the variant is RFC_4122) + """ + + def __init__(self, hex=None, bytes=None, bytes_le=None, fields=None, + int=None, version=None): + r"""Create a UUID from either a string of 32 hexadecimal digits, + a string of 16 bytes as the 'bytes' argument, a string of 16 bytes + in little-endian order as the 'bytes_le' argument, a tuple of six + integers (32-bit time_low, 16-bit time_mid, 16-bit time_hi_version, + 8-bit clock_seq_hi_variant, 8-bit clock_seq_low, 48-bit node) as + the 'fields' argument, or a single 128-bit integer as the 'int' + argument. When a string of hex digits is given, curly braces, + hyphens, and a URN prefix are all optional. For example, these + expressions all yield the same UUID: + + UUID('{12345678-1234-5678-1234-567812345678}') + UUID('12345678123456781234567812345678') + UUID('urn:uuid:12345678-1234-5678-1234-567812345678') + UUID(bytes='\x12\x34\x56\x78'*4) + UUID(bytes_le='\x78\x56\x34\x12\x34\x12\x78\x56' + + '\x12\x34\x56\x78\x12\x34\x56\x78') + UUID(fields=(0x12345678, 0x1234, 0x5678, 0x12, 0x34, 0x567812345678)) + UUID(int=0x12345678123456781234567812345678) + + Exactly one of 'hex', 'bytes', 'bytes_le', 'fields', or 'int' must + be given. The 'version' argument is optional; if given, the resulting + UUID will have its variant and version set according to RFC 4122, + overriding the given 'hex', 'bytes', 'bytes_le', 'fields', or 'int'. + """ + + if [hex, bytes, bytes_le, fields, int].count(None) != 4: + raise TypeError('need one of hex, bytes, bytes_le, fields, or int') + if hex is not None: + hex = hex.replace('urn:', '').replace('uuid:', '') + hex = hex.strip('{}').replace('-', '') + if len(hex) != 32: + raise ValueError('badly formed hexadecimal UUID string') + int = long(hex, 16) + if bytes_le is not None: + if len(bytes_le) != 16: + raise ValueError('bytes_le is not a 16-char string') + bytes = (bytes_le[3] + bytes_le[2] + bytes_le[1] + bytes_le[0] + + bytes_le[5] + bytes_le[4] + bytes_le[7] + bytes_le[6] + + bytes_le[8:]) + if bytes is not None: + if len(bytes) != 16: + raise ValueError('bytes is not a 16-char string') + int = long(('%02x'*16) % tuple(map(ord, bytes)), 16) + if fields is not None: + if len(fields) != 6: + raise ValueError('fields is not a 6-tuple') + (time_low, time_mid, time_hi_version, + clock_seq_hi_variant, clock_seq_low, node) = fields + if not 0 <= time_low < 1<<32L: + raise ValueError('field 1 out of range (need a 32-bit value)') + if not 0 <= time_mid < 1<<16L: + raise ValueError('field 2 out of range (need a 16-bit value)') + if not 0 <= time_hi_version < 1<<16L: + raise ValueError('field 3 out of range (need a 16-bit value)') + if not 0 <= clock_seq_hi_variant < 1<<8L: + raise ValueError('field 4 out of range (need an 8-bit value)') + if not 0 <= clock_seq_low < 1<<8L: + raise ValueError('field 5 out of range (need an 8-bit value)') + if not 0 <= node < 1<<48L: + raise ValueError('field 6 out of range (need a 48-bit value)') + clock_seq = (clock_seq_hi_variant << 8L) | clock_seq_low + int = ((time_low << 96L) | (time_mid << 80L) | + (time_hi_version << 64L) | (clock_seq << 48L) | node) + if int is not None: + if not 0 <= int < 1<<128L: + raise ValueError('int is out of range (need a 128-bit value)') + if version is not None: + if not 1 <= version <= 5: + raise ValueError('illegal version number') + # Set the variant to RFC 4122. + int &= ~(0xc000 << 48L) + int |= 0x8000 << 48L + # Set the version number. + int &= ~(0xf000 << 64L) + int |= version << 76L + self.__dict__['int'] = int + + def __cmp__(self, other): + if isinstance(other, UUID): + return cmp(self.int, other.int) + return NotImplemented + + def __hash__(self): + return hash(self.int) + + def __int__(self): + return self.int + + def __repr__(self): + return 'UUID(%r)' % str(self) + + def __setattr__(self, name, value): + raise TypeError('UUID objects are immutable') + + def __str__(self): + hex = '%032x' % self.int + return '%s-%s-%s-%s-%s' % ( + hex[:8], hex[8:12], hex[12:16], hex[16:20], hex[20:]) + + def get_bytes(self): + bytes = '' + for shift in range(0, 128, 8): + bytes = chr((self.int >> shift) & 0xff) + bytes + return bytes + + bytes = property(get_bytes) + + def get_bytes_le(self): + bytes = self.bytes + return (bytes[3] + bytes[2] + bytes[1] + bytes[0] + + bytes[5] + bytes[4] + bytes[7] + bytes[6] + bytes[8:]) + + bytes_le = property(get_bytes_le) + + def get_fields(self): + return (self.time_low, self.time_mid, self.time_hi_version, + self.clock_seq_hi_variant, self.clock_seq_low, self.node) + + fields = property(get_fields) + + def get_time_low(self): + return self.int >> 96L + + time_low = property(get_time_low) + + def get_time_mid(self): + return (self.int >> 80L) & 0xffff + + time_mid = property(get_time_mid) + + def get_time_hi_version(self): + return (self.int >> 64L) & 0xffff + + time_hi_version = property(get_time_hi_version) + + def get_clock_seq_hi_variant(self): + return (self.int >> 56L) & 0xff + + clock_seq_hi_variant = property(get_clock_seq_hi_variant) + + def get_clock_seq_low(self): + return (self.int >> 48L) & 0xff + + clock_seq_low = property(get_clock_seq_low) + + def get_time(self): + return (((self.time_hi_version & 0x0fffL) << 48L) | + (self.time_mid << 32L) | self.time_low) + + time = property(get_time) + + def get_clock_seq(self): + return (((self.clock_seq_hi_variant & 0x3fL) << 8L) | + self.clock_seq_low) + + clock_seq = property(get_clock_seq) + + def get_node(self): + return self.int & 0xffffffffffff + + node = property(get_node) + + def get_hex(self): + return '%032x' % self.int + + hex = property(get_hex) + + def get_urn(self): + return 'urn:uuid:' + str(self) + + urn = property(get_urn) + + def get_variant(self): + if not self.int & (0x8000 << 48L): + return RESERVED_NCS + elif not self.int & (0x4000 << 48L): + return RFC_4122 + elif not self.int & (0x2000 << 48L): + return RESERVED_MICROSOFT + else: + return RESERVED_FUTURE + + variant = property(get_variant) + + def get_version(self): + # The version bits are only meaningful for RFC 4122 UUIDs. + if self.variant == RFC_4122: + return int((self.int >> 76L) & 0xf) + + version = property(get_version) + +def _find_mac(command, args, hw_identifiers, get_index): + import os + for dir in ['', '/sbin/', '/usr/sbin']: + executable = os.path.join(dir, command) + if not os.path.exists(executable): + continue + + try: + # LC_ALL to get English output, 2>/dev/null to + # prevent output on stderr + cmd = 'LC_ALL=C %s %s 2>/dev/null' % (executable, args) + pipe = os.popen(cmd) + except IOError: + continue + + for line in pipe: + words = line.lower().split() + for i in range(len(words)): + if words[i] in hw_identifiers: + return int(words[get_index(i)].replace(':', ''), 16) + return None + +def _ifconfig_getnode(): + """Get the hardware address on Unix by running ifconfig.""" + + # This works on Linux ('' or '-a'), Tru64 ('-av'), but not all Unixes. + for args in ('', '-a', '-av'): + mac = _find_mac('ifconfig', args, ['hwaddr', 'ether'], lambda i: i+1) + if mac: + return mac + + import socket + ip_addr = socket.gethostbyname(socket.gethostname()) + + # Try getting the MAC addr from arp based on our IP address (Solaris). + mac = _find_mac('arp', '-an', [ip_addr], lambda i: -1) + if mac: + return mac + + # This might work on HP-UX. + mac = _find_mac('lanscan', '-ai', ['lan0'], lambda i: 0) + if mac: + return mac + + return None + +def _ipconfig_getnode(): + """Get the hardware address on Windows by running ipconfig.exe.""" + import os, re + dirs = ['', r'c:\windows\system32', r'c:\winnt\system32'] + try: + import ctypes + buffer = ctypes.create_string_buffer(300) + ctypes.windll.kernel32.GetSystemDirectoryA(buffer, 300) + dirs.insert(0, buffer.value.decode('mbcs')) + except: + pass + for dir in dirs: + try: + pipe = os.popen(os.path.join(dir, 'ipconfig') + ' /all') + except IOError: + continue + for line in pipe: + value = line.split(':')[-1].strip().lower() + if re.match('([0-9a-f][0-9a-f]-){5}[0-9a-f][0-9a-f]', value): + return int(value.replace('-', ''), 16) + +def _netbios_getnode(): + """Get the hardware address on Windows using NetBIOS calls. + See http://support.microsoft.com/kb/118623 for details.""" + import win32wnet, netbios + ncb = netbios.NCB() + ncb.Command = netbios.NCBENUM + ncb.Buffer = adapters = netbios.LANA_ENUM() + adapters._pack() + if win32wnet.Netbios(ncb) != 0: + return + adapters._unpack() + for i in range(adapters.length): + ncb.Reset() + ncb.Command = netbios.NCBRESET + ncb.Lana_num = ord(adapters.lana[i]) + if win32wnet.Netbios(ncb) != 0: + continue + ncb.Reset() + ncb.Command = netbios.NCBASTAT + ncb.Lana_num = ord(adapters.lana[i]) + ncb.Callname = '*'.ljust(16) + ncb.Buffer = status = netbios.ADAPTER_STATUS() + if win32wnet.Netbios(ncb) != 0: + continue + status._unpack() + bytes = map(ord, status.adapter_address) + return ((bytes[0]<<40L) + (bytes[1]<<32L) + (bytes[2]<<24L) + + (bytes[3]<<16L) + (bytes[4]<<8L) + bytes[5]) + +# Thanks to Thomas Heller for ctypes and for his help with its use here. + +# If ctypes is available, use it to find system routines for UUID generation. +_uuid_generate_random = _uuid_generate_time = _UuidCreate = None +try: + import ctypes, ctypes.util + _buffer = ctypes.create_string_buffer(16) + + # The uuid_generate_* routines are provided by libuuid on at least + # Linux and FreeBSD, and provided by libc on Mac OS X. + for libname in ['uuid', 'c']: + try: + lib = ctypes.CDLL(ctypes.util.find_library(libname)) + except: + continue + if hasattr(lib, 'uuid_generate_random'): + _uuid_generate_random = lib.uuid_generate_random + if hasattr(lib, 'uuid_generate_time'): + _uuid_generate_time = lib.uuid_generate_time + + # On Windows prior to 2000, UuidCreate gives a UUID containing the + # hardware address. On Windows 2000 and later, UuidCreate makes a + # random UUID and UuidCreateSequential gives a UUID containing the + # hardware address. These routines are provided by the RPC runtime. + # NOTE: at least on Tim's WinXP Pro SP2 desktop box, while the last + # 6 bytes returned by UuidCreateSequential are fixed, they don't appear + # to bear any relationship to the MAC address of any network device + # on the box. + try: + lib = ctypes.windll.rpcrt4 + except: + lib = None + _UuidCreate = getattr(lib, 'UuidCreateSequential', + getattr(lib, 'UuidCreate', None)) +except: + pass + +def _unixdll_getnode(): + """Get the hardware address on Unix using ctypes.""" + _uuid_generate_time(_buffer) + return UUID(bytes=_buffer.raw).node + +def _windll_getnode(): + """Get the hardware address on Windows using ctypes.""" + if _UuidCreate(_buffer) == 0: + return UUID(bytes=_buffer.raw).node + +def _random_getnode(): + """Get a random node ID, with eighth bit set as suggested by RFC 4122.""" + import random + return random.randrange(0, 1<<48L) | 0x010000000000L + +_node = None + +def getnode(): + """Get the hardware address as a 48-bit positive integer. + + The first time this runs, it may launch a separate program, which could + be quite slow. If all attempts to obtain the hardware address fail, we + choose a random 48-bit number with its eighth bit set to 1 as recommended + in RFC 4122. + """ + + global _node + if _node is not None: + return _node + + import sys + if sys.platform == 'win32': + getters = [_windll_getnode, _netbios_getnode, _ipconfig_getnode] + else: + getters = [_unixdll_getnode, _ifconfig_getnode] + + for getter in getters + [_random_getnode]: + try: + _node = getter() + except: + continue + if _node is not None: + return _node + +_last_timestamp = None + +def uuid1(node=None, clock_seq=None): + """Generate a UUID from a host ID, sequence number, and the current time. + If 'node' is not given, getnode() is used to obtain the hardware + address. If 'clock_seq' is given, it is used as the sequence number; + otherwise a random 14-bit sequence number is chosen.""" + + # When the system provides a version-1 UUID generator, use it (but don't + # use UuidCreate here because its UUIDs don't conform to RFC 4122). + if _uuid_generate_time and node is clock_seq is None: + _uuid_generate_time(_buffer) + return UUID(bytes=_buffer.raw) + + global _last_timestamp + import time + nanoseconds = int(time.time() * 1e9) + # 0x01b21dd213814000 is the number of 100-ns intervals between the + # UUID epoch 1582-10-15 00:00:00 and the Unix epoch 1970-01-01 00:00:00. + timestamp = int(nanoseconds/100) + 0x01b21dd213814000L + if timestamp <= _last_timestamp: + timestamp = _last_timestamp + 1 + _last_timestamp = timestamp + if clock_seq is None: + import random + clock_seq = random.randrange(1<<14L) # instead of stable storage + time_low = timestamp & 0xffffffffL + time_mid = (timestamp >> 32L) & 0xffffL + time_hi_version = (timestamp >> 48L) & 0x0fffL + clock_seq_low = clock_seq & 0xffL + clock_seq_hi_variant = (clock_seq >> 8L) & 0x3fL + if node is None: + node = getnode() + return UUID(fields=(time_low, time_mid, time_hi_version, + clock_seq_hi_variant, clock_seq_low, node), version=1) + +def uuid3(namespace, name): + """Generate a UUID from the MD5 hash of a namespace UUID and a name.""" + import md5 + hash = md5.md5(namespace.bytes + name).digest() + return UUID(bytes=hash[:16], version=3) + +def uuid4(): + """Generate a random UUID.""" + + # When the system provides a version-4 UUID generator, use it. + if _uuid_generate_random: + _uuid_generate_random(_buffer) + return UUID(bytes=_buffer.raw) + + # Otherwise, get randomness from urandom or the 'random' module. + try: + import os + return UUID(bytes=os.urandom(16), version=4) + except: + import random + bytes = [chr(random.randrange(256)) for i in range(16)] + return UUID(bytes=bytes, version=4) + +def uuid5(namespace, name): + """Generate a UUID from the SHA-1 hash of a namespace UUID and a name.""" + import sha + hash = sha.sha(namespace.bytes + name).digest() + return UUID(bytes=hash[:16], version=5) + +# The following standard UUIDs are for use with uuid3() or uuid5(). + +NAMESPACE_DNS = UUID('6ba7b810-9dad-11d1-80b4-00c04fd430c8') +NAMESPACE_URL = UUID('6ba7b811-9dad-11d1-80b4-00c04fd430c8') +NAMESPACE_OID = UUID('6ba7b812-9dad-11d1-80b4-00c04fd430c8') +NAMESPACE_X500 = UUID('6ba7b814-9dad-11d1-80b4-00c04fd430c8') |