Source

django-environments / scripts / djenvlib

Full commit
#
# django-environments environment initialization script
#
# Instead of editing the file, it's better to set the following
# variable in your own initenv script or virtualenv bin/postactivate,
# and source this file (order does not matter).
#
# PROJECT_ROOT is the path to the root of the entire project, i.e.
# the directory containing the one or more 'Django projects'.
#
# PROJECT_ROOT=/Users/joe/projects/myproject

# Not to be called directly, see djenv below
function _djenv_init() {
    DJANGO_PROJECT=$1
    DJANGO_SETTINGS=$2

    # The root of the project should exist, of course
    [ -z "$PROJECT_ROOT" ] && \
        echo "Variable \$PROJECT_ROOT not set or empty" 1>&2 && return 1
    [ ! -d "$PROJECT_ROOT" ] && \
         echo "Variable \$PROJECT_ROOT does not point to a readable directory" 1>&2 && return 1

    # Check Django project as well
    [ -z "$DJANGO_PROJECT" ] && \
        echo "Argument \$DJANGO_PROJECT not set or empty" 1>&2 && return 1
    [ ! -d "$PROJECT_ROOT/$DJANGO_PROJECT" ] && \
        echo "Argument \$DJANGO_PROJECT does not identify a readable directory within $PROJECT_ROOT" 1>&2 && \
        return 1

    # If no particular settings are defined, just use the "base" settings
    _OLD_DJANGO_SETTINGS_MODULE=$DJANGO_SETTINGS_MODULE
    if [ ! -z "$DJANGO_SETTINGS" ]; then
        DJANGO_SETTINGS_MODULE=$DJANGO_PROJECT.$DJANGO_SETTINGS
    else
        DJANGO_SETTINGS_MODULE=$DJANGO_PROJECT.settings
    fi

    # Set the PYTHONPATH to include PROJECT_ROOT
    _OLD_PYTHONPATH=$PYTHONPATH
    PYTHONPATH=$PROJECT_ROOT:$PYTHONPATH

    export PROJECT_ROOT DJANGO_PROJECT PYTHONPATH DJANGO_SETTINGS_MODULE

    # Test settings import
    python -c "import $DJANGO_SETTINGS_MODULE" > /dev/null 2>&1
    if [ ! "$?" -eq 0 ]; then
        echo "Error importing settings $DJANGO_SETTINGS_MODULE (PYTHONPATH: $PYTHONPATH)" 1>&2
        python -c "import $DJANGO_SETTINGS_MODULE"
        return 1
    fi

    # Test if the definitions in the settings match ours

    if [ ! -z "`get_django_setting :`" ]; then
        echo "Current Django settings generate output - debugging print statements maybe?" 1>&2
    else
        [ "$PROJECT_ROOT" != "`get_django_setting PROJECT_ROOT`" ] && \
            echo "\$PROJECT_ROOT in Django settings is different from shell" 1>&2 && \
            echo "\$PROJECT_ROOT: $PROJECT_ROOT" 1>&2 && \
            echo "settings.PROJECT_ROOT: `get_django_setting PROJECT_ROOT`" 1>&2 && \
            return 1
        [ "$DJANGO_PROJECT" != "`get_django_setting DJANGO_PROJECT`" ] && \
            echo "\$DJANGO_PROJECT in Django settings is different from shell" 1>&2 && \
            return 1
        [ "$PROJECT_ROOT/$DJANGO_PROJECT" != "`get_django_setting DJANGO_PROJECT_DIR`" ] && \
            echo "\$DJANGO_PROJECT_DIR in Django settings is different from \$PROJECT_ROOT/\$DJANGO_PROJECT" 1>&2 && 
            return 1
    fi

    # We're good - let's set the prompt
    _OLD_DJENV_PS1=$PS1
    PS1=[$DJANGO_PROJECT]$PS1

    # Show environment info
    if [ ! -z "$SHOW_DJANGO_ENVIRONMENT" ]; then
        if [ "$SHOW_DJANGO_ENVIRONMENT" = "yes" ]; then
            echo Welcome to $DJANGO_PROJECT. Environment info:
            djenv
            echo
            if [ "$SHOW_DJANGO_ENVIRONMENT" = "settings" ]; then
                echo Django settings:
                PAGER=cat python -c "import $DJANGO_SETTINGS_MODULE; help($DJANGO_SETTINGS_MODULE)" | grep -v "^$"
                echo
            fi
        fi
    fi

    # Check non-critical settings
    local dirs dir
    for dirs in TEMPLATE_DIRS FIXTURE_DIRS; do
        eval `import_django_settings $dirs`
        envname=DJANGO_$dirs
        for dir in ${!envname}; do
            [ ! -d $dir ] && echo "Warning: \"$dir\" in settings.$dirs is not a valid directory"
        done
    done

    return 0
}

# Register cleanup hook
function _djenv_register_cleanup() {
    local func
    for func in $*; do
        if [ -z "$DJENV_CLEANUP_FUNCTIONS" ]; then
            DJENV_CLEANUP_FUNCTIONS=$func
        else
            DJENV_CLEANUP_FUNCTIONS=$DJENV_CLEANUP_FUNCTIONS:$func
        fi
    done
}

# Some useful functions

# Exit current project
function djexit() {
    [ -z "$DJANGO_PROJECT" ] && return 1

    # Restore prompt, django settings and python path
    PS1=$_OLD_DJENV_PS1

    if [ ! -z "$_OLD_DJANGO_SETTINGS_MODULE" ]; then
        DJANGO_SETTINGS_MODULE=$_OLD_DJANGO_SETTINGS_MODULE
    else
        unset DJANGO_SETTINGS_MODULE
    fi

    if [ ! -z "$_OLD_PYTHONPATH" ]; then
        PYTHONPATH=$_OLD_PYTHONPATH
    else
        unset PYTHONPATH
    fi

    # Call the registerd cleanup functions
    _IFS=$IFS
    IFS=:
    local func
    for func in $DJENV_CLEANUP_FUNCTIONS; do
        $func
    done
    IFS=$_IFS
}

# Change django project
# Example:
# djenv # Print current environment settings
# or
# djenv mysite # Use default settings
# or
# djenv mysite settings.env.local # Use specific settins
function djenv() {
    # Environment info
    if [ -z "$1" ]; then
        echo PROJECT_ROOT: \'$PROJECT_ROOT\'
        echo DJANGO_PROJECT: \'$DJANGO_PROJECT\'
        echo DJANGO_SETTINGS_MODULE: \'$DJANGO_SETTINGS_MODULE\'
        echo PYTHONPATH: \'$PYTHONPATH\'
        return
    fi

    # Help
    if [ "$1" = "-h" -o "$1" = "--help" ]; then
        echo "Usage: djenv [DJANGO_PROJECT [DJANGO_SETTINGS]]"
        return
    fi

    # Check $PROJECT_ROOT
    if [ -z "$PROJECT_ROOT" ]; then
        echo "Variable \$PROJECT_ROOT not set or empty" 1>&2
        return 1
    fi

    # Exit current environment (if any)
    djexit

    # Initialize
    _djenv_init $*

    # On error, use djexit for cleanup
    [ ! $? -eq 0 ] && djexit && return 1

    # Change working directory
    cdroot

    return 0
}

# Get a django setting from the current settings module
# Experimental.
#
# Example:
# LC=`get_django_setting LANGUAGE_CODE` && echo $LC
#
# Returns nothing if setting cannot be found. Be aware that the
# settings should not print anything to stdout for this to work.
function get_django_setting() {
    [ -z "$1" ] && echo "Usage: get_django_setting <setting-name>" 1>&2 && return 1

    python << EOF
import $DJANGO_SETTINGS_MODULE as settings

try:
    print settings.__dict__['$1']
except KeyError:
    pass
EOF
}

# Import django settings into the shell environment
# Experimental.
#
# When using, set $IFS to empty (this is needed because we eval
# the output of the python-generated shell script code):
# IFS=''
#
# Then use the function as follows:
# eval `import_django_settings` # all settings
# or
# eval `import_django_settings ADMIN` # all settings starting with 'ADMIN'
#
# Tuples and lists items are separated with newlines, so set $IFS
# to newline to get to those:
# IFS='
# '
# Note that all variables are prefixed with value of $prefix, 'DJANGO_'.
function import_django_settings() {
    prefix=DJANGO_
    python << EOF
import $DJANGO_SETTINGS_MODULE as settings
from types import TupleType, ListType, DictType

def escape(value):
    return str(value).replace('"', '\\\\"')

settings = settings.__dict__.items()
settings.sort()
for name, value in settings:
    if name.find('__') == -1 and name.find('$1') == 0:
        if type(value) in (TupleType, ListType):
            print '$prefix%s="' % name
            for item in value:
                print escape(item)
            print '"'
        elif type(value) == DictType:
            print '$prefix%s="' % name
            for name, value in value.items():
                print '%s:%s' % (name, escape(value))
            print '"'
        else:
            print '$prefix%s="%s"' % (name, escape(value))
EOF
}

# Change directory to project root
function cdroot() {
    [ -z "$PROJECT_ROOT" ] && echo "No \$PROJECT_ROOT" 1>&2 && return 1
    cd $PROJECT_ROOT/$1
}

# Install dependencies for a python environment (first argument);
# the file with the dependencies should be in the requirements
# directory and should have the format 'libs-<environment>.txt'
function pipup() {
    [ -z "$PROJECT_ROOT" ] && echo "No \$PROJECT_ROOT" 1>&2 && return 1
    [ -z "$1" ] && echo "Usage: pipup <requirements-identifier>" 1>&2 && return 1
    pip install --requirement=$PROJECT_ROOT/requirements/libs-$1.txt
}

# Use compileall to compile all .py files - handy for web server
# environments where the server user often has no write access to the
# .pyc files / directories
function pycompile() {
    [ -z "$PROJECT_ROOT" ] && echo "No \$PROJECT_ROOT" 1>&2 && return 1
    python -c "import compileall; compileall.compile_dir('$PROJECT_ROOT')"
    removeorphanpycs
}

# Remove .pyc files without a corresponding .py
function removeorphanpycs() {
    [ -z "$PROJECT_ROOT" ] && echo "No \$PROJECT_ROOT" 1>&2 && return 1
    local pyc
    for pyc in `find $PROJECT_ROOT -name \*.pyc`; do
        [ -f  `echo $pyc | sed 's/c$//'` ] || rm -v $pyc
    done
}

# Change directory to Django project
function cdjango () {
    [ -z "$PROJECT_ROOT" ] && echo "No \$PROJECT_ROOT" 1>&2 && return 1
    [ -z "$DJANGO_PROJECT" ] && echo "No \$DJANGO_PROJECT" 1>&2 && return 1
    cd $PROJECT_ROOT/$DJANGO_PROJECT/$1
}

# Forget manage.py, django-admin.py respects our settings!
function djadmin() {
    [ -z "$PROJECT_ROOT" ] && echo "No \$PROJECT_ROOT" 1>&2 && return 1
    [ -z "$DJANGO_PROJECT" ] && echo "No \$DJANGO_PROJECT" 1>&2 && return 1
    django-admin.py $*
}

# Run development server on settings.LOCAL_SERVER_PORT
function runserver() {
    [ -z "$PROJECT_ROOT" ] && echo "No \$PROJECT_ROOT" 1>&2 && return 1
    [ -z "$DJANGO_PROJECT" ] && echo "No \$DJANGO_PROJECT" 1>&2 && return 1
    django-admin.py runserver `get_django_setting LOCAL_SERVER_PORT`
}

# Export functions that are not solely intended for interactive use
export -f get_django_setting import_django_settings

# Clean up the environment
function _djenv_cleanup () {
    unset DJANGO_PROJECT DJANGO_SETTINGS \
        _OLD_DJANGO_SETTINGS_MODULE _OLD_PYTHONPATH \
        DJANGO_TEMPLATE_DIRS DJANGO_FIXTURE_DIRS
}

# Use _djenv_register_cleanup to register your own cleanup functions
_djenv_register_cleanup _djenv_cleanup

#
# Tab completion
#

function show_django_projects() {
    [ -z "$PROJECT_ROOT" ] && echo "No \$PROJECT_ROOT" 1>&2 && return 1
    (cd $PROJECT_ROOT; for f in */settings/__init__.py; do echo $f; done) | \
        sed 's#/settings/__init__.py##' | sort
}

function show_django_settings() {
    [ -z "$PROJECT_ROOT" ] && echo "No \$PROJECT_ROOT" 1>&2 && return 1
    (cd $PROJECT_ROOT/$1; for f in settings/env/*.py; do echo $f; done) | \
        grep -v '/__init__.py$' | sed 's#settings/env/#settings.env.#' | \
        sed 's#\.py$##' | sort
}

_django_projects_complete() {
    local current="${COMP_WORDS[COMP_CWORD]}"
    local previous="${COMP_WORDS[COMP_CWORD - 1]}"

    if [ $COMP_CWORD -eq 1 ]; then
        COMPREPLY=($(compgen -W "`show_django_projects`" -- ${current}))
    elif [ $COMP_CWORD -eq 2 ]; then
        COMPREPLY=( $(compgen -W "`show_django_settings $previous`" -- ${current}) )
    fi
}

_cdroot_complete() {
    [ -z "$PROJECT_ROOT" ] && echo "No \$PROJECT_ROOT" 1>&2 && return 1
    COMPREPLY=($(cdroot && compgen -d -- "${2}" ))
}

_cdjango_complete() {
    [ -z "$DJANGO_PROJECT" ] && echo "No \$DJANGO_PROJECT" 1>&2 && return 1
    COMPREPLY=($(cdjango && compgen -d -- "${2}" ))
}

function show_pip_environments() {
    [ -z "$PROJECT_ROOT" ] && echo "No \$PROJECT_ROOT" 1>&2 && return 1
    (cd $PROJECT_ROOT/requirements; for f in libs-*.txt; do echo $f; done) | \
        sed 's#libs-##' | sed 's#.txt##' | sort
}

_pipup_complete() {
    [ -z "$PROJECT_ROOT" ] && echo "No \$PROJECT_ROOT" 1>&2 && return 1
    COMPREPLY=($(compgen -W "`show_pip_environments`" -- "${2}" ))
}

complete -o nospace -F _cdroot_complete -S/ cdroot
complete -o nospace -F _cdjango_complete -S/ cdjango
complete -F _django_projects_complete djenv
complete -F _pipup_complete pipup