web2py.gluon.debug

Package web2py :: Package gluon :: Module debug |
[hide private]
[frames] | no frames]

# Source Code for Module web2py.gluon.debug

      1  #!/usr/bin/env python
      2  # -*- coding: utf-8 -*-
      3
      4  """
      5  This file is part of the web2py Web Framework
      6  Developed by Massimo Di Pierro <mdipierro@cs.depaul.edu>,
      7  limodou <limodou@gmail.com> and srackham <srackham@gmail.com>.
      8  License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
      9
     10  """
     11
     12  import logging
     13  import os
     14  import pdb
     15  import Queue
     16  import sys
     17
     18  logger = logging.getLogger("web2py")
     19
     20 -class Pipe(Queue.Queue):
     21 -    def __init__(self, name, mode='r', *args, **kwargs):
     22          self.__name = name
     23          Queue.Queue.__init__(self, *args, **kwargs)
     24
     25 -    def write(self, data):
     26          logger.debug("debug %s writting %s" % (self.__name, data))
     27          self.put(data)
     28
     29 -    def flush(self):
     30          # mark checkpoint (complete message)
     31          logger.debug("debug %s flushing..." % self.__name)
     32          self.put(None)
     33          # wait until it is processed
     34          self.join()
     35          logger.debug("debug %s flush done" % self.__name)
     36
     37 -    def read(self, count=None, timeout=None):
     38          logger.debug("debug %s reading..." % (self.__name, ))
     39          data = self.get(block=True, timeout=timeout)
     40          # signal that we are ready
     41          self.task_done()
     42          logger.debug("debug %s read %s" % (self.__name, data))
     43          return data
     44
     45 -    def readline(self):
     46          logger.debug("debug %s readline..." % (self.__name, ))
     47          return self.read()
     48
     49
     50  pipe_in = Pipe('in')
     51  pipe_out = Pipe('out')
     52
     53  debugger = pdb.Pdb(completekey=None, stdin=pipe_in, stdout=pipe_out,)
     54
     55 -def set_trace():
     56      "breakpoint shortcut (like pdb)"
     57      logger.info("DEBUG: set_trace!")
     58      debugger.set_trace(sys._getframe().f_back)
     59
     60
     61 -def stop_trace():
     62      "stop waiting for the debugger (called atexit)"
     63      # this should prevent communicate is wait forever a command result
     64      # and the main thread has finished
     65      logger.info("DEBUG: stop_trace!")
     66      pipe_out.write("debug finished!")
     67      pipe_out.write(None)
     68      #pipe_out.flush()
     69
     70 -def communicate(command=None):
     71      "send command to debbuger, wait result"
     72      if command is not None:
     73          logger.info("DEBUG: sending command %s" % command)
     74          pipe_in.write(command)
     75          #pipe_in.flush()
     76      result = []
     77      while True:
     78          data = pipe_out.read()
     79          if data is None:
     80              break
     81          result.append(data)
     82      logger.info("DEBUG: result %s" % repr(result))
     83      return ''.join(result)
     84
     85
     86  # New debugger implementation using qdb and a web UI
     87
     88  import gluon.contrib.qdb as qdb
     89  from threading import RLock
     90
     91  interact_lock = RLock()
     92  run_lock = RLock()
     93
     94 -def check_interaction(fn):
     95      "Decorator to clean and prevent interaction when not available"
     96      def check_fn(self, *args, **kwargs):
     97          interact_lock.acquire()
     98          try:
     99              if self.filename:
    100                  self.clear_interaction()
    101                  return fn(self, *args, **kwargs)
    102          finally:
    103              interact_lock.release()
    104      return check_fn
    105
    106
    107 -class WebDebugger(qdb.Frontend):
    108      "Qdb web2py interface"
    109
    110 -    def __init__(self, pipe, completekey='tab', stdin=None, stdout=None):
    111          qdb.Frontend.__init__(self, pipe)
    112          self.clear_interaction()
    113
    114 -    def clear_interaction(self):
    115          self.filename = None
    116          self.lineno = None
    117          self.exception_info = None
    118          self.context = None
    119
    120      # redefine Frontend methods:
    121
    122 -    def run(self):
    123          run_lock.acquire()
    124          try:
    125              while self.pipe.poll():
    126                  qdb.Frontend.run(self)
    127          finally:
    128              run_lock.release()
    129
    130 -    def interaction(self, filename, lineno, line, **context):
    131          # store current status
    132          interact_lock.acquire()
    133          try:
    134              self.filename = filename
    135              self.lineno = lineno
    136              self.context = context
    137          finally:
    138              interact_lock.release()
    139
    140 -    def exception(self, title, extype, exvalue, trace, request):
    141          self.exception_info = {'title': title,
    142                                 'extype': extype, 'exvalue': exvalue,
    143                                 'trace': trace, 'request': request}
    144
    145      @check_interaction
    146 -    def do_continue(self):
    147          qdb.Frontend.do_continue(self)
    148
    149      @check_interaction
    150 -    def do_step(self):
    151          qdb.Frontend.do_step(self)
    152
    153      @check_interaction
    154 -    def do_return(self):
    155          qdb.Frontend.do_return(self)
    156
    157      @check_interaction
    158 -    def do_next(self):
    159          qdb.Frontend.do_next(self)
    160
    161      @check_interaction
    162 -    def do_quit(self):
    163          qdb.Frontend.do_quit(self)
    164
    165 -    def do_exec(self, statement):
    166          interact_lock.acquire()
    167          try:
    168              # check to see if we're inside interaction
    169              if self.filename:
    170                  # avoid spurious interaction notifications:
    171                  self.set_burst(2)
    172                  # execute the statement in the remote debugger:
    173                  return qdb.Frontend.do_exec(self, statement)
    174          finally:
    175              interact_lock.release()
    176
    177  # create the connection between threads:
    178
    179  parent_queue, child_queue = Queue.Queue(), Queue.Queue()
    180  front_conn = qdb.QueuePipe("parent", parent_queue, child_queue)
    181  child_conn = qdb.QueuePipe("child", child_queue, parent_queue)
    182
    183  web_debugger = WebDebugger(front_conn)                          # frontend
    184  qdb_debugger = qdb.Qdb(pipe=child_conn, redirect_stdio=False, skip=None)   # backend
    185  dbg = qdb_debugger
    186
    187  # enable getting context (stack, globals/locals) at interaction
    188  qdb_debugger.set_params(dict(call_stack=True, environment=True))
    189
    190  import gluon.main
    191  gluon.main.global_settings.debugging = True
    192