Merge lp:~vila/bzr/411413-disable-plugin into lp:bzr
- 411413-disable-plugin
- Merge into bzr.dev
Status: | Merged |
---|---|
Merged at revision: | not available |
Proposed branch: | lp:~vila/bzr/411413-disable-plugin |
Merge into: | lp:bzr |
Diff against target: |
212 lines (+107/-5) 5 files modified
NEWS (+4/-0) bzrlib/help_topics/en/configuration.txt (+16/-0) bzrlib/plugin.py (+27/-3) bzrlib/tests/__init__.py (+1/-0) bzrlib/tests/test_plugins.py (+59/-2) |
To merge this branch: | bzr merge lp:~vila/bzr/411413-disable-plugin |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Martin Pool | Needs Fixing | ||
Review via email: mp+21435@code.launchpad.net |
Commit message
Description of the change
This fix bug #411413 by allowing the '-<plugin_name>' syntax to be used in BZR_PLUGIN_PATH.
Note that it works for:
- the automatic loading triggered by bzrlib.
- direct imports from other plugins, import bzrlib.
will now raise an import error
Matthew Fuller (fullermd) wrote : | # |
Vincent Ladeuil (vila) wrote : | # |
> > [...] allowing the '-<plugin_name>' syntax to be used in
> > BZR_PLUGIN_PATH.
>
> That seems... unclean.
More precisely ? Any better idea ?
We already use +user/-site/+core to give access to the "standard" parts of the paths.
Disabling a plugin is not something you do on a regular basis (or you'd better
just uninstall it).
Martin Pool (mbp) wrote : | # |
On 17 March 2010 03:09, Vincent Ladeuil <email address hidden> wrote:
>> > [...] allowing the '-<plugin_name>' syntax to be used in
>> > BZR_PLUGIN_PATH.
>>
>> That seems... unclean.
Matthew's right, that's just pretty weird to allow a mix of plugin
names and paths.
>
> More precisely ? Any better idea ?
>
> We already use +user/-site/+core to give access to the "standard" parts of the paths.
> Disabling a plugin is not something you do on a regular basis (or you'd better
> just uninstall it).
BZR_DISABLE_
--
Martin <http://
Martin Pool (mbp) : | # |
Matthew Fuller (fullermd) wrote : | # |
> More precisely ? Any better idea ?
I'd have a separate env var for it.
> We already use +user/-site/+core to give access to the "standard"
> parts of the paths.
Well, but it doesn't give paths to plugins. It gives paths to dirs
CONTAINING plugins. To then mix dirs with plugins, with names of
plugins, is icky.
Vincent Ladeuil (vila) wrote : | # |
> BZR_DISABLE_
Bah, of course, fixed.
Martin Pool (mbp) wrote : | # |
23 +BZR_DISABLE_
24 +~~~~~~
25 +
26 +Under special circumstances, it's better to disable a plugin (or
27 +several) rather than uninstalling them completely. Such plugins
28 +can be specified in the ``BZR_DISABLE_
29 +variable.
30 +
31 +In that case, ``bzr`` will stop loading the specified plugins and
32 +will raise an import error if you try to import them explicitly.
33 +
34 +Example:
35 +~~~~~~~~
36 +
The ReST syntax here looks strange, because "Example" will be another heading at the same level as BZR_DISABLE_
Users may not understand what "if you try to import them explicitly" means - indeed it doesn't mean a lot at the external user level. So you could say they will error if for example another plugin depends upon them?
202 + self.warnings = []
203 + def captured_
204 + self.warnings.
205 + self.overrideAt
It seems like there should already be a facility for this, but maybe not. Maybe you could lift it to a base class to save time?
Otherwise very good, thanks for fixing this!
bb:tweak
Vincent Ladeuil (vila) wrote : | # |
> The ReST syntax here looks strange, because "Example" will be another heading
> at the same level as BZR_DISABLE_
> after the word "explicitly".
>
Ok, after some wtf instants and some help from igc, I've fixed that and checked
the results in the sphinx generated html.
> Users may not understand what "if you try to import them explicitly" means -
> indeed it doesn't mean a lot at the external user level. So you could say
> they will error if for example another plugin depends upon them?
Added.
>
> 202 + self.warnings = []
> 203 + def captured_
> 204 + self.warnings.
> 205 + self.overrideAt
>
> It seems like there should already be a facility for this, but maybe not.
> Maybe you could lift it to a base class to save time?
Well, I struggled a bit with _capture_
So I agree we should provide an easier way :)
May be if all warnings, note, mutter was controlled by UIFactory and friends things will be clearer ?
Since that's the first time I know of that we need to capture trace.warning, I'd rather punt on this one.
I could make a further submission if you prefer, but *I*'d prefer some unification
of various warnings first (if only by providing more explicit names).
Preview Diff
1 | === modified file 'NEWS' |
2 | --- NEWS 2010-03-12 19:59:50 +0000 |
3 | +++ NEWS 2010-03-17 08:15:36 +0000 |
4 | @@ -51,6 +51,10 @@ |
5 | treats backslash as an escape character on Windows. (Gordon Tyler, |
6 | #392248) |
7 | |
8 | +* Plugins can be disabled by defining ``BZR_DISABLE_PLUGINS`` as |
9 | + a list of plugin names separated by ':' (';' on windows). |
10 | + (Vincent Ladeuil, #411413) |
11 | + |
12 | * Tree-shape conflicts can be resolved by providing ``--take-this`` and |
13 | ``--take-other`` to the ``bzr resolve`` command. Just marking the conflict |
14 | as resolved is still accessible via the ``--done`` default action. |
15 | |
16 | === modified file 'bzrlib/help_topics/en/configuration.txt' |
17 | --- bzrlib/help_topics/en/configuration.txt 2010-01-03 03:33:10 +0000 |
18 | +++ bzrlib/help_topics/en/configuration.txt 2010-03-17 08:15:36 +0000 |
19 | @@ -116,6 +116,22 @@ |
20 | Overriding the default site plugin directory: |
21 | ``BZR_PLUGIN_PATH='/path/to/my/site/plugins:-site':+user`` |
22 | |
23 | +BZR_DISABLE_PLUGINS |
24 | +~~~~~~~~~~~~~~~~~~~ |
25 | + |
26 | +Under special circumstances, it's better to disable a plugin (or |
27 | +several) rather than uninstalling them completely. Such plugins |
28 | +can be specified in the ``BZR_DISABLE_PLUGINS`` environment |
29 | +variable. |
30 | + |
31 | +In that case, ``bzr`` will stop loading the specified plugins and |
32 | +will raise an import error if you try to import them explicitly. |
33 | + |
34 | +Example: |
35 | +~~~~~~~~ |
36 | + |
37 | +Disabling ``myplugin`` and ``yourplugin``: |
38 | +``BZR_DISABLE_PLUGINS='myplugin:yourplugin'`` |
39 | |
40 | |
41 | BZRPATH |
42 | |
43 | === modified file 'bzrlib/plugin.py' |
44 | --- bzrlib/plugin.py 2010-03-12 13:41:08 +0000 |
45 | +++ bzrlib/plugin.py 2010-03-17 08:15:36 +0000 |
46 | @@ -91,6 +91,12 @@ |
47 | if path is None: |
48 | path = get_standard_plugins_path() |
49 | _mod_plugins.__path__ = path |
50 | + # Set up a blacklist for disabled plugins if any |
51 | + PluginBlackListImporter.blacklist = {} |
52 | + disabled_plugins = os.environ.get('BZR_DISABLE_PLUGINS', None) |
53 | + if disabled_plugins is not None: |
54 | + for name in disabled_plugins.split(os.pathsep): |
55 | + PluginBlackListImporter.blacklist['bzrlib.plugins.' + name] = True |
56 | return path |
57 | |
58 | |
59 | @@ -183,8 +189,8 @@ |
60 | try: |
61 | p = refs[p[1:]] |
62 | except KeyError: |
63 | - # Leave them untouched otherwise, user may have paths starting |
64 | - # with '+'... |
65 | + # Leave them untouched so user can still use paths starting |
66 | + # with '+' |
67 | pass |
68 | _append_new_path(paths, p) |
69 | |
70 | @@ -202,7 +208,7 @@ |
71 | files (and whatever other extensions are used in the platform, |
72 | such as *.pyd). |
73 | |
74 | - load_from_dirs() provides the underlying mechanism and is called with |
75 | + load_from_path() provides the underlying mechanism and is called with |
76 | the default directory list to provide the normal behaviour. |
77 | |
78 | :param path: The list of paths to search for plugins. By default, |
79 | @@ -290,6 +296,8 @@ |
80 | plugin_names.add(f) |
81 | |
82 | for name in plugin_names: |
83 | + if ('bzrlib.plugins.%s' % name) in PluginBlackListImporter.blacklist: |
84 | + continue |
85 | try: |
86 | exec "import bzrlib.plugins.%s" % name in {} |
87 | except KeyboardInterrupt: |
88 | @@ -476,3 +484,19 @@ |
89 | return version_string |
90 | |
91 | __version__ = property(_get__version__) |
92 | + |
93 | + |
94 | +class _PluginBlackListImporter(object): |
95 | + |
96 | + def __init__(self): |
97 | + self.blacklist = {} |
98 | + |
99 | + def find_module(self, fullname, parent_path=None): |
100 | + if fullname in self.blacklist: |
101 | + raise ImportError('%s is disabled' % fullname) |
102 | + return None |
103 | + |
104 | +PluginBlackListImporter = _PluginBlackListImporter() |
105 | +sys.meta_path.append(PluginBlackListImporter) |
106 | + |
107 | + |
108 | |
109 | === modified file 'bzrlib/tests/__init__.py' |
110 | --- bzrlib/tests/__init__.py 2010-03-10 06:02:51 +0000 |
111 | +++ bzrlib/tests/__init__.py 2010-03-17 08:15:36 +0000 |
112 | @@ -1519,6 +1519,7 @@ |
113 | 'BZR_PROGRESS_BAR': None, |
114 | 'BZR_LOG': None, |
115 | 'BZR_PLUGIN_PATH': None, |
116 | + 'BZR_DISABLE_PLUGINS': None, |
117 | 'BZR_CONCURRENCY': None, |
118 | # Make sure that any text ui tests are consistent regardless of |
119 | # the environment the test case is run in; you may want tests that |
120 | |
121 | === modified file 'bzrlib/tests/test_plugins.py' |
122 | --- bzrlib/tests/test_plugins.py 2010-03-15 11:17:44 +0000 |
123 | +++ bzrlib/tests/test_plugins.py 2010-03-17 08:15:36 +0000 |
124 | @@ -31,6 +31,7 @@ |
125 | plugin, |
126 | plugins, |
127 | tests, |
128 | + trace, |
129 | ) |
130 | |
131 | |
132 | @@ -38,6 +39,25 @@ |
133 | |
134 | class TestPluginMixin(object): |
135 | |
136 | + def create_plugin(self, name, source='', dir='.', file_name=None): |
137 | + if file_name is None: |
138 | + file_name = name + '.py' |
139 | + # 'source' must not fail to load |
140 | + path = osutils.pathjoin(dir, file_name) |
141 | + f = open(path, 'w') |
142 | + self.addCleanup(os.unlink, path) |
143 | + try: |
144 | + f.write(source + '\n') |
145 | + finally: |
146 | + f.close() |
147 | + |
148 | + def create_plugin_package(self, name, source='', dir='.'): |
149 | + plugin_dir = osutils.pathjoin(dir, name) |
150 | + os.mkdir(plugin_dir) |
151 | + self.addCleanup(osutils.rmtree, plugin_dir) |
152 | + self.create_plugin(name, source, dir=plugin_dir, |
153 | + file_name='__init__.py') |
154 | + |
155 | def _unregister_plugin(self, name): |
156 | """Remove the plugin from sys.modules and the bzrlib namespace.""" |
157 | py_name = 'bzrlib.plugins.%s' % name |
158 | @@ -47,11 +67,11 @@ |
159 | delattr(bzrlib.plugins, name) |
160 | |
161 | def assertPluginUnknown(self, name): |
162 | - self.failIf(getattr(bzrlib.plugins, 'plugin', None) is not None) |
163 | + self.failIf(getattr(bzrlib.plugins, name, None) is not None) |
164 | self.failIf('bzrlib.plugins.%s' % name in sys.modules) |
165 | |
166 | def assertPluginKnown(self, name): |
167 | - self.failUnless(getattr(bzrlib.plugins, 'plugin', None) is not None) |
168 | + self.failUnless(getattr(bzrlib.plugins, name, None) is not None) |
169 | self.failUnless('bzrlib.plugins.%s' % name in sys.modules) |
170 | |
171 | |
172 | @@ -723,3 +743,40 @@ |
173 | self.check_path(['+foo', '-bar', self.core, self.site], |
174 | ['+foo', '-bar']) |
175 | |
176 | + |
177 | +class TestDisablePlugin(tests.TestCaseInTempDir, TestPluginMixin): |
178 | + |
179 | + def setUp(self): |
180 | + super(TestDisablePlugin, self).setUp() |
181 | + self.create_plugin_package('test_foo') |
182 | + # Make sure we don't pollute the plugins namespace |
183 | + self.overrideAttr(plugins, '__path__') |
184 | + # Be paranoid in case a test fail |
185 | + self.addCleanup(self._unregister_plugin, 'test_foo') |
186 | + |
187 | + def test_cannot_import(self): |
188 | + osutils.set_or_unset_env('BZR_DISABLE_PLUGINS', 'test_foo') |
189 | + plugin.set_plugins_path(['.']) |
190 | + try: |
191 | + import bzrlib.plugins.test_foo |
192 | + except ImportError: |
193 | + pass |
194 | + self.assertPluginUnknown('test_foo') |
195 | + |
196 | + def test_regular_load(self): |
197 | + self.overrideAttr(plugin, '_loaded', False) |
198 | + plugin.load_plugins(['.']) |
199 | + self.assertPluginKnown('test_foo') |
200 | + |
201 | + def test_not_loaded(self): |
202 | + self.warnings = [] |
203 | + def captured_warning(*args, **kwargs): |
204 | + self.warnings.append((args, kwargs)) |
205 | + self.overrideAttr(trace, 'warning', captured_warning) |
206 | + self.overrideAttr(plugin, '_loaded', False) |
207 | + osutils.set_or_unset_env('BZR_DISABLE_PLUGINS', 'test_foo') |
208 | + plugin.load_plugins(plugin.set_plugins_path(['.'])) |
209 | + self.assertPluginUnknown('test_foo') |
210 | + # Make sure we don't warn about the plugin ImportError since this has |
211 | + # been *requested* by the user. |
212 | + self.assertLength(0, self.warnings) |
> [...] allowing the '-<plugin_name>' syntax to be used in
> BZR_PLUGIN_PATH.
That seems... unclean.