/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

  • Committer: John Arbash Meinel
  • Date: 2006-08-22 21:31:23 UTC
  • mto: This revision was merged to the branch mainline in revision 1953.
  • Revision ID: john@arbash-meinel.com-20060822213123-15cf5e4c6a9ab4d2
Don't raise UnicodeCommand on request, instead just let it fall out when we get to NoSuchCommand

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2006 by Canonical Ltd
 
2
#
 
3
# This program is free software; you can redistribute it and/or modify
 
4
# it under the terms of the GNU General Public License as published by
 
5
# the Free Software Foundation; either version 2 of the License, or
 
6
# (at your option) any later version.
 
7
#
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
# GNU General Public License for more details.
 
12
#
 
13
# You should have received a copy of the GNU General Public License
 
14
# along with this program; if not, write to the Free Software
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
 
 
17
 
 
18
# TODO: probably should say which arguments are candidates for glob
 
19
# expansion on windows and do that at the command level.
 
20
 
 
21
# TODO: Define arguments by objects, rather than just using names.
 
22
# 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.
 
27
 
 
28
# TODO: "--profile=cum", to change sort order.  Is there any value in leaving
 
29
# the profile output behind so it can be interactively examined?
 
30
 
 
31
import codecs
 
32
import errno
 
33
import os
 
34
from warnings import warn
 
35
import sys
 
36
 
 
37
import bzrlib
 
38
import bzrlib.errors as errors
 
39
from bzrlib.errors import (BzrError,
 
40
                           BzrCommandError,
 
41
                           BzrCheckError,
 
42
                           NotBranchError)
 
43
from bzrlib import option
 
44
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
 
 
51
plugin_cmds = {}
 
52
 
 
53
 
 
54
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
    """
 
62
    global plugin_cmds
 
63
    k = cmd.__name__
 
64
 
 
65
    if k.startswith("cmd_"):
 
66
        k_unsquished = _unsquish_command_name(k)
 
67
    else:
 
68
        k_unsquished = k
 
69
    if not plugin_cmds.has_key(k_unsquished):
 
70
        plugin_cmds[k_unsquished] = cmd
 
71
        mutter('registered plugin command %s', k_unsquished)
 
72
        if decorate and k_unsquished in builtin_command_names():
 
73
            return _builtin_commands()[k_unsquished]
 
74
    elif decorate:
 
75
        result = plugin_cmds[k_unsquished]
 
76
        plugin_cmds[k_unsquished] = cmd
 
77
        return result
 
78
    else:
 
79
        log_error('Two plugins defined the same command: %r' % k)
 
80
        log_error('Not loading the one in %r' % sys.modules[cmd.__module__])
 
81
 
 
82
 
 
83
def _squish_command_name(cmd):
 
84
    return 'cmd_' + cmd.replace('-', '_')
 
85
 
 
86
 
 
87
def _unsquish_command_name(cmd):
 
88
    assert cmd.startswith("cmd_")
 
89
    return cmd[4:].replace('_','-')
 
90
 
 
91
 
 
92
def _builtin_commands():
 
93
    import bzrlib.builtins
 
94
    r = {}
 
95
    builtins = bzrlib.builtins.__dict__
 
96
    for name in builtins:
 
97
        if name.startswith("cmd_"):
 
98
            real_name = _unsquish_command_name(name)
 
99
            r[real_name] = builtins[name]
 
100
    return r
 
101
            
 
102
 
 
103
def builtin_command_names():
 
104
    """Return list of builtin command names."""
 
105
    return _builtin_commands().keys()
 
106
    
 
107
 
 
108
def plugin_command_names():
 
109
    return plugin_cmds.keys()
 
110
 
 
111
 
 
112
def _get_cmd_dict(plugins_override=True):
 
113
    """Return name->class mapping for all commands."""
 
114
    d = _builtin_commands()
 
115
    if plugins_override:
 
116
        d.update(plugin_cmds)
 
117
    return d
 
118
 
 
119
    
 
120
def get_all_cmds(plugins_override=True):
 
121
    """Return canonical name and class for all registered commands."""
 
122
    for k, v in _get_cmd_dict(plugins_override=plugins_override).iteritems():
 
123
        yield k,v
 
124
 
 
125
 
 
126
def get_cmd_object(cmd_name, plugins_override=True):
 
127
    """Return the canonical name and command class for a command.
 
128
 
 
129
    plugins_override
 
130
        If true, plugin commands can override builtins.
 
131
    """
 
132
    from bzrlib.externalcommand import ExternalCommand
 
133
 
 
134
    try:
 
135
        cmd_name = str(cmd_name)
 
136
    except UnicodeError:
 
137
        pass 
 
138
        # We want only 'ascii' command names, but the user may have typed
 
139
        # in a Unicode name. In that case, they should just get a
 
140
        # 'command not found' error later.
 
141
        # In the future, we may actually support Unicode command names.
 
142
 
 
143
    # first look up this command under the specified name
 
144
    cmds = _get_cmd_dict(plugins_override=plugins_override)
 
145
    try:
 
146
        return cmds[cmd_name]()
 
147
    except KeyError:
 
148
        pass
 
149
 
 
150
    # look for any command which claims this as an alias
 
151
    for real_cmd_name, cmd_class in cmds.iteritems():
 
152
        if cmd_name in cmd_class.aliases:
 
153
            return cmd_class()
 
154
 
 
155
    cmd_obj = ExternalCommand.find_command(cmd_name)
 
156
    if cmd_obj:
 
157
        return cmd_obj
 
158
 
 
159
    raise BzrCommandError('unknown command "%s"' % cmd_name)
 
160
 
 
161
 
 
162
class Command(object):
 
163
    """Base class for commands.
 
164
 
 
165
    Commands are the heart of the command-line bzr interface.
 
166
 
 
167
    The command object mostly handles the mapping of command-line
 
168
    parameters into one or more bzrlib operations, and of the results
 
169
    into textual output.
 
170
 
 
171
    Commands normally don't have any state.  All their arguments are
 
172
    passed in to the run method.  (Subclasses may take a different
 
173
    policy if the behaviour of the instance needs to depend on e.g. a
 
174
    shell plugin and not just its Python class.)
 
175
 
 
176
    The docstring for an actual command should give a single-line
 
177
    summary, then a complete description of the command.  A grammar
 
178
    description will be inserted.
 
179
 
 
180
    aliases
 
181
        Other accepted names for this command.
 
182
 
 
183
    takes_args
 
184
        List of argument forms, marked with whether they are optional,
 
185
        repeated, etc.
 
186
 
 
187
                Examples:
 
188
 
 
189
                ['to_location', 'from_branch?', 'file*']
 
190
 
 
191
                'to_location' is required
 
192
                'from_branch' is optional
 
193
                'file' can be specified 0 or more times
 
194
 
 
195
    takes_options
 
196
        List of options that may be given for this command.  These can
 
197
        be either strings, referring to globally-defined options,
 
198
        or option objects.  Retrieve through options().
 
199
 
 
200
    hidden
 
201
        If true, this command isn't advertised.  This is typically
 
202
        for commands intended for expert users.
 
203
 
 
204
    encoding_type
 
205
        Command objects will get a 'outf' attribute, which has been
 
206
        setup to properly handle encoding of unicode strings.
 
207
        encoding_type determines what will happen when characters cannot
 
208
        be encoded
 
209
            strict - abort if we cannot decode
 
210
            replace - put in a bogus character (typically '?')
 
211
            exact - do not encode sys.stdout
 
212
 
 
213
    """
 
214
    aliases = []
 
215
    takes_args = []
 
216
    takes_options = []
 
217
    encoding_type = 'strict'
 
218
 
 
219
    hidden = False
 
220
    
 
221
    def __init__(self):
 
222
        """Construct an instance of this command."""
 
223
        if self.__doc__ == Command.__doc__:
 
224
            warn("No help message set for %r" % self)
 
225
 
 
226
    def options(self):
 
227
        """Return dict of valid options for this command.
 
228
 
 
229
        Maps from long option name to option object."""
 
230
        r = dict()
 
231
        r['help'] = Option.OPTIONS['help']
 
232
        for o in self.takes_options:
 
233
            if isinstance(o, basestring):
 
234
                o = Option.OPTIONS[o]
 
235
            r[o.name] = o
 
236
        return r
 
237
 
 
238
    def _setup_outf(self):
 
239
        """Return a file linked to stdout, which has proper encoding."""
 
240
        assert self.encoding_type in ['strict', 'exact', 'replace']
 
241
 
 
242
        # Originally I was using self.stdout, but that looks
 
243
        # *way* too much like sys.stdout
 
244
        if self.encoding_type == 'exact':
 
245
            self.outf = sys.stdout
 
246
            return
 
247
 
 
248
        output_encoding = bzrlib.osutils.get_terminal_encoding()
 
249
 
 
250
        # use 'replace' so that we don't abort if trying to write out
 
251
        # in e.g. the default C locale.
 
252
        self.outf = codecs.getwriter(output_encoding)(sys.stdout, errors=self.encoding_type)
 
253
        # For whatever reason codecs.getwriter() does not advertise its encoding
 
254
        # it just returns the encoding of the wrapped file, which is completely
 
255
        # bogus. So set the attribute, so we can find the correct encoding later.
 
256
        self.outf.encoding = output_encoding
 
257
 
 
258
    @deprecated_method(zero_eight)
 
259
    def run_argv(self, argv):
 
260
        """Parse command line and run.
 
261
        
 
262
        See run_argv_aliases for the 0.8 and beyond api.
 
263
        """
 
264
        return self.run_argv_aliases(argv)
 
265
 
 
266
    def run_argv_aliases(self, argv, alias_argv=None):
 
267
        """Parse the command line and run with extra aliases in alias_argv."""
 
268
        if argv is None:
 
269
            warn("Passing None for [] is deprecated from bzrlib 0.10", 
 
270
                 DeprecationWarning, stacklevel=2)
 
271
            argv = []
 
272
        args, opts = parse_args(self, argv, alias_argv)
 
273
        if 'help' in opts:  # e.g. bzr add --help
 
274
            from bzrlib.help import help_on_command
 
275
            help_on_command(self.name())
 
276
            return 0
 
277
        # mix arguments and options into one dictionary
 
278
        cmdargs = _match_argform(self.name(), self.takes_args, args)
 
279
        cmdopts = {}
 
280
        for k, v in opts.items():
 
281
            cmdopts[k.replace('-', '_')] = v
 
282
 
 
283
        all_cmd_args = cmdargs.copy()
 
284
        all_cmd_args.update(cmdopts)
 
285
 
 
286
        self._setup_outf()
 
287
 
 
288
        return self.run(**all_cmd_args)
 
289
    
 
290
    def run(self):
 
291
        """Actually run the command.
 
292
 
 
293
        This is invoked with the options and arguments bound to
 
294
        keyword parameters.
 
295
 
 
296
        Return 0 or None if the command was successful, or a non-zero
 
297
        shell error code if not.  It's OK for this method to allow
 
298
        an exception to raise up.
 
299
        """
 
300
        raise NotImplementedError('no implementation of command %r' 
 
301
                                  % self.name())
 
302
 
 
303
    def help(self):
 
304
        """Return help message for this class."""
 
305
        from inspect import getdoc
 
306
        if self.__doc__ is Command.__doc__:
 
307
            return None
 
308
        return getdoc(self)
 
309
 
 
310
    def name(self):
 
311
        return _unsquish_command_name(self.__class__.__name__)
 
312
 
 
313
    def plugin_name(self):
 
314
        """Get the name of the plugin that provides this command.
 
315
 
 
316
        :return: The name of the plugin or None if the command is builtin.
 
317
        """
 
318
        mod_parts = self.__module__.split('.')
 
319
        if len(mod_parts) >= 3 and mod_parts[1] == 'plugins':
 
320
            return mod_parts[2]
 
321
        else:
 
322
            return None
 
323
 
 
324
 
 
325
def parse_spec(spec):
 
326
    """
 
327
    >>> parse_spec(None)
 
328
    [None, None]
 
329
    >>> parse_spec("./")
 
330
    ['./', None]
 
331
    >>> parse_spec("../@")
 
332
    ['..', -1]
 
333
    >>> parse_spec("../f/@35")
 
334
    ['../f', 35]
 
335
    >>> parse_spec('./@revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67')
 
336
    ['.', 'revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67']
 
337
    """
 
338
    if spec is None:
 
339
        return [None, None]
 
340
    if '/@' in spec:
 
341
        parsed = spec.split('/@')
 
342
        assert len(parsed) == 2
 
343
        if parsed[1] == "":
 
344
            parsed[1] = -1
 
345
        else:
 
346
            try:
 
347
                parsed[1] = int(parsed[1])
 
348
            except ValueError:
 
349
                pass # We can allow stuff like ./@revid:blahblahblah
 
350
            else:
 
351
                assert parsed[1] >=0
 
352
    else:
 
353
        parsed = [spec, None]
 
354
    return parsed
 
355
 
 
356
def parse_args(command, argv, alias_argv=None):
 
357
    """Parse command line.
 
358
    
 
359
    Arguments and options are parsed at this level before being passed
 
360
    down to specific command handlers.  This routine knows, from a
 
361
    lookup table, something about the available options, what optargs
 
362
    they take, and which commands will accept them.
 
363
    """
 
364
    # TODO: make it a method of the Command?
 
365
    parser = option.get_optparser(command.options())
 
366
    if alias_argv is not None:
 
367
        args = alias_argv + argv
 
368
    else:
 
369
        args = argv
 
370
 
 
371
    options, args = parser.parse_args(args)
 
372
    opts = dict([(k, v) for k, v in options.__dict__.iteritems() if 
 
373
                 v is not option.OptionParser.DEFAULT_VALUE])
 
374
    return args, opts
 
375
 
 
376
 
 
377
def _match_argform(cmd, takes_args, args):
 
378
    argdict = {}
 
379
 
 
380
    # step through args and takes_args, allowing appropriate 0-many matches
 
381
    for ap in takes_args:
 
382
        argname = ap[:-1]
 
383
        if ap[-1] == '?':
 
384
            if args:
 
385
                argdict[argname] = args.pop(0)
 
386
        elif ap[-1] == '*': # all remaining arguments
 
387
            if args:
 
388
                argdict[argname + '_list'] = args[:]
 
389
                args = []
 
390
            else:
 
391
                argdict[argname + '_list'] = None
 
392
        elif ap[-1] == '+':
 
393
            if not args:
 
394
                raise BzrCommandError("command %r needs one or more %s"
 
395
                        % (cmd, argname.upper()))
 
396
            else:
 
397
                argdict[argname + '_list'] = args[:]
 
398
                args = []
 
399
        elif ap[-1] == '$': # all but one
 
400
            if len(args) < 2:
 
401
                raise BzrCommandError("command %r needs one or more %s"
 
402
                        % (cmd, argname.upper()))
 
403
            argdict[argname + '_list'] = args[:-1]
 
404
            args[:-1] = []
 
405
        else:
 
406
            # just a plain arg
 
407
            argname = ap
 
408
            if not args:
 
409
                raise BzrCommandError("command %r requires argument %s"
 
410
                        % (cmd, argname.upper()))
 
411
            else:
 
412
                argdict[argname] = args.pop(0)
 
413
            
 
414
    if args:
 
415
        raise BzrCommandError("extra argument to command %s: %s"
 
416
                              % (cmd, args[0]))
 
417
 
 
418
    return argdict
 
419
 
 
420
 
 
421
 
 
422
def apply_profiled(the_callable, *args, **kwargs):
 
423
    import hotshot
 
424
    import tempfile
 
425
    import hotshot.stats
 
426
    pffileno, pfname = tempfile.mkstemp()
 
427
    try:
 
428
        prof = hotshot.Profile(pfname)
 
429
        try:
 
430
            ret = prof.runcall(the_callable, *args, **kwargs) or 0
 
431
        finally:
 
432
            prof.close()
 
433
        stats = hotshot.stats.load(pfname)
 
434
        stats.strip_dirs()
 
435
        stats.sort_stats('cum')   # 'time'
 
436
        ## XXX: Might like to write to stderr or the trace file instead but
 
437
        ## print_stats seems hardcoded to stdout
 
438
        stats.print_stats(20)
 
439
        return ret
 
440
    finally:
 
441
        os.close(pffileno)
 
442
        os.remove(pfname)
 
443
 
 
444
 
 
445
def apply_lsprofiled(filename, the_callable, *args, **kwargs):
 
446
    from bzrlib.lsprof import profile
 
447
    import cPickle
 
448
    ret, stats = profile(the_callable, *args, **kwargs)
 
449
    stats.sort()
 
450
    if filename is None:
 
451
        stats.pprint()
 
452
    else:
 
453
        stats.freeze()
 
454
        cPickle.dump(stats, open(filename, 'w'), 2)
 
455
        print 'Profile data written to %r.' % filename
 
456
    return ret
 
457
 
 
458
 
 
459
def get_alias(cmd):
 
460
    """Return an expanded alias, or None if no alias exists"""
 
461
    import bzrlib.config
 
462
    alias = bzrlib.config.GlobalConfig().get_alias(cmd)
 
463
    if (alias):
 
464
        return alias.split(' ')
 
465
    return None
 
466
 
 
467
 
 
468
def run_bzr(argv):
 
469
    """Execute a command.
 
470
 
 
471
    This is similar to main(), but without all the trappings for
 
472
    logging and error handling.  
 
473
    
 
474
    argv
 
475
       The command-line arguments, without the program name from argv[0]
 
476
       These should already be decoded. All library/test code calling
 
477
       run_bzr should be passing valid strings (don't need decoding).
 
478
    
 
479
    Returns a command status or raises an exception.
 
480
 
 
481
    Special master options: these must come before the command because
 
482
    they control how the command is interpreted.
 
483
 
 
484
    --no-plugins
 
485
        Do not load plugin modules at all
 
486
 
 
487
    --no-aliases
 
488
        Do not allow aliases
 
489
 
 
490
    --builtin
 
491
        Only use builtin commands.  (Plugins are still allowed to change
 
492
        other behaviour.)
 
493
 
 
494
    --profile
 
495
        Run under the Python hotshot profiler.
 
496
 
 
497
    --lsprof
 
498
        Run under the Python lsprof profiler.
 
499
    """
 
500
    argv = list(argv)
 
501
 
 
502
    opt_lsprof = opt_profile = opt_no_plugins = opt_builtin =  \
 
503
                opt_no_aliases = False
 
504
    opt_lsprof_file = None
 
505
 
 
506
    # --no-plugins is handled specially at a very early stage. We need
 
507
    # to load plugins before doing other command parsing so that they
 
508
    # can override commands, but this needs to happen first.
 
509
 
 
510
    argv_copy = []
 
511
    i = 0
 
512
    while i < len(argv):
 
513
        a = argv[i]
 
514
        if a == '--profile':
 
515
            opt_profile = True
 
516
        elif a == '--lsprof':
 
517
            opt_lsprof = True
 
518
        elif a == '--lsprof-file':
 
519
            opt_lsprof = True
 
520
            opt_lsprof_file = argv[i + 1]
 
521
            i += 1
 
522
        elif a == '--no-plugins':
 
523
            opt_no_plugins = True
 
524
        elif a == '--no-aliases':
 
525
            opt_no_aliases = True
 
526
        elif a == '--builtin':
 
527
            opt_builtin = True
 
528
        elif a in ('--quiet', '-q'):
 
529
            be_quiet()
 
530
        else:
 
531
            argv_copy.append(a)
 
532
        i += 1
 
533
 
 
534
    argv = argv_copy
 
535
    if (not argv):
 
536
        from bzrlib.builtins import cmd_help
 
537
        cmd_help().run_argv_aliases([])
 
538
        return 0
 
539
 
 
540
    if argv[0] == '--version':
 
541
        from bzrlib.version import show_version
 
542
        show_version()
 
543
        return 0
 
544
        
 
545
    if not opt_no_plugins:
 
546
        from bzrlib.plugin import load_plugins
 
547
        load_plugins()
 
548
    else:
 
549
        from bzrlib.plugin import disable_plugins
 
550
        disable_plugins()
 
551
 
 
552
    alias_argv = None
 
553
 
 
554
    if not opt_no_aliases:
 
555
        alias_argv = get_alias(argv[0])
 
556
        if alias_argv:
 
557
            alias_argv = [a.decode(bzrlib.user_encoding) for a in alias_argv]
 
558
            argv[0] = alias_argv.pop(0)
 
559
 
 
560
    cmd = argv.pop(0)
 
561
    try:
 
562
        cmd = cmd.encode('ascii')
 
563
    except UnicodeError:
 
564
        pass 
 
565
        # We want only 'ascii' command names, but the user may have typed
 
566
        # in a Unicode name. In that case, they should just get a
 
567
        # 'command not found' error later.
 
568
 
 
569
    cmd_obj = get_cmd_object(cmd, plugins_override=not opt_builtin)
 
570
    if not getattr(cmd_obj.run_argv, 'is_deprecated', False):
 
571
        run = cmd_obj.run_argv
 
572
        run_argv = [argv]
 
573
    else:
 
574
        run = cmd_obj.run_argv_aliases
 
575
        run_argv = [argv, alias_argv]
 
576
 
 
577
    try:
 
578
        if opt_lsprof:
 
579
            ret = apply_lsprofiled(opt_lsprof_file, run, *run_argv)
 
580
        elif opt_profile:
 
581
            ret = apply_profiled(run, *run_argv)
 
582
        else:
 
583
            ret = run(*run_argv)
 
584
        return ret or 0
 
585
    finally:
 
586
        # reset, in case we may do other commands later within the same process
 
587
        be_quiet(False)
 
588
 
 
589
def display_command(func):
 
590
    """Decorator that suppresses pipe/interrupt errors."""
 
591
    def ignore_pipe(*args, **kwargs):
 
592
        try:
 
593
            result = func(*args, **kwargs)
 
594
            sys.stdout.flush()
 
595
            return result
 
596
        except IOError, e:
 
597
            if not hasattr(e, 'errno'):
 
598
                raise
 
599
            if e.errno != errno.EPIPE:
 
600
                # Win32 raises IOError with errno=0 on a broken pipe
 
601
                if sys.platform != 'win32' or e.errno != 0:
 
602
                    raise
 
603
            pass
 
604
        except KeyboardInterrupt:
 
605
            pass
 
606
    return ignore_pipe
 
607
 
 
608
 
 
609
def main(argv):
 
610
    import bzrlib.ui
 
611
    from bzrlib.ui.text import TextUIFactory
 
612
    bzrlib.ui.ui_factory = TextUIFactory()
 
613
    argv = [a.decode(bzrlib.user_encoding) for a in argv[1:]]
 
614
    ret = run_bzr_catch_errors(argv)
 
615
    mutter("return code %d", ret)
 
616
    return ret
 
617
 
 
618
 
 
619
def run_bzr_catch_errors(argv):
 
620
    try:
 
621
        return run_bzr(argv)
 
622
        # do this here inside the exception wrappers to catch EPIPE
 
623
        sys.stdout.flush()
 
624
    except Exception, e:
 
625
        # used to handle AssertionError and KeyboardInterrupt
 
626
        # specially here, but hopefully they're handled ok by the logger now
 
627
        bzrlib.trace.report_exception(sys.exc_info(), sys.stderr)
 
628
        if os.environ.get('BZR_PDB'):
 
629
            print '**** entering debugger'
 
630
            import pdb
 
631
            pdb.post_mortem(sys.exc_traceback)
 
632
        return 3
 
633
 
 
634
if __name__ == '__main__':
 
635
    sys.exit(main(sys.argv))