[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