~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

  • Committer: Martin Pool
  • Date: 2006-01-13 09:57:13 UTC
  • mto: This revision was merged to the branch mainline in revision 1611.
  • Revision ID: mbp@sourcefrog.net-20060113095713-1fa5912229a3898e
Review updates of pycurl transport

Split them out into 

  bzrlib.transport.http             common base
  bzrlib.transport.http._urllib     pure python
  bzrlib.transport.http._pycurl     calls pycurl

Update to work with robert's nice transport test multiplexer.

Add PyCurlTransport.has() which does just a HEAD request; should be faster.

Show diffs side-by-side

added added

removed removed

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