Using bpython with django – another way

par Alexis Métaireau

Lakshman wrotes up a nice article introducing bpython, and gives some tips to integrate it with django.

Unfortunatly, the tip laksman proposed can’t use additional parameters. Here is another way to use bpython, with django. It makes somes changes into the files of the framework (that some people — myself included — calls an ugly hack, but, it works the right way).

To run django shell with bpython, just enter

django shell --bpython

And you’re done.

Here is the patch:

Index: shell.py
===================================================================
--- shell.py    (révision 11821)
+++ shell.py    (copie de travail)
@@ -6,8 +6,12 @@
     option_list = NoArgsCommand.option_list + (
         make_option('--plain', action='store_true', dest='plain',
             help='Tells Django to use plain Python, not IPython.'),
+        make_option('--bpython', action='store_true', dest='bpython',
+            help='Tells Django to use the bpython interpreter, not IPython.'),    
     )
-    help = "Runs a Python interactive interpreter. Tries to use IPython, if it's available."
+    help = """Runs a Python interactive interpreter. Tries to use IPython, if
+    it's available. Can use bpython with the --bpython option
+    """
 
     requires_model_validation = False
 
@@ -18,16 +22,22 @@
         loaded_models = get_models()
 
         use_plain = options.get('plain', False)
+        use_bpython = options.get('bpython', False)
 
         try:
             if use_plain:
                 # Don't bother loading IPython, because the user wants plain Python.
                 raise ImportError
-            import IPython
-            # Explicitly pass an empty list as arguments, because otherwise IPython
-            # would use sys.argv from this script.
-            shell = IPython.Shell.IPShell(argv=[])
-            shell.mainloop()
+            elif use_bpython:
+                # The user wants bpython
+                from bpython.cli import main
+                main(args=[])
+            else:
+                import IPython
+                # Explicitly pass an empty list as arguments, because otherwise IPython
+                # would use sys.argv from this script.
+                shell = IPython.Shell.IPShell(argv=[])
+                shell.mainloop()
         except ImportError:
             import code
             # Set up a dictionary to serve as the environment for the shell, so

And the entire file (for django 1.1.1 and trunk):

import os
from django.core.management.base import NoArgsCommand
from optparse import make_option

class Command(NoArgsCommand):
    option_list = NoArgsCommand.option_list + (
        make_option('--plain', action='store_true', dest='plain',
            help='Tells Django to use plain Python, not IPython.'),
        make_option('--bpython', action='store_true', dest='bpython',
            help='Tells Django to use the bpython interpreter, not IPython.'),    
    )
    help = """Runs a Python interactive interpreter. Tries to use IPython, if
    it's available. Can use bpython with the --bpython option
    """


    requires_model_validation = False

    def handle_noargs(self, **options):
        # XXX: (Temporary) workaround for ticket #1796: force early loading of all
        # models from installed apps.
        from django.db.models.loading import get_models
        loaded_models = get_models()

        use_plain = options.get('plain', False)
        use_bpython = options.get('bpython', False)

        try:
            if use_plain:
                # Don't bother loading IPython, because the user wants plain Python.
                raise ImportError
            elif use_bpython:
                # The user wants bpython
                from bpython.cli import main
                main(args=[])
            else:
                import IPython
                # Explicitly pass an empty list as arguments, because otherwise IPython
                # would use sys.argv from this script.
                shell = IPython.Shell.IPShell(argv=[])
                shell.mainloop()
        except ImportError:
            import code
            # Set up a dictionary to serve as the environment for the shell, so
            # that tab completion works on objects that are imported at runtime.
            # See ticket 5082.
            imported_objects = {}
            try: # Try activating rlcompleter, because it's handy.
                import readline
            except ImportError:
                pass
            else:
                # We don't have to wrap the following import in a 'try', because
                # we already know 'readline' was imported successfully.
                import rlcompleter
                readline.set_completer(rlcompleter.Completer(imported_objects).complete)
                readline.parse_and_bind("tab:complete")

            # We want to honor both $PYTHONSTARTUP and .pythonrc.py, so follow system
            # conventions and get $PYTHONSTARTUP first then import user.
            if not use_plain:
                pythonrc = os.environ.get("PYTHONSTARTUP")
                if pythonrc and os.path.isfile(pythonrc):
                    try:
                        execfile(pythonrc)
                    except NameError:
                        pass
                # This will import .pythonrc.py as a side-effect
                import user
            code.interact(local=imported_objects)

Hope it helps