[Checkins] SVN: grokcore.startup/trunk/ svn merge ipython-debug-shell

Jan Wijbrand Kolman cvs-admin at zope.org
Tue May 1 14:27:43 UTC 2012


Log message for revision 125497:
  svn merge ipython-debug-shell

Changed:
  U   grokcore.startup/trunk/CHANGES.txt
  U   grokcore.startup/trunk/buildout.cfg
  U   grokcore.startup/trunk/setup.py
  U   grokcore.startup/trunk/src/grokcore/startup/README.txt
  U   grokcore.startup/trunk/src/grokcore/startup/__init__.py
  A   grokcore.startup/trunk/src/grokcore/startup/debug.py
  U   grokcore.startup/trunk/src/grokcore/startup/startup.py

-=-
Modified: grokcore.startup/trunk/CHANGES.txt
===================================================================
--- grokcore.startup/trunk/CHANGES.txt	2012-05-01 14:23:02 UTC (rev 125496)
+++ grokcore.startup/trunk/CHANGES.txt	2012-05-01 14:27:40 UTC (rev 125497)
@@ -4,9 +4,10 @@
 1.2 (unreleased)
 ================
 
-- Nothing changed yet.
+- Added new IPython-based interactive debugger which is used
+  automatically when IPython is available. Otherwise the gdb-style
+  debugger is provided.
 
-
 1.1 (2010-10-26)
 ================
 

Modified: grokcore.startup/trunk/buildout.cfg
===================================================================
--- grokcore.startup/trunk/buildout.cfg	2012-05-01 14:23:02 UTC (rev 125496)
+++ grokcore.startup/trunk/buildout.cfg	2012-05-01 14:27:40 UTC (rev 125497)
@@ -17,5 +17,5 @@
 
 [test]
 recipe = zc.recipe.testrunner
-eggs = grokcore.startup
+eggs = grokcore.startup [test]
 defaults = ['--tests-pattern', '^f?tests$', '-v']

Modified: grokcore.startup/trunk/setup.py
===================================================================
--- grokcore.startup/trunk/setup.py	2012-05-01 14:23:02 UTC (rev 125496)
+++ grokcore.startup/trunk/setup.py	2012-05-01 14:27:40 UTC (rev 125497)
@@ -18,8 +18,13 @@
     'zope.interface',
     'zope.testing',
     'zope.security',
+    'zope.securitypolicy',
     ]
 
+debug_requires = [
+    'IPython',
+    ]
+
 setup(
     name='grokcore.startup',
     version='1.2dev',
@@ -50,7 +55,7 @@
                       'zope.app.debug',
                       ],
     tests_require = tests_require,
-    extras_require = dict(test=tests_require),
+    extras_require = dict(test=tests_require, debug=debug_requires),
     entry_points={
         'paste.app_factory': [
             'main = grokcore.startup:application_factory',

Modified: grokcore.startup/trunk/src/grokcore/startup/README.txt
===================================================================
--- grokcore.startup/trunk/src/grokcore/startup/README.txt	2012-05-01 14:23:02 UTC (rev 125496)
+++ grokcore.startup/trunk/src/grokcore/startup/README.txt	2012-05-01 14:27:40 UTC (rev 125497)
@@ -260,11 +260,34 @@
     >>> import shutil
     >>> shutil.rmtree(temp_dir)
 
-``interactive_debug_prompt(zope_conf_path)``
+``get_debugger(zope_conf_path)``
 --------------------------------------------
 
   Get an interactive console with a debugging shell started.
 
+  `grokcore.startup` provides two different debuggers currently: a
+  plain one based on `zope.app.debug` and a more powerful `IPython`_
+  debugger. The IPython debugger is automatically enabled if you have
+  IPython available in the environment.
+
+  You can explicitly enable the IPython_ debugger by stating::
+
+    grokcore.startup [debug]
+
+  in the install requirements of your `setup.py`, probably adding only
+  ``[debug]`` to an already existing entry for
+  `grokcore.startup`. Don't forget to rerun `buildout` afterwards.
+
+  You can explicitly require one or the other debugger by calling::
+
+    grokcore.startup.startup.interactive_debug_prompt(zope_conf)
+
+  or::
+
+    grokcore.startup.debug.ipython_debug_prompt(zope_conf)
+
+  in the ``[interactive_debugger]`` section of your ``buildout.cfg``.
+
     >>> import zope.app.appsetup.appsetup
     >>> # Ugh - allow a reconfiguration of an app.
     >>> zope.app.appsetup.appsetup._configured = False
@@ -313,7 +336,7 @@
     ... pprint(__file__)
     ... pprint(__name__)""")
     >>>
-    >>> sys.argv = ['interactive_debug_prompt', script]
+    >>> sys.argv = ['get_debugger', script]
     >>> from grokcore.startup import interactive_debug_prompt
     >>> try:
     ...     interactive_debug_prompt(zope_conf=zopeconf)
@@ -347,3 +370,4 @@
 .. _WSGI: http://www.wsgi.org/wsgi/
 .. _WSGIPublisherApplication: http://apidoc.zope.org/++apidoc++/Code/zope/app/wsgi/WSGIPublisherApplication/index.html
 .. _zc.buildout: http://pypi.python.org/pypi/zc.buildout
+.. _ipython: http://ipython.org/

Modified: grokcore.startup/trunk/src/grokcore/startup/__init__.py
===================================================================
--- grokcore.startup/trunk/src/grokcore/startup/__init__.py	2012-05-01 14:23:02 UTC (rev 125496)
+++ grokcore.startup/trunk/src/grokcore/startup/__init__.py	2012-05-01 14:27:40 UTC (rev 125497)
@@ -12,6 +12,8 @@
 #
 ##############################################################################
 # Make this a package.
-from grokcore.startup.startup import (application_factory,
-                                      debug_application_factory,
-                                      interactive_debug_prompt)
+from grokcore.startup.startup import (
+    application_factory,
+    debug_application_factory,
+    interactive_debug_prompt,
+    )

Copied: grokcore.startup/trunk/src/grokcore/startup/debug.py (from rev 125496, grokcore.startup/branches/ipython-debug-shell/src/grokcore/startup/debug.py)
===================================================================
--- grokcore.startup/trunk/src/grokcore/startup/debug.py	                        (rev 0)
+++ grokcore.startup/trunk/src/grokcore/startup/debug.py	2012-05-01 14:27:40 UTC (rev 125497)
@@ -0,0 +1,209 @@
+##############################################################################
+#
+# Copyright (c) 2006-2012 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+import sys
+import os.path
+import textwrap
+
+import transaction
+import zope.app.wsgi
+import zope.app.debug
+from pprint import pprint
+from zope.securitypolicy.zopepolicy import settingsForObject
+from zope.component import getUtility, getMultiAdapter
+
+from IPython.frontend.terminal.embed import InteractiveShellEmbed
+shell = InteractiveShellEmbed()
+
+
+PATH_SEP = '/'
+
+class GrokDebug(object):
+
+    def __init__(self, zope_conf='parts/etc/zope.debug.conf'):
+        db = zope.app.wsgi.config(zope_conf)
+        debugger = zope.app.debug.Debugger.fromDatabase(db)
+        self.app = debugger
+        self.root = debugger.root()
+        self.context = self.root
+
+    def get_start_context(self, path):
+        if path.startswith(PATH_SEP):
+            context = self.root
+        else:
+            # relative path
+            context = self.context
+        return context
+
+    def _get_object_names(self, context):
+        return [obj.__name__ for obj in context.values()]
+
+    def ns(self):
+        """Return namespace dictionary.
+
+        To be used for updating namespace of commands available
+        for user in shell.
+        """
+        return dict(lsg=self.ls,
+                    cdg=self.cd,
+                    pwdg=self.pwd,
+                    app=self.app,
+                    root=self.root,
+                    ctx=self.ctx,
+                    sec=self.get_security_settings,
+                    gu=getUtility,
+                    gma=getMultiAdapter,
+                    sync=self.sync,
+                    pby=self.providedBy,
+                    commit=transaction.commit)
+
+    def update_ns(self):
+        shell.user_ns.update(self.ns())
+
+    def get_security_settings(self, path):
+        pprint(settingsForObject(get_context_by_path(
+                    self.get_start_context(path), path)))
+
+    def sync(self):
+        self.root._p_jar.sync()
+
+    def ls(self, path=None):
+        """List objects.
+
+        This command is bound to `lsg` in IPython shell.
+
+        Without `path` parameter list objects in current container,
+        which is available as `ctx` from IPython shell.
+
+        `path` can be relative or absolute.
+
+        To use autocompletion of path command should be invoked
+        with prepended semicolon in ipython shell as
+        ;lsg /path
+        """
+        if path is None:
+            return self._get_object_names(self.context)
+
+        context = get_context_by_path(self.get_start_context(path), path)
+        return self._get_object_names(context)
+
+
+    def cd(self, path):
+        """cd to specified path.
+
+        Bound to `cdg` in IPython shell.
+        `path` can be relative or absolute.
+
+        To use autocompletion of path command should be invoked
+        with prepended semicolon in ipython shell as
+        ;cdg /path
+        """
+        if path.strip() == '..':
+            self.context = self.context.__parent__
+            self.update_ns()
+            return self.pwd
+
+        # cd
+        self.context = get_context_by_path(self.get_start_context(path), path)
+        self.update_ns()
+        return self.pwd
+
+    @property
+    def pwd(self):
+        """Print absolute path to current context object.
+
+        Bound to `pwdg` in IPython shell
+        """
+        res = []
+        obj = self.context
+        while obj is not None:
+            name = obj.__name__
+            if name is not None and name:
+                res.append(name)
+            obj = obj.__parent__
+
+        if not res:
+            return PATH_SEP
+
+        res = PATH_SEP.join(reversed(res))
+        if not res.startswith(PATH_SEP):
+            return PATH_SEP + res
+
+        return res
+
+    @property
+    def ctx(self):
+        """Return current context object.
+
+        Bound to `ctx` in IPython shell
+        """
+        return self.context
+
+    def providedBy(self, obj=None):
+        if not obj:
+            obj = self.ctx
+        return list(zope.interface.providedBy(obj))
+
+def get_context_by_path(context, path):
+    for name in (p for p in path.split(PATH_SEP) if p):
+        context = context[name]
+    return context
+
+def path_completer(self, event):
+    """TAB path completer for `cdg` and `lsg` commands."""
+    relpath = event.symbol
+
+    context = grokd.get_start_context(relpath)
+
+    # ends with '/'
+    if relpath.endswith(PATH_SEP):
+        context = get_context_by_path(context, relpath)
+        return [relpath+obj.__name__ for obj in context.values()]
+
+    head, tail = os.path.split(relpath)
+    if head and not head.endswith(PATH_SEP):
+        head += PATH_SEP
+    context = get_context_by_path(context, head)
+
+    return [head+obj.__name__ for obj in context.values()
+            if obj.__name__.startswith(tail)]
+
+
+def ipython_debug_prompt(zope_conf):
+    grokd = GrokDebug(zope_conf)
+    banner = textwrap.dedent(
+        """\
+        IPython shell for Grok.
+
+        Bound object names:
+        -------------------
+          root
+          ctx
+
+        Bound command names:
+        --------------------
+          cdg / ;cdg
+          lsg / ;lsg
+          sec / ;sec
+          gu  / ;gu
+          gma / ;gma
+          pby (providedBy)
+          pwdg
+          sync
+          commit
+        """)
+
+    shell.user_ns.update(grokd.ns())
+    shell.banner2=banner
+    shell.set_hook('complete_command', path_completer, re_key='.*cdg|.*lsg')
+    shell(local_ns=grokd.ns())

Modified: grokcore.startup/trunk/src/grokcore/startup/startup.py
===================================================================
--- grokcore.startup/trunk/src/grokcore/startup/startup.py	2012-05-01 14:23:02 UTC (rev 125496)
+++ grokcore.startup/trunk/src/grokcore/startup/startup.py	2012-05-01 14:27:40 UTC (rev 125497)
@@ -32,9 +32,7 @@
     # Return the created application
     return app
 
-def interactive_debug_prompt(
-    zope_conf=os.path.join('parts', 'etc', 'zope.conf')):
-
+def _classic_debug_prompt(zope_conf):
     db = zope.app.wsgi.config(zope_conf)
     debugger = zope.app.debug.Debugger.fromDatabase(db)
     globals_ = {
@@ -67,3 +65,14 @@
         "The 'app' variable contains the Debugger, 'app.publish(path)' "
         "simulates a request.")
     code.interact(banner=banner, local=globals_)
+
+def _ipython_debug_prompt(zope_conf):
+    from grokcore.startup.debug import ipython_debug_prompt
+    return ipython_debug_prompt(zope_conf)
+
+def interactive_debug_prompt(zope_conf):
+    try:
+        import IPython
+    except ImportError:
+        return _classic_debug_prompt(zope_conf)
+    return _ipython_debug_prompt(zope_conf)



More information about the checkins mailing list