zope2/ScriptDebugging

changeset 1:84be5b9a417d

Initial revision
author lregebro
date Mon, 06 Sep 2004 15:16:41 +0000
parents 5bf8cf4f89f6
children 779b3eeb48f1 c4a57d74aa9e
files CHANGES HISTORY README.txt VERSION __init__.py refresh.txt
diffstat 6 files changed, 243 insertions(+), 0 deletions(-) [+]
line diff
     1.1 new file mode 100644
     1.2 --- /dev/null
     1.3 +++ b/CHANGES
     1.4 @@ -0,0 +1,1 @@
     1.5 +Initial Release
     1.6 \ No newline at end of file
     2.1 new file mode 100644
     3.1 new file mode 100644
     3.2 --- /dev/null
     3.3 +++ b/README.txt
     3.4 @@ -0,0 +1,30 @@
     3.5 +FSPythonScript Support for pdb
     3.6 +------------------------------
     3.7 +
     3.8 +This Product monkey patches Zope and to allow pdb and other pdb based 
     3.9 +debuggers (such as Wing IDE) to stop on breakpoints placed in script 
    3.10 +files stored on disk and to step into and through script file code.
    3.11 +
    3.12 +The Monkeypatches are based on Zope 2.7.2 and CMF 1.4.2, and may not
    3.13 +work with other versions (but they probably will).
    3.14 +
    3.15 +Unpatched Zope and derived code such as CMFCore and CMFFormController set
    3.16 +the co_filename attribute in the code objects produced from script files
    3.17 +to the string "Script (Python)", and strip meta data header comments from
    3.18 +the script file before compiling it, causing line number information in
    3.19 +the byte code to be incorrect. This prevents Wing's debugger (or any
    3.20 +other debugger) from determining where instructions in the code objects
    3.21 +come from, and thus makes it impossible to stop at breakpoints or step
    3.22 +through those files. This products fixes this. It does not create debugging
    3.23 +support for ZODB based Python scripts, but only for the file system based
    3.24 +FSPythonScripts.
    3.25 +
    3.26 +
    3.27 +This product is based on patches by Stephan Deibel (sdeibel@wingware.com),
    3.28 +and made into a product by Lennart Regebro.
    3.29 +
    3.30 +The original patches can be found here:
    3.31 +http://collector.zope.org/CMF/194
    3.32 +
    3.33 +For more information, please contact regebro@nuxeo.com, or support@wingide.com.
    3.34 +
     4.1 new file mode 100644
     4.2 --- /dev/null
     4.3 +++ b/VERSION
     4.4 @@ -0,0 +1,5 @@
     4.5 +# NUXEO PRODUCT CONFIGURATION FILE
     4.6 +# do not edit this file
     4.7 +PKG_NAME=ScriptDebugging
     4.8 +PKG_VERSION=0.0.0
     4.9 +PKG_RELEASE=0
     5.1 new file mode 100644
     5.2 --- /dev/null
     5.3 +++ b/__init__.py
     5.4 @@ -0,0 +1,206 @@
     5.5 +import sys, marshal
     5.6 +from zLOG import LOG, ERROR
     5.7 +
     5.8 +from Shared.DC.Scripts.Script import defaultBindings
     5.9 +
    5.10 +from Products.CMFCore.FSPythonScript import FSPythonScript
    5.11 +from Products.PythonScripts.PythonScript import PythonScript, Python_magic, \
    5.12 +    Script_magic, _nonempty_line, _first_indent
    5.13 +from Products.CMFCore.DirectoryView import expandpath
    5.14 +
    5.15 +from AccessControl import ModuleSecurityInfo
    5.16 +ModuleSecurityInfo('pdb').declarePublic('set_trace')
    5.17 +    
    5.18 +# Add support for storing a path to the file.
    5.19 +def __init__(self, id, filepath=None):
    5.20 +    self.id = id
    5.21 +    self._filepath = filepath
    5.22 +    self.ZBindings_edit(defaultBindings)
    5.23 +    self._makeFunction()
    5.24 +
    5.25 +PythonScript.__init__ = __init__
    5.26 +        
    5.27 +# If filepath exists, it is used instead of the meta_type.
    5.28 +# If not, the meta type is used as before.
    5.29 +def _compile(self):
    5.30 +    fp = getattr(self, '_filepath', self.meta_type)
    5.31 +    bind_names = self.getBindingAssignments().getAssignedNamesInOrder()
    5.32 +    r = self._compiler(self._params, self._body or 'pass',
    5.33 +                        self.id, fp, globalize=bind_names)
    5.34 +    code = r[0]
    5.35 +    errors = r[1]
    5.36 +    self.warnings = tuple(r[2])
    5.37 +    if errors:
    5.38 +        self._code = None
    5.39 +        self._v_ft = self._v_f = None
    5.40 +        self._setFuncSignature((), (), 0)
    5.41 +        # Fix up syntax errors.
    5.42 +        filestring = '  File "<string>",'
    5.43 +        for i in range(len(errors)):
    5.44 +            line = errors[i]
    5.45 +            if line.startswith(filestring):
    5.46 +                errors[i] = line.replace(filestring, '  Script', 1)
    5.47 +        self.errors = errors
    5.48 +        return
    5.49 +
    5.50 +    self._code = marshal.dumps(code)
    5.51 +    self.errors = ()
    5.52 +    f = self._newfun(code)
    5.53 +    fc = f.func_code
    5.54 +    self._setFuncSignature(f.func_defaults, fc.co_varnames,
    5.55 +                            fc.co_argcount)
    5.56 +    self.Python_magic = Python_magic
    5.57 +    self.Script_magic = Script_magic
    5.58 +    self._v_change = 0
    5.59 +
    5.60 +PythonScript._compile = _compile
    5.61 +
    5.62 +# The stripping of the header is moved from write() to the read() method.
    5.63 +def write(self, text):
    5.64 +    """ Change the Script by parsing a read()-style source text. """
    5.65 +    self._validateProxy()
    5.66 +    mdata = self._metadata_map()
    5.67 +    bindmap = self.getBindingAssignments().getAssignedNames()
    5.68 +    bup = 0
    5.69 +
    5.70 +    st = 0
    5.71 +    try:
    5.72 +        while 1:
    5.73 +            # Find the next non-empty line
    5.74 +            m = _nonempty_line.search(text, st)
    5.75 +            if not m:
    5.76 +                # There were no non-empty body lines
    5.77 +                body = ''
    5.78 +                break
    5.79 +            line = m.group(0).strip()
    5.80 +            if line[:2] != '##':
    5.81 +                # We have found the first line of the body
    5.82 +                body = text
    5.83 +                break
    5.84 +
    5.85 +            st = m.end(0)
    5.86 +            # Parse this header line
    5.87 +            if len(line) == 2 or line[2] == ' ' or '=' not in line:
    5.88 +                # Null header line
    5.89 +                continue
    5.90 +            k, v = line[2:].split('=', 1)
    5.91 +            k = k.strip().lower()
    5.92 +            v = v.strip()
    5.93 +            if not mdata.has_key(k):
    5.94 +                SyntaxError, 'Unrecognized header line "%s"' % line
    5.95 +            if v == mdata[k]:
    5.96 +                # Unchanged value
    5.97 +                continue
    5.98 +
    5.99 +            # Set metadata value
   5.100 +            if k == 'title':
   5.101 +                self.title = v
   5.102 +            elif k == 'parameters':
   5.103 +                self._params = v
   5.104 +            elif k[:5] == 'bind ':
   5.105 +                bindmap[_nice_bind_names[k[5:]]] = v
   5.106 +                bup = 1
   5.107 +
   5.108 +        body = body.rstrip()
   5.109 +        if body:
   5.110 +            body = body + '\n'
   5.111 +        if body != self._body:
   5.112 +            self._body = body
   5.113 +        if bup:
   5.114 +            self.ZBindings_edit(bindmap)
   5.115 +        else:
   5.116 +            self._makeFunction()
   5.117 +    except:
   5.118 +        LOG(self.meta_type, ERROR, 'write failed', error=sys.exc_info())
   5.119 +        raise
   5.120 +
   5.121 +PythonScript.write = write
   5.122 +
   5.123 +# The stripping of the header is moved from write() to the read() method.
   5.124 +def read(self):
   5.125 +    """ Generate a text representation of the Script source.
   5.126 +
   5.127 +    Includes specially formatted comment lines for parameters,
   5.128 +    bindings, and the title.
   5.129 +    """
   5.130 +    # Construct metadata header lines, indented the same as the body.
   5.131 +    m = _first_indent.search(self._body)
   5.132 +    if m: prefix = m.group(0) + '##'
   5.133 +    else: prefix = '##'
   5.134 +
   5.135 +    hlines = ['%s %s "%s"' % (prefix, self.meta_type, self.id)]
   5.136 +    mm = self._metadata_map().items()
   5.137 +    mm.sort()
   5.138 +    for kv in mm:
   5.139 +        hlines.append('%s=%s' % kv)
   5.140 +    if self.errors:
   5.141 +        hlines.append('')
   5.142 +        hlines.append(' Errors:')
   5.143 +        for line in self.errors:
   5.144 +            hlines.append('  ' + line)
   5.145 +    if self.warnings:
   5.146 +        hlines.append('')
   5.147 +        hlines.append(' Warnings:')
   5.148 +        for line in self.warnings:
   5.149 +            hlines.append('  ' + line)
   5.150 +    hlines.append('')
   5.151 +
   5.152 +    # Strip old header from existing body before adding new one
   5.153 +    st = 0
   5.154 +    while 1:
   5.155 +        # Find the next non-empty line
   5.156 +        m = _nonempty_line.search(self._body, st)
   5.157 +        if not m:
   5.158 +            # There were no non-empty body lines
   5.159 +            body = ''
   5.160 +            break
   5.161 +        line = m.group(0).strip()
   5.162 +        st = m.end(0)
   5.163 +        if line[:2] != '##':
   5.164 +            # We have found the first line of the body
   5.165 +            body = self._body[m.start(0):]
   5.166 +            break
   5.167 +    body = body.rstrip()
   5.168 +    if body:
   5.169 +        body = body + '\n'
   5.170 +
   5.171 +    return ('\n' + prefix).join(hlines) + '\n' + body
   5.172 +
   5.173 +PythonScript.read = read
   5.174 +
   5.175 +# Now includes the filepath
   5.176 +def _createZODBClone(self):
   5.177 +    """Create a ZODB (editable) equivalent of this object."""
   5.178 +    obj = PythonScript(self.getId(), expandpath(self._filepath))
   5.179 +    obj.write(self.read())
   5.180 +    return obj
   5.181 +
   5.182 +FSPythonScript._createZODBClone = _createZODBClone
   5.183 +
   5.184 +# Now includes the file path
   5.185 +def _write(self, text, compile):
   5.186 +    '''
   5.187 +    Parses the source, storing the body, params, title, bindings,
   5.188 +    and source in self.  If compile is set, compiles the
   5.189 +    function.
   5.190 +    '''
   5.191 +    ps = PythonScript(self.id, expandpath(self._filepath))
   5.192 +    ps.write(text)
   5.193 +    if compile:
   5.194 +        ps._makeFunction(1)
   5.195 +        self._v_f = f = ps._v_f
   5.196 +        if f is not None:
   5.197 +            self.func_code = f.func_code
   5.198 +            self.func_defaults = f.func_defaults
   5.199 +        else:
   5.200 +            # There were errors in the compile.
   5.201 +            # No signature.
   5.202 +            self.func_code = bad_func_code()
   5.203 +            self.func_defaults = None
   5.204 +    self._body = ps._body
   5.205 +    self._params = ps._params
   5.206 +    self.title = ps.title
   5.207 +    self._setupBindings(ps.getBindingAssignments().getAssignedNames())
   5.208 +    self._source = ps.read()  # Find out what the script sees.
   5.209 +
   5.210 +FSPythonScript._write =_write
     6.1 new file mode 100644
     6.2 --- /dev/null
     6.3 +++ b/refresh.txt
     6.4 @@ -0,0 +1,1 @@
     6.5 +