django-environments / bin / djenvlib

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
#
# django-environments 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/spanky/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
}

# Verification functions

# Check if $PROJECT_ROOT is set
function _verify_project_root() {
    [ -z "$PROJECT_ROOT" ] && echo "No \$PROJECT_ROOT" 1>&2 && return 1
    return 0
}

# Check if $DJANGO_PROJECT is set
function _verify_django_project() {
    [ -z "$DJANGO_PROJECT" ] && echo "No \$DJANGO_PROJECT" 1>&2 && return 1
    return 0
}

# Check if both $PROJECT_ROOT and $DJANGO_PROJECT are set
function _verify_project_root_and_django_project() {
    _verify_project_root || return 1
    _verify_django_project || return 1
    return 0
}

# Check if $DJANGO_SETTINGS_MODULE is set
function _verify_django_settings_module() {
    [ -z "$DJANGO_SETTINGS_MODULE" ] && echo "No \$DJANGO_SETTINGS_MODULE" 1>&2 && return 1
    return 0
}

# Some useful functions

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

    # Restore prompt
    if [ ! -z "$_OLD_DJENV_PS1" ]; then
        PS1=$_OLD_DJENV_PS1
        unset _OLD_DJENV_PS1
    fi

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

    # Restore python path
    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
    cdjango

    return 0
}

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

    local settings_module
    if [ ! -z "$3" ]; then
        settings_module=$3
    else
        _verify_django_settings_module || return 1
        settings_module=$DJANGO_SETTINGS_MODULE
    fi
    python << EOF
try:
    import $settings_module as settings
except ImportError:
    print '$2'
    raise
else:
    try:
        print settings.__dict__['$1']
    except KeyError:
        print '$2'
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() {
    _verify_project_root || return 1
    _verify_django_settings_module || return 1

    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() {
    _verify_project_root || 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() {
    _verify_project_root || return 1
    [ -z "$1" ] && echo "Usage: pipup <requirements-identifier> [pip options]" 1>&2 && return 1

    local requirements=$1
    shift
    pip install --requirement=$PROJECT_ROOT/requirements/libs-$requirements.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() {
    _verify_project_root || return 1

    python -c "import compileall; compileall.compile_dir('$PROJECT_ROOT')"
    removeorphanpycs
}

# Remove .pyc files without a corresponding .py the fast way
function removeorphanpycs() {
    _verify_project_root || return 1

    # Use the GNU extension --no-run-if-empty on Linux, in case no files are found
    if [ `uname` = Linux ]; then
        extra_xargs_args=--no-run-if-empty
    fi

    # Pipe through bash because this implementation uses process substitution,
    # and you don't want to export functions using that since /bin/sh will choke
    # on them with a syntax error because process substitution is not available in
    # POSIX mode
    echo "diff --old-line-format= --new-line-format=%L --unchanged-group-format= \
        <(find -H $PROJECT_ROOT -name \*.py | sort) \
        <(find -H $PROJECT_ROOT -name \*.pyc | sed 's/c$//' | sort) | \
        sed 's/$/c/' | xargs $extra_xargs_args rm -v" | bash
}

# Remove empty directories
function removeemptydirs() {
    _verify_project_root || return 1

    removeorphanpycs # Make directories as empty as possible
    find $PROJECT_ROOT -type d -empty -delete
}

# Change directory to Django project
function cdjango () {
    _verify_project_root_and_django_project || 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
    django-admin.py $*
}

# Run development server on settings.LOCAL_SERVER_PORT, and restart automatically
function runserver() {
    _verify_project_root_and_django_project || return 1

    local port=`get_django_setting LOCAL_SERVER_PORT 8000`
    while true; do
        django-admin.py runserver $port $*
        echo "Server exited - restarting" 1>&2
        sleep 1
    done
}

# Run test server on settings.LOCAL_SERVER_PORT, and restart automatically
function testserver() {
    _verify_project_root_and_django_project || return 1

    local port=`get_django_setting LOCAL_SERVER_PORT 8000`
    django-admin.py testserver --addrport=$port $*
}

# Open an URL in the default browser
function open_url() {
    if which -s xdg-open; then
        # Linux
        xdg-open "$@"
    else
        # Mac OS
        open "$@"
    fi
}

# Points the browser to the server running the current settings:
# http://localhost:<settings.LOCAL_SERVER_PORT>/$1
function djbrowse() {
    _verify_project_root_and_django_project || return 1
    _verify_django_settings_module || return 1

    local port=`get_django_setting LOCAL_SERVER_PORT 8000`
    open_url http://localhost:$port/$1
}

# Points the browser to the named virtual host for the current
# settings. Assumes Apache is running as reverse proxy; see
# create_apache_vhoste_conf.sh for more information.
function djvirtualbrowse() {
    _verify_project_root_and_django_project || return 1
    _verify_django_settings_module || return 1

    local domain=local
    [ ! -z "$DOMAIN" ] && domain=$DOMAIN
    local django_settings_id=`echo $DJANGO_SETTINGS_MODULE | sed "s#.*\\.##"`

    open_url http://$django_settings_id.$DJANGO_PROJECT.$domain/$1
}

# Export functions so they can be used in shell scripts
export -f _verify_project_root _verify_django_project \
    _verify_project_root_and_django_project _verify_django_settings_module \
    get_django_setting import_django_settings cdroot pipup \
    pycompile removeorphanpycs removeemptydirs cdjango \
    djadmin runserver testserver djbrowse

# 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() {
    _verify_project_root || 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() {
    _verify_project_root || 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() {
    _verify_project_root || return 1

    COMPREPLY=($(cdroot && compgen -d -- "${2}" ))
}

_cdjango_complete() {
    _verify_django_project || return 1

    COMPREPLY=($(cdjango && compgen -d -- "${2}" ))
}

function show_pip_environments() {
    _verify_project_root || return 1

    (cd $PROJECT_ROOT/requirements; for f in libs-*.txt; do echo $f; done) | \
        sed 's#libs-##' | sed 's#.txt##' | sort
}

_pipup_complete() {
    _verify_project_root || 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
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.