Saturday morning hack: Automatically import your app's models when starting IPython

Saturday morning hacks

I use Flask and peewee for all my personal projects, and wanted an easy way to automatically open an IPython shell with all my project's models in the namespace. If you've used the excellent django-extensions project, you may be familiar with the shell_plus command, which does the same thing. If your project has multiple models spread across several modules, this kind of hack can save you a lot of keystrokes.

Normally my workflow would look something like this:

$ ipython

In [1]: from blog.entries.blueprint import Entry, Comment

In [2]: from blog.models import Page

In [3]: # do something with these models.

Typing these import statements over and over gets pretty tedious. So here is the script I wrote that does it for me. It walks the directory finding non-executable Python files and automatically imports them, loading any Model subclasses into the namespace:

#!/usr/bin/env python

from importlib import import_module
import inspect
import os
from stat import ST_MODE
import sys

from IPython import embed
from peewee import Model


def main(site):
    namespace = {}

    for dirname, subdirs, filenames in os.walk(site or '.'):
        # Not a package, so skip this directory.
        if '__init__.py' not in filenames:
            continue

        # Create a dotted path representation of this directory.
        module_base = dirname.strip('.').strip('/').replace('/', '.')

        # Iterate over all the python files in this directory searching for
        # model subclasses.
        for filename in filter(lambda s: s.endswith('.py'), filenames):
            perms = os.stat(os.path.join(dirname, filename))[ST_MODE]

            # Skip over any executable Python scripts.
            if perms & 0100:
                continue

            # If this is the `__init__` in the root directory, skip over.
            if not module_base and filename == '__init__.py':
                continue

            if filename == '__init__.py':
                module_name = module_base
            elif module_base:
                module_name = '.'.join((module_base, filename[:-3]))
            else:
                module_name = filename[:-3]

            module = import_module(module_name)

            for name in dir(module):
                obj = getattr(module, name)
                if inspect.isclass(obj) and issubclass(obj, Model):
                    namespace[obj.__name__] = obj

    # Start up IPython with all the models in the namespace.
    embed(user_ns=namespace)

if __name__ == '__main__':
    if len(sys.argv) == 2:
        site = sys.argv[1]
    else:
        site = ''
    main(site)

I've put this script on my PATH so whenever I'm working on a Flask project, I can just run pyshell and everything just works!

Thanks for reading

Thanks for taking the time to read this post, I hope you found it helpful. As always I would appreciate any suggestions on how to improve this script -- feel free to leave a comment below.

If you're interested in more projects like this, check out the saturday-morning hack posts.

Comments (0)


Commenting has been closed.