hook_loader.py 6.46 KB
Newer Older
1 2 3 4 5 6 7
# encoding: utf-8
#
# Copyright (c) 2010 Doug Hellmann.  All rights reserved.
#
"""Load hooks for virtualenvwrapper.
"""

8
import inspect
9
import itertools
10
import logging
11
import logging.handlers
12 13
import optparse
import os
14
import sys
15

16 17 18 19
from stevedore import ExtensionManager
from stevedore import NamedExtensionManager

LOG_FORMAT = '%(asctime)s %(levelname)s %(name)s %(message)s'
20

21

22
class GroupWriteRotatingFileHandler(logging.handlers.RotatingFileHandler):
23
    """Taken from http://stackoverflow.com/questions/1407474
24 25 26 27 28 29 30
    """
    def _open(self):
        prevumask = os.umask(0o002)
        rtv = logging.handlers.RotatingFileHandler._open(self)
        os.umask(prevumask)
        return rtv

31

32 33 34 35 36
def main():
    parser = optparse.OptionParser(
        usage='usage: %prog [options] <hook> [<arguments>]',
        prog='virtualenvwrapper.hook_loader',
        description='Manage hooks for virtualenvwrapper',
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
    )

    parser.add_option(
        '-S', '--script',
        help='Runs "hook" then "<hook>_source", writing the ' +
        'result to <file>',
        dest='script_filename',
        default=None,
    )
    parser.add_option(
        '-s', '--source',
        help='Print the shell commands to be run in the current shell',
        action='store_true',
        dest='sourcing',
        default=False,
    )
    parser.add_option(
        '-l', '--list',
        help='Print a list of the plugins available for the given hook',
        action='store_true',
        default=False,
        dest='listing',
    )
    parser.add_option(
        '-v', '--verbose',
        help='Show more information on the console',
        action='store_const',
        const=2,
        default=1,
        dest='verbose_level',
    )
    parser.add_option(
        '-q', '--quiet',
        help='Show less information on the console',
        action='store_const',
        const=0,
        dest='verbose_level',
    )
    parser.add_option(
        '-n', '--name',
        help='Only run the hook from the named plugin',
        action='append',
        dest='names',
        default=[],
    )
    parser.disable_interspersed_args()  # stop when on option without an '-'
83 84
    options, args = parser.parse_args()

85
    root_logger = logging.getLogger('virtualenvwrapper')
86 87

    # Set up logging to a file
88 89 90 91 92 93 94
    logfile = os.environ.get('VIRTUALENVWRAPPER_LOG_FILE')
    if logfile:
        root_logger.setLevel(logging.DEBUG)
        file_handler = GroupWriteRotatingFileHandler(
            logfile,
            maxBytes=10240,
            backupCount=1,
95
        )
96 97 98
        formatter = logging.Formatter(LOG_FORMAT)
        file_handler.setFormatter(formatter)
        root_logger.addHandler(file_handler)
99 100

    # Send higher-level messages to the console, too
101
    console = logging.StreamHandler(sys.stderr)
102 103 104 105
    console_level = [logging.WARNING,
                     logging.INFO,
                     logging.DEBUG,
                     ][options.verbose_level]
106 107 108
    console.setLevel(console_level)
    formatter = logging.Formatter('%(name)s %(message)s')
    console.setFormatter(formatter)
109
    root_logger.addHandler(console)
110
    root_logger.setLevel(console_level)
111

112
    # logging.getLogger(__name__).debug('cli args %s', args)
113 114 115

    # Determine which hook we're running
    if not args:
116 117 118 119 120
        if options.listing:
            list_hooks()
            return 0
        else:
            parser.error('Please specify the hook to run')
121
    hook = args[0]
122 123 124 125

    if options.sourcing and options.script_filename:
        parser.error('--source and --script are mutually exclusive.')

126 127 128
    if options.sourcing:
        hook += '_source'

129
    log = logging.getLogger('virtualenvwrapper.hook_loader')
130 131 132 133 134

    log.debug('Running %s hooks', hook)
    run_hooks(hook, options, args)

    if options.script_filename:
135 136
        log.debug('Saving sourcable %s hooks to %s',
                  hook, options.script_filename)
137 138 139 140
        options.sourcing = True
        output = open(options.script_filename, "w")
        try:
            output.write('# %s\n' % hook)
141 142
            # output.write('echo %s\n' % hook)
            # output.write('set -x\n')
143 144 145 146 147 148
            run_hooks(hook + '_source', options, args, output)
        finally:
            output.close()

    return 0

149

150
def run_hooks(hook, options, args, output=None):
151
    log = logging.getLogger('virtualenvwrapper.hook_loader')
152 153 154
    if output is None:
        output = sys.stdout

155 156
    namespace = 'virtualenvwrapper.%s' % hook
    if options.names:
157
        log.debug('looking for %s hooks %s' % (namespace, options.names))
158 159
        hook_mgr = NamedExtensionManager(namespace, options.names)
    else:
160
        log.debug('looking for %s hooks' % namespace)
161 162 163 164 165 166 167 168 169 170 171 172 173
        hook_mgr = ExtensionManager(namespace)

    if options.listing:
        def show(ext):
            output.write('  %-10s -- %s\n' %
                         (ext.name, inspect.getdoc(ext.plugin) or ''))
        try:
            hook_mgr.map(show)
        except RuntimeError:  # no templates
            output.write('  No templates installed.\n')

    elif options.sourcing:
        def get_source(ext, args):
174 175
            # Show the shell commands so they can
            # be run in the calling shell.
176
            log.debug('getting source instructions for %s' % ext.name)
177
            contents = (ext.plugin(args) or '').strip()
178
            if contents:
179
                output.write('# %s\n' % ext.name)
180 181
                output.write(contents)
                output.write("\n")
182 183 184 185 186 187 188 189
        try:
            hook_mgr.map(get_source, args[1:])
        except RuntimeError:
            pass

    else:
        # Just run the plugin ourselves
        def invoke(ext, args):
190
            log.debug('running %s' % ext.name)
191 192 193 194 195
            ext.plugin(args)
        try:
            hook_mgr.map(invoke, args[1:])
        except RuntimeError:
            pass
196

197 198 199 200

def list_hooks(output=None):
    if output is None:
        output = sys.stdout
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
    static_names = [
        'initialize',
        'get_env_details',
        'project.pre_mkproject',
        'project.post_mkproject',
        'project.template',
    ]
    pre_post_hooks = (
        '_'.join(h)
        for h in itertools.product(['pre', 'post'],
                                   ['mkvirtualenv',
                                    'rmvirtualenv',
                                    'activate',
                                    'deactivate',
                                    'cpvirtualenv',
                                    ])
    )
    for hook in itertools.chain(static_names, pre_post_hooks):
219 220 221
        output.write(hook + '\n')


222 223
if __name__ == '__main__':
    main()