~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

  • Committer: Martin Pool
  • Date: 2005-06-22 06:04:43 UTC
  • Revision ID: mbp@sourcefrog.net-20050622060443-12fe7e3443dde3bb
- merge plugin patch from john

Show diffs side-by-side

added added

removed removed

Lines of Context:
26
26
     format_date
27
27
 
28
28
 
 
29
plugin_cmds = {}
 
30
 
 
31
 
 
32
def register_plugin_command(cmd):
 
33
    "Utility function to help register a command"
 
34
    global plugin_cmds
 
35
    k = cmd.__name__
 
36
    if k.startswith("cmd_"):
 
37
        k_unsquished = _unsquish_command_name(k)
 
38
    else:
 
39
        k_unsquished = k
 
40
    if not plugin_cmds.has_key(k_unsquished):
 
41
        plugin_cmds[k_unsquished] = cmd
 
42
    else:
 
43
        log_error('Two plugins defined the same command: %r' % k)
 
44
        log_error('Not loading the one in %r' % sys.modules[cmd.__module__])
 
45
 
 
46
 
29
47
def _squish_command_name(cmd):
30
48
    return 'cmd_' + cmd.replace('-', '_')
31
49
 
68
86
        revs = int(revstr)
69
87
    return revs
70
88
 
71
 
def _find_plugins():
72
 
    """Find all python files which are plugins, and load their commands
73
 
    to add to the list of "all commands"
74
 
 
75
 
    The environment variable BZRPATH is considered a delimited set of
76
 
    paths to look through. Each entry is searched for *.py files.
77
 
    If a directory is found, it is also searched, but they are 
78
 
    not searched recursively. This allows you to revctl the plugins.
79
 
    
80
 
    Inside the plugin should be a series of cmd_* function, which inherit from
81
 
    the bzrlib.commands.Command class.
82
 
    """
83
 
    bzrpath = os.environ.get('BZRPLUGINPATH', '')
84
 
 
85
 
    plugin_cmds = {} 
86
 
    if not bzrpath:
87
 
        return plugin_cmds
88
 
    _platform_extensions = {
89
 
        'win32':'.pyd',
90
 
        'cygwin':'.dll',
91
 
        'darwin':'.dylib',
92
 
        'linux2':'.so'
93
 
        }
94
 
    if _platform_extensions.has_key(sys.platform):
95
 
        platform_extension = _platform_extensions[sys.platform]
96
 
    else:
97
 
        platform_extension = None
98
 
    for d in bzrpath.split(os.pathsep):
99
 
        plugin_names = {} # This should really be a set rather than a dict
100
 
        for f in os.listdir(d):
101
 
            if f.endswith('.py'):
102
 
                f = f[:-3]
103
 
            elif f.endswith('.pyc') or f.endswith('.pyo'):
104
 
                f = f[:-4]
105
 
            elif platform_extension and f.endswith(platform_extension):
106
 
                f = f[:-len(platform_extension)]
107
 
                if f.endswidth('module'):
108
 
                    f = f[:-len('module')]
109
 
            else:
110
 
                continue
111
 
            if not plugin_names.has_key(f):
112
 
                plugin_names[f] = True
113
 
 
114
 
        plugin_names = plugin_names.keys()
115
 
        plugin_names.sort()
116
 
        try:
117
 
            sys.path.insert(0, d)
118
 
            for name in plugin_names:
119
 
                try:
120
 
                    old_module = None
121
 
                    try:
122
 
                        if sys.modules.has_key(name):
123
 
                            old_module = sys.modules[name]
124
 
                            del sys.modules[name]
125
 
                        plugin = __import__(name, locals())
126
 
                        for k in dir(plugin):
127
 
                            if k.startswith('cmd_'):
128
 
                                k_unsquished = _unsquish_command_name(k)
129
 
                                if not plugin_cmds.has_key(k_unsquished):
130
 
                                    plugin_cmds[k_unsquished] = getattr(plugin, k)
131
 
                                else:
132
 
                                    log_error('Two plugins defined the same command: %r' % k)
133
 
                                    log_error('Not loading the one in %r in dir %r' % (name, d))
134
 
                    finally:
135
 
                        if old_module:
136
 
                            sys.modules[name] = old_module
137
 
                except ImportError, e:
138
 
                    log_error('Unable to load plugin: %r from %r\n%s' % (name, d, e))
139
 
        finally:
140
 
            sys.path.pop(0)
141
 
    return plugin_cmds
142
 
 
143
 
def _get_cmd_dict(include_plugins=True):
 
89
 
 
90
 
 
91
def _get_cmd_dict(plugins_override=True):
144
92
    d = {}
145
93
    for k, v in globals().iteritems():
146
94
        if k.startswith("cmd_"):
147
95
            d[_unsquish_command_name(k)] = v
148
 
    if include_plugins:
149
 
        d.update(_find_plugins())
 
96
    # If we didn't load plugins, the plugin_cmds dict will be empty
 
97
    if plugins_override:
 
98
        d.update(plugin_cmds)
 
99
    else:
 
100
        d2 = plugin_cmds.copy()
 
101
        d2.update(d)
 
102
        d = d2
150
103
    return d
 
104
 
151
105
    
152
 
def get_all_cmds(include_plugins=True):
 
106
def get_all_cmds(plugins_override=True):
153
107
    """Return canonical name and class for all registered commands."""
154
 
    for k, v in _get_cmd_dict(include_plugins=include_plugins).iteritems():
 
108
    for k, v in _get_cmd_dict(plugins_override=plugins_override).iteritems():
155
109
        yield k,v
156
110
 
157
111
 
158
 
def get_cmd_class(cmd,include_plugins=True):
 
112
def get_cmd_class(cmd, plugins_override=True):
159
113
    """Return the canonical name and command class for a command.
160
114
    """
161
115
    cmd = str(cmd)                      # not unicode
162
116
 
163
117
    # first look up this command under the specified name
164
 
    cmds = _get_cmd_dict(include_plugins=include_plugins)
 
118
    cmds = _get_cmd_dict(plugins_override=plugins_override)
165
119
    try:
166
120
        return cmd, cmds[cmd]
167
121
    except KeyError:
1472
1426
    return argdict
1473
1427
 
1474
1428
 
 
1429
def _parse_master_args(argv):
 
1430
    """Parse the arguments that always go with the original command.
 
1431
    These are things like bzr --no-plugins, etc.
 
1432
 
 
1433
    There are now 2 types of option flags. Ones that come *before* the command,
 
1434
    and ones that come *after* the command.
 
1435
    Ones coming *before* the command are applied against all possible commands.
 
1436
    And are generally applied before plugins are loaded.
 
1437
 
 
1438
    The current list are:
 
1439
        --builtin   Allow plugins to load, but don't let them override builtin commands,
 
1440
                    they will still be allowed if they do not override a builtin.
 
1441
        --no-plugins    Don't load any plugins. This lets you get back to official source
 
1442
                        behavior.
 
1443
        --profile   Enable the hotspot profile before running the command.
 
1444
                    For backwards compatibility, this is also a non-master option.
 
1445
        --version   Spit out the version of bzr that is running and exit.
 
1446
                    This is also a non-master option.
 
1447
        --help      Run help and exit, also a non-master option (I think that should stay, though)
 
1448
 
 
1449
    >>> argv, opts = _parse_master_args(['bzr', '--test'])
 
1450
    Traceback (most recent call last):
 
1451
    ...
 
1452
    BzrCommandError: Invalid master option: 'test'
 
1453
    >>> argv, opts = _parse_master_args(['bzr', '--version', 'command'])
 
1454
    >>> print argv
 
1455
    ['command']
 
1456
    >>> print opts['version']
 
1457
    True
 
1458
    >>> argv, opts = _parse_master_args(['bzr', '--profile', 'command', '--more-options'])
 
1459
    >>> print argv
 
1460
    ['command', '--more-options']
 
1461
    >>> print opts['profile']
 
1462
    True
 
1463
    >>> argv, opts = _parse_master_args(['bzr', '--no-plugins', 'command'])
 
1464
    >>> print argv
 
1465
    ['command']
 
1466
    >>> print opts['no-plugins']
 
1467
    True
 
1468
    >>> print opts['profile']
 
1469
    False
 
1470
    >>> argv, opts = _parse_master_args(['bzr', 'command', '--profile'])
 
1471
    >>> print argv
 
1472
    ['command', '--profile']
 
1473
    >>> print opts['profile']
 
1474
    False
 
1475
    """
 
1476
    master_opts = {'builtin':False,
 
1477
        'no-plugins':False,
 
1478
        'version':False,
 
1479
        'profile':False,
 
1480
        'help':False
 
1481
    }
 
1482
 
 
1483
    # This is the point where we could hook into argv[0] to determine
 
1484
    # what front-end is supposed to be run
 
1485
    # For now, we are just ignoring it.
 
1486
    cmd_name = argv.pop(0)
 
1487
    for arg in argv[:]:
 
1488
        if arg[:2] != '--': # at the first non-option, we return the rest
 
1489
            break
 
1490
        arg = arg[2:] # Remove '--'
 
1491
        if arg not in master_opts:
 
1492
            # We could say that this is not an error, that we should
 
1493
            # just let it be handled by the main section instead
 
1494
            raise BzrCommandError('Invalid master option: %r' % arg)
 
1495
        argv.pop(0) # We are consuming this entry
 
1496
        master_opts[arg] = True
 
1497
    return argv, master_opts
 
1498
 
 
1499
 
1475
1500
 
1476
1501
def run_bzr(argv):
1477
1502
    """Execute a command.
1481
1506
    """
1482
1507
    argv = [a.decode(bzrlib.user_encoding) for a in argv]
1483
1508
    
1484
 
    include_plugins=True
1485
1509
    try:
1486
 
        args, opts = parse_args(argv[1:])
 
1510
        # some options like --builtin and --no-plugins have special effects
 
1511
        argv, master_opts = _parse_master_args(argv)
 
1512
        if 'no-plugins' not in master_opts:
 
1513
            bzrlib.load_plugins()
 
1514
 
 
1515
        args, opts = parse_args(argv)
 
1516
 
 
1517
        if master_opts['help']:
 
1518
            from bzrlib.help import help
 
1519
            if argv:
 
1520
                help(argv[0])
 
1521
            else:
 
1522
                help()
 
1523
            return 0            
 
1524
            
1487
1525
        if 'help' in opts:
1488
 
            import help
 
1526
            from bzrlib.help import help
1489
1527
            if args:
1490
 
                help.help(args[0])
 
1528
                help(args[0])
1491
1529
            else:
1492
 
                help.help()
 
1530
                help()
1493
1531
            return 0
1494
1532
        elif 'version' in opts:
1495
1533
            show_version()
1504
1542
        return 1
1505
1543
          
1506
1544
 
1507
 
    canonical_cmd, cmd_class = get_cmd_class(cmd,include_plugins=include_plugins)
 
1545
    plugins_override = not (master_opts['builtin'])
 
1546
    canonical_cmd, cmd_class = get_cmd_class(cmd, plugins_override=plugins_override)
1508
1547
 
1509
 
    # global option
 
1548
    profile = master_opts['profile']
 
1549
    # For backwards compatibility, I would rather stick with --profile being a
 
1550
    # master/global option
1510
1551
    if 'profile' in opts:
1511
1552
        profile = True
1512
1553
        del opts['profile']
1513
 
    else:
1514
 
        profile = False
1515
1554
 
1516
1555
    # check options are reasonable
1517
1556
    allowed = cmd_class.takes_options