root/galaxy-central/eggs/PasteScript-1.7.3-py2.6.egg/paste/script/appinstall.py @ 3

リビジョン 3, 23.7 KB (コミッタ: kohda, 14 年 前)

Install Unix tools  http://hannonlab.cshl.edu/galaxy_unix_tools/galaxy.html

行番号 
1# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org)
2# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
3"""
4Provides the two commands for preparing an application:
5``prepare-app`` and ``setup-app``
6"""
7
8import os
9import sys
10if sys.version_info < (2, 4):
11    from paste.script.util import string24 as string
12else:
13    import string
14import new
15from cStringIO import StringIO
16from paste.script.command import Command, BadCommand, run as run_command
17import paste.script.templates
18from paste.script import copydir
19import pkg_resources
20Cheetah = None
21from ConfigParser import ConfigParser
22from paste.util import import_string
23from paste.deploy import appconfig
24from paste.script.util import uuid
25from paste.script.util import secret
26
27class AbstractInstallCommand(Command):
28
29    default_interactive = 1
30
31    default_sysconfigs = [
32        (False, '/etc/paste/sysconfig.py'),
33        (False, '/usr/local/etc/paste/sysconfig.py'),
34        (True, 'paste.script.default_sysconfig'),
35        ]
36    if os.environ.get('HOME'):
37        default_sysconfigs.insert(
38            0, (False, os.path.join(os.environ['HOME'], '.paste', 'config',
39                                    'sysconfig.py')))
40    if os.environ.get('PASTE_SYSCONFIG'):
41        default_sysconfigs.insert(
42            0, (False, os.environ['PASTE_SYSCONFIG']))
43
44    def run(self, args):
45        # This is overridden so we can parse sys-config before we pass
46        # it to optparse
47        self.sysconfigs = self.default_sysconfigs
48        new_args = []
49        while args:
50            if args[0].startswith('--no-default-sysconfig'):
51                self.sysconfigs = []
52                args.pop(0)
53                continue
54            if args[0].startswith('--sysconfig='):
55                self.sysconfigs.insert(
56                    0, (True, args.pop(0)[len('--sysconfig='):]))
57                continue
58            if args[0] == '--sysconfig':
59                args.pop(0)
60                if not args:
61                    raise BadCommand, (
62                        "You gave --sysconfig as the last argument without "
63                        "a value")
64                self.sysconfigs.insert(0, (True, args.pop(0)))
65                continue
66            new_args.append(args.pop(0))
67        self.load_sysconfigs()
68        return super(AbstractInstallCommand, self).run(new_args)
69
70    #@classmethod
71    def standard_parser(cls, **kw):
72        parser = super(AbstractInstallCommand, cls).standard_parser(**kw)
73        parser.add_option('--sysconfig',
74                          action="append",
75                          dest="sysconfigs",
76                          help="System configuration file")
77        parser.add_option('--no-default-sysconfig',
78                          action='store_true',
79                          dest='no_default_sysconfig',
80                          help="Don't load the default sysconfig files")
81        parser.add_option(
82            '--easy-install',
83            action='append',
84            dest='easy_install_op',
85            metavar='OP',
86            help='An option to add if invoking easy_install (like --easy-install=exclude-scripts)')
87        parser.add_option(
88            '--no-install',
89            action='store_true',
90            dest='no_install',
91            help="Don't try to install the package (it must already be installed)")
92        parser.add_option(
93            '-f', '--find-links',
94            action='append',
95            dest='easy_install_find_links',
96            metavar='URL',
97            help='Passed through to easy_install')
98
99        return parser
100
101    standard_parser = classmethod(standard_parser)
102
103    ########################################
104    ## Sysconfig Handling
105    ########################################
106
107    def load_sysconfigs(self):
108        configs = self.sysconfigs[:]
109        configs.reverse()
110        self.sysconfig_modules = []
111        for index, (explicit, name) in enumerate(configs):
112            # @@: At some point I'd like to give the specialized
113            # modules some access to the values in earlier modules,
114            # e.g., to specialize those values or functions.  That's
115            # why these modules are loaded backwards.
116            if name.endswith('.py'):
117                if not os.path.exists(name):
118                    if explicit:
119                        raise BadCommand, (
120                            "sysconfig file %s does not exist"
121                            % name)
122                    else:
123                        continue
124                globs = {}
125                execfile(name, globs)
126                mod = new.module('__sysconfig_%i__' % index)
127                for name, value in globs.items():
128                    setattr(mod, name, value)
129                mod.__file__ = name
130            else:
131                try:
132                    mod = import_string.simple_import(name)
133                except ImportError, e:
134                    if explicit:
135                        raise
136                    else:
137                        continue
138            mod.paste_command = self
139            self.sysconfig_modules.insert(0, mod)
140        # @@: I'd really prefer to clone the parser here somehow,
141        # not to modify it in place
142        parser = self.parser
143        self.call_sysconfig_functions('add_custom_options', parser)
144
145    def get_sysconfig_option(self, name, default=None):
146        """
147        Return the value of the given option in the first sysconfig
148        module in which it is found, or ``default`` (None) if not
149        found in any.
150        """
151        for mod in self.sysconfig_modules:
152            if hasattr(mod, name):
153                return getattr(mod, name)
154        return default
155
156    def get_sysconfig_options(self, name):
157        """
158        Return the option value for the given name in all the
159        sysconfig modules in which is is found (``[]`` if none).
160        """
161        return [getattr(mod, name) for mod in self.sysconfig_modules
162                if hasattr(mod, name)]
163
164    def call_sysconfig_function(self, name, *args, **kw):
165        """
166        Call the specified function in the first sysconfig module it
167        is defined in.  ``NameError`` if no function is found.
168        """
169        val = self.get_sysconfig_option(name)
170        if val is None:
171            raise NameError, (
172                "Method %s not found in any sysconfig module" % name)
173        return val(*args, **kw)
174
175    def call_sysconfig_functions(self, name, *args, **kw):
176        """
177        Call all the named functions in the sysconfig modules,
178        returning a list of the return values.
179        """
180        return [method(*args, **kw) for method in
181                self.get_sysconfig_options(name)]
182
183    def sysconfig_install_vars(self, installer):
184        """
185        Return the folded results of calling the
186        ``install_variables()`` functions.
187        """
188        result = {}
189        all_vars = self.call_sysconfig_functions(
190            'install_variables', installer)
191        all_vars.reverse()
192        for vardict in all_vars:
193            result.update(vardict)
194        return result
195
196    ########################################
197    ## Distributions
198    ########################################
199
200    def get_distribution(self, req):
201        """
202        This gets a distribution object, and installs the distribution
203        if required.
204        """
205        try:
206            dist = pkg_resources.get_distribution(req)
207            if self.verbose:
208                print 'Distribution already installed:'
209                print ' ', dist, 'from', dist.location
210            return dist
211        except pkg_resources.DistributionNotFound:
212            if self.options.no_install:
213                print "Because --no-install was given, we won't try to install the package %s" % req
214                raise
215            options = ['-v', '-m']
216            for op in self.options.easy_install_op or []:
217                if not op.startswith('-'):
218                    op = '--'+op
219                options.append(op)
220            for op in self.options.easy_install_find_links or []:
221                options.append('--find-links=%s' % op)
222            if self.simulate:
223                raise BadCommand(
224                    "Must install %s, but in simulation mode" % req)
225            print "Must install %s" % req
226            from setuptools.command import easy_install
227            from setuptools import setup
228            setup(script_args=['-q', 'easy_install']
229                  + options + [req])
230            return pkg_resources.get_distribution(req)
231
232    def get_installer(self, distro, ep_group, ep_name):
233        installer_class = distro.load_entry_point(
234            'paste.app_install', ep_name)
235        installer = installer_class(
236            distro, ep_group, ep_name)
237        return installer
238   
239
240class MakeConfigCommand(AbstractInstallCommand):
241
242    default_verbosity = 1
243    max_args = None
244    min_args = 1
245    summary = "Install a package and create a fresh config file/directory"
246    usage = "PACKAGE_NAME [CONFIG_FILE] [VAR=VALUE]"
247
248    description = """\
249    Note: this is an experimental command, and it will probably change
250    in several ways by the next release.
251
252    make-config is part of a two-phase installation process (the
253    second phase is setup-app).  make-config installs the package
254    (using easy_install) and asks it to create a bare configuration
255    file or directory (possibly filling in defaults from the extra
256    variables you give).
257    """
258
259    parser = AbstractInstallCommand.standard_parser(
260        simulate=True, quiet=True, no_interactive=True)
261    parser.add_option('--info',
262                      action="store_true",
263                      dest="show_info",
264                      help="Show information on the package (after installing it), but do not write a config.")
265    parser.add_option('--name',
266                      action='store',
267                      dest='ep_name',
268                      help='The name of the application contained in the distribution (default "main")')
269    parser.add_option('--entry-group',
270                      action='store',
271                      dest='ep_group',
272                      default='paste.app_factory',
273                      help='The entry point group to install (i.e., the kind of application; default paste.app_factory')
274    parser.add_option('--edit',
275                      action='store_true',
276                      dest='edit',
277                      help='Edit the configuration file after generating it (using $EDITOR)')
278    parser.add_option('--setup',
279                      action='store_true',
280                      dest='run_setup',
281                      help='Run setup-app immediately after generating (and possibly editing) the configuration file')
282
283    def command(self):
284        self.requirement = self.args[0]
285        if '#' in self.requirement:
286            if self.options.ep_name is not None:
287                raise BadCommand(
288                    "You may not give both --name and a requirement with "
289                    "#name")
290            self.requirement, self.options.ep_name = self.requirement.split('#', 1)
291        if not self.options.ep_name:
292            self.options.ep_name = 'main'
293        self.distro = self.get_distribution(self.requirement)
294        self.installer = self.get_installer(
295            self.distro, self.options.ep_group, self.options.ep_name)
296        if self.options.show_info:
297            if len(self.args) > 1:
298                raise BadCommand(
299                    "With --info you can only give one argument")
300            return self.show_info()
301        if len(self.args) < 2:
302            # See if sysconfig can give us a default filename
303            options = filter(None, self.call_sysconfig_functions(
304                'default_config_filename', self.installer))
305            if not options:
306                raise BadCommand(
307                    "You must give a configuration filename")
308            self.config_file = options[0]
309        else:
310            self.config_file = self.args[1]
311        self.check_config_file()
312        self.project_name = self.distro.project_name
313        self.vars = self.sysconfig_install_vars(self.installer)
314        self.vars.update(self.parse_vars(self.args[2:]))
315        self.vars['project_name'] = self.project_name
316        self.vars['requirement'] = self.requirement
317        self.vars['ep_name'] = self.options.ep_name
318        self.vars['ep_group'] = self.options.ep_group
319        self.vars.setdefault('app_name', self.project_name.lower())
320        self.vars.setdefault('app_instance_uuid', uuid.uuid4())
321        self.vars.setdefault('app_instance_secret', secret.secret_string())
322        if self.verbose > 1:
323            print_vars = self.vars.items()
324            print_vars.sort()
325            print 'Variables for installation:'
326            for name, value in print_vars:
327                print '  %s: %r' % (name, value)
328        self.installer.write_config(self, self.config_file, self.vars)
329        edit_success = True
330        if self.options.edit:
331            edit_success = self.run_editor()
332        setup_configs = self.installer.editable_config_files(self.config_file)
333        # @@: We'll just assume the first file in the list is the one
334        # that works with setup-app...
335        setup_config = setup_configs[0]
336        if self.options.run_setup:
337            if not edit_success:
338                print 'Config-file editing was not successful.'
339                if self.ask('Run setup-app anyway?', default=False):
340                    self.run_setup(setup_config)
341            else:
342                self.run_setup(setup_config)
343        else:
344            filenames = self.installer.editable_config_files(self.config_file)
345            assert not isinstance(filenames, basestring), (
346                "editable_config_files returned a string, not a list")
347            if not filenames and filenames is not None:
348                print 'No config files need editing'
349            else:
350                print 'Now you should edit the config files'
351                if filenames:
352                    for fn in filenames:
353                        print '  %s' % fn
354
355    def show_info(self):
356        text = self.installer.description(None)
357        print text
358
359    def check_config_file(self):
360        if self.installer.expect_config_directory is None:
361            return
362        fn = self.config_file
363        if self.installer.expect_config_directory:
364            if os.path.splitext(fn)[1]:
365                raise BadCommand(
366                    "The CONFIG_FILE argument %r looks like a filename, "
367                    "and a directory name is expected" % fn)
368        else:
369            if fn.endswith('/') or not os.path.splitext(fn):
370                raise BadCommand(
371                    "The CONFIG_FILE argument %r looks like a directory "
372                    "name and a filename is expected" % fn)
373
374    def run_setup(self, filename):
375        run_command(['setup-app', filename])
376
377    def run_editor(self):
378        filenames = self.installer.editable_config_files(self.config_file)
379        if filenames is None:
380            print 'Warning: the config file is not known (--edit ignored)'
381            return False
382        if not filenames:
383            print 'Warning: no config files need editing (--edit ignored)'
384            return True
385        if len(filenames) > 1:
386            print 'Warning: there is more than one editable config file (--edit ignored)'
387            return False
388        if not os.environ.get('EDITOR'):
389            print 'Error: you must set $EDITOR if using --edit'
390            return False
391        if self.verbose:
392            print '%s %s' % (os.environ['EDITOR'], filenames[0])
393        retval = os.system('$EDITOR %s' % filenames[0])
394        if retval:
395            print 'Warning: editor %s returned with error code %i' % (
396                os.environ['EDITOR'], retval)
397            return False
398        return True
399       
400class SetupCommand(AbstractInstallCommand):
401
402    default_verbosity = 1
403    max_args = 1
404    min_args = 1
405    summary = "Setup an application, given a config file"
406    usage = "CONFIG_FILE"
407
408    description = """\
409    Note: this is an experimental command, and it will probably change
410    in several ways by the next release.
411
412    Setup an application according to its configuration file.  This is
413    the second part of a two-phase web application installation
414    process (the first phase is prepare-app).  The setup process may
415    consist of things like creating directories and setting up
416    databases.
417    """
418
419    parser = AbstractInstallCommand.standard_parser(
420        simulate=True, quiet=True, interactive=True)
421    parser.add_option('--name',
422                      action='store',
423                      dest='section_name',
424                      default=None,
425                      help='The name of the section to set up (default: app:main)')
426
427    def command(self):
428        config_spec = self.args[0]
429        section = self.options.section_name
430        if section is None:
431            if '#' in config_spec:
432                config_spec, section = config_spec.split('#', 1)
433            else:
434                section = 'main'
435        if not ':' in section:
436            plain_section = section
437            section = 'app:'+section
438        else:
439            plain_section = section.split(':', 1)[0]
440        if not config_spec.startswith('config:'):
441            config_spec = 'config:' + config_spec
442        if plain_section != 'main':
443            config_spec += '#' + plain_section
444        config_file = config_spec[len('config:'):].split('#', 1)[0]
445        config_file = os.path.join(os.getcwd(), config_file)
446        self.logging_file_config(config_file)
447        conf = appconfig(config_spec, relative_to=os.getcwd())
448        ep_name = conf.context.entry_point_name
449        ep_group = conf.context.protocol
450        dist = conf.context.distribution
451        if dist is None:
452            raise BadCommand(
453                "The section %r is not the application (probably a filter).  You should add #section_name, where section_name is the section that configures your application" % plain_section)
454        installer = self.get_installer(dist, ep_group, ep_name)
455        installer.setup_config(
456            self, config_file, section, self.sysconfig_install_vars(installer))
457        self.call_sysconfig_functions(
458            'post_setup_hook', installer, config_file)
459       
460       
461class Installer(object):
462
463    """
464    Abstract base class for installers, and also a generic
465    installer that will run off config files in the .egg-info
466    directory of a distribution.
467
468    Packages that simply refer to this installer can provide a file
469    ``*.egg-info/paste_deploy_config.ini_tmpl`` that will be
470    interpreted by Cheetah.  They can also provide ``websetup``
471    modules with a ``setup_app(command, conf, vars)`` (or the
472    now-deprecated ``setup_config(command, filename, section, vars)``)
473    function, that will be called.
474
475    In the future other functions or configuration files may be
476    called.
477    """
478
479    # If this is true, then try to detect filename-looking config_file
480    # values, and reject them.  Conversely, if false try to detect
481    # directory-looking values and reject them.  None means don't
482    # check.
483    expect_config_directory = False
484   
485    # Set this to give a default config filename when none is
486    # specified:
487    default_config_filename = None
488
489    # Set this to true to use Cheetah to fill your templates, or false
490    # to not do so:
491    use_cheetah = True
492
493    def __init__(self, dist, ep_group, ep_name):
494        self.dist = dist
495        self.ep_group = ep_group
496        self.ep_name = ep_name
497
498    def description(self, config):
499        return 'An application'
500       
501    def write_config(self, command, filename, vars):
502        """
503        Writes the content to the filename (directory or single file).
504        You should use the ``command`` object, which respects things
505        like simulation and interactive.  ``vars`` is a dictionary
506        of user-provided variables.
507        """
508        command.ensure_file(filename, self.config_content(command, vars))
509
510    def editable_config_files(self, filename):
511        """
512        Return a list of filenames; this is primarily used when the
513        filename is treated as a directory and several configuration
514        files are created.  The default implementation returns the
515        file itself.  Return None if you don't know what files should
516        be edited on installation.
517        """
518        if not self.expect_config_directory:
519            return [filename]
520        else:
521            return None
522
523    def config_content(self, command, vars):
524        """
525        Called by ``self.write_config``, this returns the text content
526        for the config file, given the provided variables.
527
528        The default implementation reads
529        ``Package.egg-info/paste_deploy_config.ini_tmpl`` and fills it
530        with the variables.
531        """
532        global Cheetah
533        meta_name = 'paste_deploy_config.ini_tmpl'
534        if not self.dist.has_metadata(meta_name):
535            if command.verbose:
536                print 'No %s found' % meta_name
537            return self.simple_config(vars)
538        return self.template_renderer(
539            self.dist.get_metadata(meta_name), vars, filename=meta_name)
540
541    def template_renderer(self, content, vars, filename=None):
542        """
543        Subclasses may override this to provide different template
544        substitution (e.g., use a different template engine).
545        """
546        if self.use_cheetah:
547            import Cheetah.Template
548            tmpl = Cheetah.Template.Template(content,
549                                             searchList=[vars])
550            return copydir.careful_sub(
551                tmpl, vars, filename)
552        else:
553            tmpl = string.Template(content)
554            return tmpl.substitute(vars)
555
556    def simple_config(self, vars):
557        """
558        Return a very simple configuration file for this application.
559        """
560        if self.ep_name != 'main':
561            ep_name = '#'+self.ep_name
562        else:
563            ep_name = ''
564        return ('[app:main]\n'
565                'use = egg:%s%s\n'
566                % (self.dist.project_name, ep_name))
567
568    def setup_config(self, command, filename, section, vars):
569        """
570        Called to setup an application, given its configuration
571        file/directory.
572
573        The default implementation calls
574        ``package.websetup.setup_config(command, filename, section,
575        vars)`` or ``package.websetup.setup_app(command, config,
576        vars)``
577
578        With ``setup_app`` the ``config`` object is a dictionary with
579        the extra attributes ``global_conf``, ``local_conf`` and
580        ``filename``
581        """
582        modules = [
583            line.strip()
584            for line in self.dist.get_metadata_lines('top_level.txt')
585            if line.strip() and not line.strip().startswith('#')]
586        if not modules:
587            print 'No modules are listed in top_level.txt'
588            print 'Try running python setup.py egg_info to regenerate that file'
589        for mod_name in modules:
590            mod_name = mod_name + '.websetup'
591            mod = import_string.try_import_module(mod_name)
592            if mod is None:
593                continue
594            if command.verbose:
595                print 'Running setup_config() from %s' % mod_name
596            if hasattr(mod, 'setup_app'):
597                self._call_setup_app(
598                    mod.setup_app, command, filename, section, vars)
599            elif hasattr(mod, 'setup_config'):
600                mod.setup_config(command, filename, section, vars)
601            else:
602                print 'No setup_app() or setup_config() function in %s (%s)' % (
603                    mod.__name__, mod.__file__)
604
605    def _call_setup_app(self, func, command, filename, section, vars):
606        filename = os.path.abspath(filename)
607        if ':' in section:
608            section = section.split(':', 1)[1]
609        conf = 'config:%s#%s' % (filename, section)
610        conf = appconfig(conf)
611        conf.filename = filename
612        func(command, conf, vars)
613
Note: リポジトリブラウザについてのヘルプは TracBrowser を参照してください。