[Pkg-bazaar-commits] ./bzr/unstable r731: - merge plugin patch from john

Martin Pool mbp at sourcefrog.net
Fri Apr 10 08:20:50 UTC 2009


------------------------------------------------------------
revno: 731
committer: Martin Pool <mbp at sourcefrog.net>
timestamp: Wed 2005-06-22 16:04:43 +1000
message:
  - merge plugin patch from john
added:
  bzrlib/plugin.py
modified:
  bzrlib/__init__.py
  bzrlib/commands.py
  testbzr
-------------- next part --------------
=== modified file 'bzrlib/__init__.py'
--- a/bzrlib/__init__.py	2005-06-15 06:03:16 +0000
+++ b/bzrlib/__init__.py	2005-06-22 06:04:43 +0000
@@ -23,6 +23,7 @@
 from diff import compare_trees
 from trace import mutter, warning, open_tracefile
 from log import show_log
+from plugin import load_plugins
 import add
 
 BZRDIR = ".bzr"

=== modified file 'bzrlib/commands.py'
--- a/bzrlib/commands.py	2005-06-22 00:51:08 +0000
+++ b/bzrlib/commands.py	2005-06-22 06:04:43 +0000
@@ -26,6 +26,24 @@
      format_date
 
 
+plugin_cmds = {}
+
+
+def register_plugin_command(cmd):
+    "Utility function to help register a command"
+    global plugin_cmds
+    k = cmd.__name__
+    if k.startswith("cmd_"):
+        k_unsquished = _unsquish_command_name(k)
+    else:
+        k_unsquished = k
+    if not plugin_cmds.has_key(k_unsquished):
+        plugin_cmds[k_unsquished] = cmd
+    else:
+        log_error('Two plugins defined the same command: %r' % k)
+        log_error('Not loading the one in %r' % sys.modules[cmd.__module__])
+
+
 def _squish_command_name(cmd):
     return 'cmd_' + cmd.replace('-', '_')
 
@@ -68,100 +86,36 @@
         revs = int(revstr)
     return revs
 
-def _find_plugins():
-    """Find all python files which are plugins, and load their commands
-    to add to the list of "all commands"
-
-    The environment variable BZRPATH is considered a delimited set of
-    paths to look through. Each entry is searched for *.py files.
-    If a directory is found, it is also searched, but they are 
-    not searched recursively. This allows you to revctl the plugins.
-    
-    Inside the plugin should be a series of cmd_* function, which inherit from
-    the bzrlib.commands.Command class.
-    """
-    bzrpath = os.environ.get('BZRPLUGINPATH', '')
-
-    plugin_cmds = {} 
-    if not bzrpath:
-        return plugin_cmds
-    _platform_extensions = {
-        'win32':'.pyd',
-        'cygwin':'.dll',
-        'darwin':'.dylib',
-        'linux2':'.so'
-        }
-    if _platform_extensions.has_key(sys.platform):
-        platform_extension = _platform_extensions[sys.platform]
-    else:
-        platform_extension = None
-    for d in bzrpath.split(os.pathsep):
-        plugin_names = {} # This should really be a set rather than a dict
-        for f in os.listdir(d):
-            if f.endswith('.py'):
-                f = f[:-3]
-            elif f.endswith('.pyc') or f.endswith('.pyo'):
-                f = f[:-4]
-            elif platform_extension and f.endswith(platform_extension):
-                f = f[:-len(platform_extension)]
-                if f.endswidth('module'):
-                    f = f[:-len('module')]
-            else:
-                continue
-            if not plugin_names.has_key(f):
-                plugin_names[f] = True
-
-        plugin_names = plugin_names.keys()
-        plugin_names.sort()
-        try:
-            sys.path.insert(0, d)
-            for name in plugin_names:
-                try:
-                    old_module = None
-                    try:
-                        if sys.modules.has_key(name):
-                            old_module = sys.modules[name]
-                            del sys.modules[name]
-                        plugin = __import__(name, locals())
-                        for k in dir(plugin):
-                            if k.startswith('cmd_'):
-                                k_unsquished = _unsquish_command_name(k)
-                                if not plugin_cmds.has_key(k_unsquished):
-                                    plugin_cmds[k_unsquished] = getattr(plugin, k)
-                                else:
-                                    log_error('Two plugins defined the same command: %r' % k)
-                                    log_error('Not loading the one in %r in dir %r' % (name, d))
-                    finally:
-                        if old_module:
-                            sys.modules[name] = old_module
-                except ImportError, e:
-                    log_error('Unable to load plugin: %r from %r\n%s' % (name, d, e))
-        finally:
-            sys.path.pop(0)
-    return plugin_cmds
-
-def _get_cmd_dict(include_plugins=True):
+
+
+def _get_cmd_dict(plugins_override=True):
     d = {}
     for k, v in globals().iteritems():
         if k.startswith("cmd_"):
             d[_unsquish_command_name(k)] = v
-    if include_plugins:
-        d.update(_find_plugins())
+    # If we didn't load plugins, the plugin_cmds dict will be empty
+    if plugins_override:
+        d.update(plugin_cmds)
+    else:
+        d2 = plugin_cmds.copy()
+        d2.update(d)
+        d = d2
     return d
+
     
-def get_all_cmds(include_plugins=True):
+def get_all_cmds(plugins_override=True):
     """Return canonical name and class for all registered commands."""
-    for k, v in _get_cmd_dict(include_plugins=include_plugins).iteritems():
+    for k, v in _get_cmd_dict(plugins_override=plugins_override).iteritems():
         yield k,v
 
 
-def get_cmd_class(cmd,include_plugins=True):
+def get_cmd_class(cmd, plugins_override=True):
     """Return the canonical name and command class for a command.
     """
     cmd = str(cmd)                      # not unicode
 
     # first look up this command under the specified name
-    cmds = _get_cmd_dict(include_plugins=include_plugins)
+    cmds = _get_cmd_dict(plugins_override=plugins_override)
     try:
         return cmd, cmds[cmd]
     except KeyError:
@@ -1472,6 +1426,77 @@
     return argdict
 
 
+def _parse_master_args(argv):
+    """Parse the arguments that always go with the original command.
+    These are things like bzr --no-plugins, etc.
+
+    There are now 2 types of option flags. Ones that come *before* the command,
+    and ones that come *after* the command.
+    Ones coming *before* the command are applied against all possible commands.
+    And are generally applied before plugins are loaded.
+
+    The current list are:
+        --builtin   Allow plugins to load, but don't let them override builtin commands,
+                    they will still be allowed if they do not override a builtin.
+        --no-plugins    Don't load any plugins. This lets you get back to official source
+                        behavior.
+        --profile   Enable the hotspot profile before running the command.
+                    For backwards compatibility, this is also a non-master option.
+        --version   Spit out the version of bzr that is running and exit.
+                    This is also a non-master option.
+        --help      Run help and exit, also a non-master option (I think that should stay, though)
+
+    >>> argv, opts = _parse_master_args(['bzr', '--test'])
+    Traceback (most recent call last):
+    ...
+    BzrCommandError: Invalid master option: 'test'
+    >>> argv, opts = _parse_master_args(['bzr', '--version', 'command'])
+    >>> print argv
+    ['command']
+    >>> print opts['version']
+    True
+    >>> argv, opts = _parse_master_args(['bzr', '--profile', 'command', '--more-options'])
+    >>> print argv
+    ['command', '--more-options']
+    >>> print opts['profile']
+    True
+    >>> argv, opts = _parse_master_args(['bzr', '--no-plugins', 'command'])
+    >>> print argv
+    ['command']
+    >>> print opts['no-plugins']
+    True
+    >>> print opts['profile']
+    False
+    >>> argv, opts = _parse_master_args(['bzr', 'command', '--profile'])
+    >>> print argv
+    ['command', '--profile']
+    >>> print opts['profile']
+    False
+    """
+    master_opts = {'builtin':False,
+        'no-plugins':False,
+        'version':False,
+        'profile':False,
+        'help':False
+    }
+
+    # This is the point where we could hook into argv[0] to determine
+    # what front-end is supposed to be run
+    # For now, we are just ignoring it.
+    cmd_name = argv.pop(0)
+    for arg in argv[:]:
+        if arg[:2] != '--': # at the first non-option, we return the rest
+            break
+        arg = arg[2:] # Remove '--'
+        if arg not in master_opts:
+            # We could say that this is not an error, that we should
+            # just let it be handled by the main section instead
+            raise BzrCommandError('Invalid master option: %r' % arg)
+        argv.pop(0) # We are consuming this entry
+        master_opts[arg] = True
+    return argv, master_opts
+
+
 
 def run_bzr(argv):
     """Execute a command.
@@ -1481,15 +1506,28 @@
     """
     argv = [a.decode(bzrlib.user_encoding) for a in argv]
     
-    include_plugins=True
     try:
-        args, opts = parse_args(argv[1:])
+        # some options like --builtin and --no-plugins have special effects
+        argv, master_opts = _parse_master_args(argv)
+        if 'no-plugins' not in master_opts:
+            bzrlib.load_plugins()
+
+        args, opts = parse_args(argv)
+
+        if master_opts['help']:
+            from bzrlib.help import help
+            if argv:
+                help(argv[0])
+            else:
+                help()
+            return 0            
+            
         if 'help' in opts:
-            import help
+            from bzrlib.help import help
             if args:
-                help.help(args[0])
+                help(args[0])
             else:
-                help.help()
+                help()
             return 0
         elif 'version' in opts:
             show_version()
@@ -1504,14 +1542,15 @@
         return 1
           
 
-    canonical_cmd, cmd_class = get_cmd_class(cmd,include_plugins=include_plugins)
+    plugins_override = not (master_opts['builtin'])
+    canonical_cmd, cmd_class = get_cmd_class(cmd, plugins_override=plugins_override)
 
-    # global option
+    profile = master_opts['profile']
+    # For backwards compatibility, I would rather stick with --profile being a
+    # master/global option
     if 'profile' in opts:
         profile = True
         del opts['profile']
-    else:
-        profile = False
 
     # check options are reasonable
     allowed = cmd_class.takes_options

=== added file 'bzrlib/plugin.py'
--- a/bzrlib/plugin.py	1970-01-01 00:00:00 +0000
+++ b/bzrlib/plugin.py	2005-06-22 06:04:43 +0000
@@ -0,0 +1,92 @@
+# Copyright (C) 2004, 2005 by Canonical Ltd
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+# This module implements plug-in support.
+# Any python module in $BZR_PLUGIN_PATH will be imported upon initialization
+# of bzrlib (and then forgotten about).  In the plugin's main body, it should
+# update any bzrlib registries it wants to extend; for example, to add new
+# commands, import bzrlib.commands and add your new command to the
+# plugin_cmds variable.
+
+import sys, os, imp
+try:
+    set
+except NameError:
+    from sets import Set as set
+from bzrlib.trace import log_error
+
+
+def load_plugins():
+    """Find all python files which are plugins, and load them
+
+    The environment variable BZR_PLUGIN_PATH is considered a delimited set of
+    paths to look through. Each entry is searched for *.py files (and whatever
+    other extensions are used in the platform, such as *.pyd).
+    """
+    bzrpath = os.environ.get('BZR_PLUGIN_PATH', os.path.expanduser('~/.bzr/plugins'))
+
+    # The problem with imp.get_suffixes() is that it doesn't include
+    # .pyo which is technically valid
+    # It also means that "testmodule.so" will show up as both test and testmodule
+    # though it is only valid as 'test'
+    # but you should be careful, because "testmodule.py" loads as testmodule.
+    suffixes = imp.get_suffixes()
+    suffixes.append(('.pyo', 'rb', imp.PY_COMPILED))
+    package_entries = ['__init__.py', '__init__.pyc', '__init__.pyo']
+    for d in bzrpath.split(os.pathsep):
+        # going trough them one by one allows different plugins with the same
+        # filename in different directories in the path
+        if not d:
+            continue
+        plugin_names = set()
+        if not os.path.isdir(d):
+            continue
+        for f in os.listdir(d):
+            path = os.path.join(d, f)
+            if os.path.isdir(path):
+                for entry in package_entries:
+                    # This directory should be a package, and thus added to
+                    # the list
+                    if os.path.isfile(os.path.join(path, entry)):
+                        break
+                else: # This directory is not a package
+                    continue
+            else:
+                for suffix_info in suffixes:
+                    if f.endswith(suffix_info[0]):
+                        f = f[:-len(suffix_info[0])]
+                        if suffix_info[2] == imp.C_EXTENSION and f.endswith('module'):
+                            f = f[:-len('module')]
+                        break
+                else:
+                    continue
+            plugin_names.add(f)
+
+        plugin_names = list(plugin_names)
+        plugin_names.sort()
+        for name in plugin_names:
+            try:
+                plugin_info = imp.find_module(name, [d])
+                try:
+                    plugin = imp.load_module('bzrlib.plugin.' + name,
+                                             *plugin_info)
+                finally:
+                    if plugin_info[0] is not None:
+                        plugin_info[0].close()
+            except Exception, e:
+                log_error('Unable to load plugin: %r from %r\n%s' % (name, d, e))
+

=== modified file 'testbzr'
--- a/testbzr	2005-06-21 08:43:54 +0000
+++ b/testbzr	2005-06-22 06:04:43 +0000
@@ -153,6 +153,7 @@
     """Run a test involving creating a plugin to load,
     and making sure it is seen properly.
     """
+    orig_help = backtick('bzr help commands') # No plugins yet
     mkdir('plugin_test')
     f = open(os.path.join('plugin_test', 'myplug.py'), 'wb')
     f.write("""import bzrlib, bzrlib.commands
@@ -225,6 +226,9 @@
     
     runcmd(['mkdir', TESTDIR])
     cd(TESTDIR)
+    # This means that any command that is naively run in this directory
+    # Won't affect the parent directory.
+    runcmd('bzr init')
     test_root = os.getcwd()
 
     progress("introductory commands")



More information about the Pkg-bazaar-commits mailing list