~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

(robertc) Deprecate WorkingTree.pending_merges.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2004, 2005 by Canonical Ltd
2
 
 
 
1
# Copyright (C) 2006 by Canonical Ltd
 
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
5
5
# the Free Software Foundation; either version 2 of the License, or
6
6
# (at your option) any later version.
7
 
 
 
7
#
8
8
# This program is distributed in the hope that it will be useful,
9
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
11
# GNU General Public License for more details.
12
 
 
 
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
18
# TODO: probably should say which arguments are candidates for glob
19
19
# expansion on windows and do that at the command level.
20
20
 
21
 
# TODO: Help messages for options.
22
 
 
23
21
# TODO: Define arguments by objects, rather than just using names.
24
22
# Those objects can specify the expected type of the argument, which
25
 
# would help with validation and shell completion.
 
23
# would help with validation and shell completion.  They could also provide
 
24
# help/explanation for that argument in a structured way.
 
25
 
 
26
# TODO: Specific "examples" property on commands for consistent formatting.
26
27
 
27
28
# TODO: "--profile=cum", to change sort order.  Is there any value in leaving
28
29
# the profile output behind so it can be interactively examined?
29
30
 
30
 
import sys
 
31
import codecs
 
32
import errno
31
33
import os
32
34
from warnings import warn
33
 
from inspect import getdoc
34
 
import errno
 
35
import sys
35
36
 
36
37
import bzrlib
37
 
import bzrlib.trace
38
 
from bzrlib.trace import mutter, note, log_error, warning, be_quiet
39
 
from bzrlib.errors import (BzrError, 
 
38
import bzrlib.errors as errors
 
39
from bzrlib.errors import (BzrError,
 
40
                           BzrCommandError,
40
41
                           BzrCheckError,
41
 
                           BzrCommandError,
42
 
                           BzrOptionError,
43
42
                           NotBranchError)
44
 
from bzrlib.revisionspec import RevisionSpec
45
 
from bzrlib import BZRDIR
 
43
from bzrlib import option
46
44
from bzrlib.option import Option
 
45
import bzrlib.osutils
 
46
from bzrlib.symbol_versioning import (deprecated_method, zero_eight)
 
47
import bzrlib.trace
 
48
from bzrlib.trace import mutter, note, log_error, warning, be_quiet
47
49
 
48
50
plugin_cmds = {}
49
51
 
50
52
 
51
53
def register_command(cmd, decorate=False):
52
 
    "Utility function to help register a command"
 
54
    """Utility function to help register a command
 
55
 
 
56
    :param cmd: Command subclass to register
 
57
    :param decorate: If true, allow overriding an existing command
 
58
        of the same name; the old command is returned by this function.
 
59
        Otherwise it is an error to try to override an existing command.
 
60
    """
53
61
    global plugin_cmds
54
62
    k = cmd.__name__
55
63
    if k.startswith("cmd_"):
56
64
        k_unsquished = _unsquish_command_name(k)
57
65
    else:
58
66
        k_unsquished = k
59
 
    if not plugin_cmds.has_key(k_unsquished):
 
67
    if k_unsquished not in plugin_cmds:
60
68
        plugin_cmds[k_unsquished] = cmd
61
 
        mutter('registered plugin command %s', k_unsquished)      
 
69
        mutter('registered plugin command %s', k_unsquished)
62
70
        if decorate and k_unsquished in builtin_command_names():
63
71
            return _builtin_commands()[k_unsquished]
64
72
    elif decorate:
85
93
    builtins = bzrlib.builtins.__dict__
86
94
    for name in builtins:
87
95
        if name.startswith("cmd_"):
88
 
            real_name = _unsquish_command_name(name)        
 
96
            real_name = _unsquish_command_name(name)
89
97
            r[real_name] = builtins[name]
90
98
    return r
91
 
 
92
99
            
93
100
 
94
101
def builtin_command_names():
122
129
    """
123
130
    from bzrlib.externalcommand import ExternalCommand
124
131
 
125
 
    cmd_name = str(cmd_name)            # not unicode
 
132
    # We want only 'ascii' command names, but the user may have typed
 
133
    # in a Unicode name. In that case, they should just get a
 
134
    # 'command not found' error later.
 
135
    # In the future, we may actually support Unicode command names.
126
136
 
127
137
    # first look up this command under the specified name
128
138
    cmds = _get_cmd_dict(plugins_override=plugins_override)
140
150
    if cmd_obj:
141
151
        return cmd_obj
142
152
 
143
 
    raise BzrCommandError("unknown command %r" % cmd_name)
 
153
    raise BzrCommandError('unknown command "%s"' % cmd_name)
144
154
 
145
155
 
146
156
class Command(object):
168
178
        List of argument forms, marked with whether they are optional,
169
179
        repeated, etc.
170
180
 
171
 
                Examples:
172
 
 
173
 
                ['to_location', 'from_branch?', 'file*']
174
 
 
175
 
                'to_location' is required
176
 
                'from_branch' is optional
177
 
                'file' can be specified 0 or more times
 
181
                Examples:
 
182
 
 
183
                ['to_location', 'from_branch?', 'file*']
 
184
 
 
185
                'to_location' is required
 
186
                'from_branch' is optional
 
187
                'file' can be specified 0 or more times
178
188
 
179
189
    takes_options
180
190
        List of options that may be given for this command.  These can
184
194
    hidden
185
195
        If true, this command isn't advertised.  This is typically
186
196
        for commands intended for expert users.
 
197
 
 
198
    encoding_type
 
199
        Command objects will get a 'outf' attribute, which has been
 
200
        setup to properly handle encoding of unicode strings.
 
201
        encoding_type determines what will happen when characters cannot
 
202
        be encoded
 
203
            strict - abort if we cannot decode
 
204
            replace - put in a bogus character (typically '?')
 
205
            exact - do not encode sys.stdout
 
206
 
187
207
    """
188
208
    aliases = []
189
209
    takes_args = []
190
210
    takes_options = []
 
211
    encoding_type = 'strict'
191
212
 
192
213
    hidden = False
193
214
    
203
224
        r = dict()
204
225
        r['help'] = Option.OPTIONS['help']
205
226
        for o in self.takes_options:
206
 
            if not isinstance(o, Option):
 
227
            if isinstance(o, basestring):
207
228
                o = Option.OPTIONS[o]
208
229
            r[o.name] = o
209
230
        return r
210
231
 
 
232
    def _setup_outf(self):
 
233
        """Return a file linked to stdout, which has proper encoding."""
 
234
        assert self.encoding_type in ['strict', 'exact', 'replace']
 
235
 
 
236
        # Originally I was using self.stdout, but that looks
 
237
        # *way* too much like sys.stdout
 
238
        if self.encoding_type == 'exact':
 
239
            self.outf = sys.stdout
 
240
            return
 
241
 
 
242
        output_encoding = bzrlib.osutils.get_terminal_encoding()
 
243
 
 
244
        # use 'replace' so that we don't abort if trying to write out
 
245
        # in e.g. the default C locale.
 
246
        self.outf = codecs.getwriter(output_encoding)(sys.stdout, errors=self.encoding_type)
 
247
        # For whatever reason codecs.getwriter() does not advertise its encoding
 
248
        # it just returns the encoding of the wrapped file, which is completely
 
249
        # bogus. So set the attribute, so we can find the correct encoding later.
 
250
        self.outf.encoding = output_encoding
 
251
 
 
252
    @deprecated_method(zero_eight)
211
253
    def run_argv(self, argv):
212
 
        """Parse command line and run."""
213
 
        args, opts = parse_args(self, argv)
 
254
        """Parse command line and run.
 
255
        
 
256
        See run_argv_aliases for the 0.8 and beyond api.
 
257
        """
 
258
        return self.run_argv_aliases(argv)
 
259
 
 
260
    def run_argv_aliases(self, argv, alias_argv=None):
 
261
        """Parse the command line and run with extra aliases in alias_argv."""
 
262
        if argv is None:
 
263
            warn("Passing None for [] is deprecated from bzrlib 0.10", 
 
264
                 DeprecationWarning, stacklevel=2)
 
265
            argv = []
 
266
        args, opts = parse_args(self, argv, alias_argv)
214
267
        if 'help' in opts:  # e.g. bzr add --help
215
268
            from bzrlib.help import help_on_command
216
269
            help_on_command(self.name())
217
270
            return 0
218
 
        # XXX: This should be handled by the parser
219
 
        allowed_names = self.options().keys()
220
 
        for oname in opts:
221
 
            if oname not in allowed_names:
222
 
                raise BzrCommandError("option '--%s' is not allowed for command %r"
223
 
                                      % (oname, self.name()))
224
271
        # mix arguments and options into one dictionary
225
272
        cmdargs = _match_argform(self.name(), self.takes_args, args)
226
273
        cmdopts = {}
230
277
        all_cmd_args = cmdargs.copy()
231
278
        all_cmd_args.update(cmdopts)
232
279
 
 
280
        self._setup_outf()
 
281
 
233
282
        return self.run(**all_cmd_args)
234
283
    
235
284
    def run(self):
242
291
        shell error code if not.  It's OK for this method to allow
243
292
        an exception to raise up.
244
293
        """
245
 
        raise NotImplementedError()
246
 
 
 
294
        raise NotImplementedError('no implementation of command %r' 
 
295
                                  % self.name())
247
296
 
248
297
    def help(self):
249
298
        """Return help message for this class."""
 
299
        from inspect import getdoc
250
300
        if self.__doc__ is Command.__doc__:
251
301
            return None
252
302
        return getdoc(self)
254
304
    def name(self):
255
305
        return _unsquish_command_name(self.__class__.__name__)
256
306
 
 
307
    def plugin_name(self):
 
308
        """Get the name of the plugin that provides this command.
 
309
 
 
310
        :return: The name of the plugin or None if the command is builtin.
 
311
        """
 
312
        mod_parts = self.__module__.split('.')
 
313
        if len(mod_parts) >= 3 and mod_parts[1] == 'plugins':
 
314
            return mod_parts[2]
 
315
        else:
 
316
            return None
 
317
 
257
318
 
258
319
def parse_spec(spec):
259
320
    """
286
347
        parsed = [spec, None]
287
348
    return parsed
288
349
 
289
 
def parse_args(command, argv):
 
350
def parse_args(command, argv, alias_argv=None):
290
351
    """Parse command line.
291
352
    
292
353
    Arguments and options are parsed at this level before being passed
294
355
    lookup table, something about the available options, what optargs
295
356
    they take, and which commands will accept them.
296
357
    """
297
 
    # TODO: chop up this beast; make it a method of the Command
298
 
    args = []
299
 
    opts = {}
300
 
 
301
 
    cmd_options = command.options()
302
 
    argsover = False
303
 
    while argv:
304
 
        a = argv.pop(0)
305
 
        if argsover:
306
 
            args.append(a)
307
 
            continue
308
 
        elif a == '--':
309
 
            # We've received a standalone -- No more flags
310
 
            argsover = True
311
 
            continue
312
 
        if a[0] == '-':
313
 
            # option names must not be unicode
314
 
            a = str(a)
315
 
            optarg = None
316
 
            if a[1] == '-':
317
 
                mutter("  got option %r", a)
318
 
                if '=' in a:
319
 
                    optname, optarg = a[2:].split('=', 1)
320
 
                else:
321
 
                    optname = a[2:]
322
 
                if optname not in cmd_options:
323
 
                    raise BzrOptionError('unknown long option %r for command %s'
324
 
                        % (a, command.name()))
325
 
            else:
326
 
                shortopt = a[1:]
327
 
                if shortopt in Option.SHORT_OPTIONS:
328
 
                    # Multi-character options must have a space to delimit
329
 
                    # their value
330
 
                    # ^^^ what does this mean? mbp 20051014
331
 
                    optname = Option.SHORT_OPTIONS[shortopt].name
332
 
                else:
333
 
                    # Single character short options, can be chained,
334
 
                    # and have their value appended to their name
335
 
                    shortopt = a[1:2]
336
 
                    if shortopt not in Option.SHORT_OPTIONS:
337
 
                        # We didn't find the multi-character name, and we
338
 
                        # didn't find the single char name
339
 
                        raise BzrError('unknown short option %r' % a)
340
 
                    optname = Option.SHORT_OPTIONS[shortopt].name
341
 
 
342
 
                    if a[2:]:
343
 
                        # There are extra things on this option
344
 
                        # see if it is the value, or if it is another
345
 
                        # short option
346
 
                        optargfn = Option.OPTIONS[optname].type
347
 
                        if optargfn is None:
348
 
                            # This option does not take an argument, so the
349
 
                            # next entry is another short option, pack it back
350
 
                            # into the list
351
 
                            argv.insert(0, '-' + a[2:])
352
 
                        else:
353
 
                            # This option takes an argument, so pack it
354
 
                            # into the array
355
 
                            optarg = a[2:]
356
 
            
357
 
                if optname not in cmd_options:
358
 
                    raise BzrOptionError('unknown short option %r for command'
359
 
                        ' %s' % (shortopt, command.name()))
360
 
            if optname in opts:
361
 
                # XXX: Do we ever want to support this, e.g. for -r?
362
 
                raise BzrError('repeated option %r' % a)
363
 
                
364
 
            option_obj = cmd_options[optname]
365
 
            optargfn = option_obj.type
366
 
            if optargfn:
367
 
                if optarg == None:
368
 
                    if not argv:
369
 
                        raise BzrError('option %r needs an argument' % a)
370
 
                    else:
371
 
                        optarg = argv.pop(0)
372
 
                opts[optname] = optargfn(optarg)
373
 
            else:
374
 
                if optarg != None:
375
 
                    raise BzrError('option %r takes no argument' % optname)
376
 
                opts[optname] = True
377
 
        else:
378
 
            args.append(a)
 
358
    # TODO: make it a method of the Command?
 
359
    parser = option.get_optparser(command.options())
 
360
    if alias_argv is not None:
 
361
        args = alias_argv + argv
 
362
    else:
 
363
        args = argv
 
364
 
 
365
    options, args = parser.parse_args(args)
 
366
    opts = dict([(k, v) for k, v in options.__dict__.iteritems() if 
 
367
                 v is not option.OptionParser.DEFAULT_VALUE])
379
368
    return args, opts
380
369
 
381
370
 
406
395
                raise BzrCommandError("command %r needs one or more %s"
407
396
                        % (cmd, argname.upper()))
408
397
            argdict[argname + '_list'] = args[:-1]
409
 
            args[:-1] = []                
 
398
            args[:-1] = []
410
399
        else:
411
400
            # just a plain arg
412
401
            argname = ap
447
436
        os.remove(pfname)
448
437
 
449
438
 
450
 
def apply_lsprofiled(the_callable, *args, **kwargs):
 
439
def apply_lsprofiled(filename, the_callable, *args, **kwargs):
451
440
    from bzrlib.lsprof import profile
452
 
    ret,stats = profile(the_callable,*args,**kwargs)
 
441
    import cPickle
 
442
    ret, stats = profile(the_callable, *args, **kwargs)
453
443
    stats.sort()
454
 
    stats.pprint()
 
444
    if filename is None:
 
445
        stats.pprint()
 
446
    else:
 
447
        stats.freeze()
 
448
        cPickle.dump(stats, open(filename, 'w'), 2)
 
449
        print 'Profile data written to %r.' % filename
455
450
    return ret
456
451
 
 
452
 
 
453
def get_alias(cmd):
 
454
    """Return an expanded alias, or None if no alias exists"""
 
455
    import bzrlib.config
 
456
    alias = bzrlib.config.GlobalConfig().get_alias(cmd)
 
457
    if (alias):
 
458
        return alias.split(' ')
 
459
    return None
 
460
 
 
461
 
457
462
def run_bzr(argv):
458
463
    """Execute a command.
459
464
 
462
467
    
463
468
    argv
464
469
       The command-line arguments, without the program name from argv[0]
 
470
       These should already be decoded. All library/test code calling
 
471
       run_bzr should be passing valid strings (don't need decoding).
465
472
    
466
473
    Returns a command status or raises an exception.
467
474
 
471
478
    --no-plugins
472
479
        Do not load plugin modules at all
473
480
 
 
481
    --no-aliases
 
482
        Do not allow aliases
 
483
 
474
484
    --builtin
475
485
        Only use builtin commands.  (Plugins are still allowed to change
476
486
        other behaviour.)
481
491
    --lsprof
482
492
        Run under the Python lsprof profiler.
483
493
    """
484
 
    argv = [a.decode(bzrlib.user_encoding) for a in argv]
 
494
    argv = list(argv)
485
495
 
486
 
    opt_lsprof = opt_profile = opt_no_plugins = opt_builtin = False
 
496
    opt_lsprof = opt_profile = opt_no_plugins = opt_builtin =  \
 
497
                opt_no_aliases = False
 
498
    opt_lsprof_file = None
487
499
 
488
500
    # --no-plugins is handled specially at a very early stage. We need
489
501
    # to load plugins before doing other command parsing so that they
490
502
    # can override commands, but this needs to happen first.
491
503
 
492
 
    for a in argv:
 
504
    argv_copy = []
 
505
    i = 0
 
506
    while i < len(argv):
 
507
        a = argv[i]
493
508
        if a == '--profile':
494
509
            opt_profile = True
495
510
        elif a == '--lsprof':
496
511
            opt_lsprof = True
 
512
        elif a == '--lsprof-file':
 
513
            opt_lsprof = True
 
514
            opt_lsprof_file = argv[i + 1]
 
515
            i += 1
497
516
        elif a == '--no-plugins':
498
517
            opt_no_plugins = True
 
518
        elif a == '--no-aliases':
 
519
            opt_no_aliases = True
499
520
        elif a == '--builtin':
500
521
            opt_builtin = True
501
522
        elif a in ('--quiet', '-q'):
502
523
            be_quiet()
503
524
        else:
504
 
            continue
505
 
        argv.remove(a)
 
525
            argv_copy.append(a)
 
526
        i += 1
506
527
 
507
 
    if (not argv) or (argv[0] == '--help'):
508
 
        from bzrlib.help import help
509
 
        if len(argv) > 1:
510
 
            help(argv[1])
511
 
        else:
512
 
            help()
 
528
    argv = argv_copy
 
529
    if (not argv):
 
530
        from bzrlib.builtins import cmd_help
 
531
        cmd_help().run_argv_aliases([])
513
532
        return 0
514
533
 
515
534
    if argv[0] == '--version':
516
 
        from bzrlib.builtins import show_version
 
535
        from bzrlib.version import show_version
517
536
        show_version()
518
537
        return 0
519
538
        
520
539
    if not opt_no_plugins:
521
540
        from bzrlib.plugin import load_plugins
522
541
        load_plugins()
523
 
 
524
 
    cmd = str(argv.pop(0))
 
542
    else:
 
543
        from bzrlib.plugin import disable_plugins
 
544
        disable_plugins()
 
545
 
 
546
    alias_argv = None
 
547
 
 
548
    if not opt_no_aliases:
 
549
        alias_argv = get_alias(argv[0])
 
550
        if alias_argv:
 
551
            alias_argv = [a.decode(bzrlib.user_encoding) for a in alias_argv]
 
552
            argv[0] = alias_argv.pop(0)
 
553
 
 
554
    cmd = argv.pop(0)
 
555
    # We want only 'ascii' command names, but the user may have typed
 
556
    # in a Unicode name. In that case, they should just get a
 
557
    # 'command not found' error later.
525
558
 
526
559
    cmd_obj = get_cmd_object(cmd, plugins_override=not opt_builtin)
 
560
    if not getattr(cmd_obj.run_argv, 'is_deprecated', False):
 
561
        run = cmd_obj.run_argv
 
562
        run_argv = [argv]
 
563
    else:
 
564
        run = cmd_obj.run_argv_aliases
 
565
        run_argv = [argv, alias_argv]
527
566
 
528
567
    try:
529
568
        if opt_lsprof:
530
 
            ret = apply_lsprofiled(cmd_obj.run_argv, argv)
 
569
            ret = apply_lsprofiled(opt_lsprof_file, run, *run_argv)
531
570
        elif opt_profile:
532
 
            ret = apply_profiled(cmd_obj.run_argv, argv)
 
571
            ret = apply_profiled(run, *run_argv)
533
572
        else:
534
 
            ret = cmd_obj.run_argv(argv)
 
573
            ret = run(*run_argv)
535
574
        return ret or 0
536
575
    finally:
537
576
        # reset, in case we may do other commands later within the same process
548
587
            if not hasattr(e, 'errno'):
549
588
                raise
550
589
            if e.errno != errno.EPIPE:
551
 
                raise
 
590
                # Win32 raises IOError with errno=0 on a broken pipe
 
591
                if sys.platform != 'win32' or e.errno != 0:
 
592
                    raise
552
593
            pass
553
594
        except KeyboardInterrupt:
554
595
            pass
558
599
def main(argv):
559
600
    import bzrlib.ui
560
601
    from bzrlib.ui.text import TextUIFactory
561
 
    ## bzrlib.trace.enable_default_logging()
562
 
    bzrlib.trace.log_startup(argv)
563
602
    bzrlib.ui.ui_factory = TextUIFactory()
564
 
    ret = run_bzr_catch_errors(argv[1:])
 
603
    argv = [a.decode(bzrlib.user_encoding) for a in argv[1:]]
 
604
    ret = run_bzr_catch_errors(argv)
565
605
    mutter("return code %d", ret)
566
606
    return ret
567
607
 
568
608
 
569
609
def run_bzr_catch_errors(argv):
570
610
    try:
571
 
        try:
572
 
            return run_bzr(argv)
573
 
        finally:
574
 
            # do this here inside the exception wrappers to catch EPIPE
575
 
            sys.stdout.flush()
 
611
        return run_bzr(argv)
 
612
        # do this here inside the exception wrappers to catch EPIPE
 
613
        sys.stdout.flush()
576
614
    except Exception, e:
577
615
        # used to handle AssertionError and KeyboardInterrupt
578
616
        # specially here, but hopefully they're handled ok by the logger now
579
 
        import errno
580
 
        if (isinstance(e, IOError) 
581
 
            and hasattr(e, 'errno')
582
 
            and e.errno == errno.EPIPE):
583
 
            bzrlib.trace.note('broken pipe')
584
 
            return 3
585
 
        else:
586
 
            bzrlib.trace.log_exception()
587
 
            if os.environ.get('BZR_PDB'):
588
 
                print '**** entering debugger'
589
 
                import pdb
590
 
                pdb.post_mortem(sys.exc_traceback)
591
 
            return 3
 
617
        bzrlib.trace.report_exception(sys.exc_info(), sys.stderr)
 
618
        if os.environ.get('BZR_PDB'):
 
619
            print '**** entering debugger'
 
620
            import pdb
 
621
            pdb.post_mortem(sys.exc_traceback)
 
622
        return 3
592
623
 
593
624
if __name__ == '__main__':
594
625
    sys.exit(main(sys.argv))