Skip to content
This repository was archived by the owner on Dec 16, 2022. It is now read-only.

Commit 967660a

Browse files
authored
remove namespace plugin mechanism (#4188)
1 parent c09833c commit 967660a

File tree

21 files changed

+15
-407
lines changed

21 files changed

+15
-407
lines changed

allennlp/common/plugins.py

+13-38
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,23 @@
11
"""
22
Plugin management.
33
4-
AllenNLP supports loading "plugins" dynamically. A plugin is just a Python package that is found
5-
by AllenNLP with the methods provided in this module.
6-
7-
There are two ways of declaring plugins for discovery:
8-
9-
- Writing the package name to import in a file (typically ".allennlp_plugins", in the current
10-
directory from which the command "allennlp" is run). This is the simplest approach.
11-
12-
- Creating a folder called "allennlp_plugins" that's in the Python path when you run the
13-
"allennlp" command (typically under your project's root directory), then creating a subfolder
14-
with the name you want and creating an "__init__.py" file that imports the code you want (e.g.,
15-
your Python package). This option is preferred when you want to create a pip-installable
16-
package and you want to make your AllenNLP plugin available when users install your package.
17-
See [allennlp-server](https://github.com/allenai/allennlp-server) for an example.
4+
AllenNLP supports loading "plugins" dynamically. A plugin is just a Python package that
5+
can be found and imported by AllenNLP. This is done by creating a file named `.allennlp_plugins`
6+
in the directory where the `allennlp` command is run that lists the modules that should be loaded,
7+
one per line.
188
"""
9+
1910
import importlib
2011
import logging
2112
import os
22-
import pkgutil
23-
import sys
2413
from typing import Iterable
2514

26-
from allennlp.common.util import push_python_path
15+
from allennlp.common.util import push_python_path, import_module_and_submodules
2716

2817
logger = logging.getLogger(__name__)
2918

3019

31-
def discover_namespace_plugins(
32-
namespace_name: str = "allennlp_plugins",
33-
) -> Iterable[pkgutil.ModuleInfo]:
34-
"""
35-
Returns an iterable of the plugins found, declared within the namespace package `namespace_name`.
36-
"""
37-
try:
38-
reload = namespace_name in sys.modules
39-
40-
namespace_module = importlib.import_module(namespace_name)
41-
42-
if reload:
43-
importlib.reload(namespace_module)
44-
45-
return pkgutil.iter_modules(
46-
namespace_module.__path__, namespace_module.__name__ + "." # type: ignore
47-
)
48-
except ModuleNotFoundError:
49-
return []
20+
DEFAULT_PLUGINS = ("allennlp_models",)
5021

5122

5223
def discover_file_plugins(plugins_filename: str = ".allennlp_plugins") -> Iterable[str]:
@@ -68,15 +39,19 @@ def discover_plugins() -> Iterable[str]:
6839
Returns an iterable of the plugins found.
6940
"""
7041
with push_python_path("."):
71-
for module_info in discover_namespace_plugins():
72-
yield module_info.name
7342
yield from discover_file_plugins()
7443

7544

7645
def import_plugins() -> None:
7746
"""
7847
Imports the plugins found with `discover_plugins()`.
7948
"""
49+
for module in DEFAULT_PLUGINS:
50+
try:
51+
# For default plugins we recursively import everything.
52+
import_module_and_submodules(module)
53+
except ModuleNotFoundError:
54+
pass
8055
for module_name in discover_plugins():
8156
try:
8257
importlib.import_module(module_name)

allennlp/tests/commands/main_test.py

+1-37
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
import io
21
import shutil
32
import sys
4-
from contextlib import redirect_stdout
53

64
import pytest
75
from overrides import overrides
@@ -12,7 +10,6 @@
1210
from allennlp.common.plugins import discover_plugins
1311
from allennlp.common.testing import AllenNlpTestCase
1412
from allennlp.common.util import push_python_path, pushd
15-
from allennlp.tests.common.plugins_util import pip_install
1613

1714

1815
class TestMain(AllenNlpTestCase):
@@ -133,46 +130,13 @@ def test_other_modules(self):
133130

134131
def test_file_plugin_loaded(self):
135132
plugins_root = self.FIXTURES_ROOT / "plugins"
136-
# "d" sets a "local" file plugin, because it's supposed to be run from that directory
137-
# and has a ".allennlp_plugins" file in it.
138-
project_d_fixtures_root = plugins_root / "project_d"
139133

140134
sys.argv = ["allennlp"]
141135

142136
available_plugins = set(discover_plugins())
143137
self.assertSetEqual(set(), available_plugins)
144138

145-
with pushd(project_d_fixtures_root):
139+
with pushd(plugins_root):
146140
main()
147141
subcommands_available = Subcommand.list_available()
148142
self.assertIn("d", subcommands_available)
149-
150-
def test_namespace_plugin_loaded(self):
151-
plugins_root = self.FIXTURES_ROOT / "plugins"
152-
# "a" sets a "global" namespace plugin, because it's gonna be installed with pip.
153-
project_a_fixtures_root = plugins_root / "project_a"
154-
155-
sys.argv = ["allennlp"]
156-
157-
available_plugins = set(discover_plugins())
158-
self.assertSetEqual(set(), available_plugins)
159-
160-
with pip_install(project_a_fixtures_root, "a"):
161-
main()
162-
163-
subcommands_available = Subcommand.list_available()
164-
self.assertIn("a", subcommands_available)
165-
166-
def test_subcommand_plugin_is_available(self):
167-
plugins_root = self.FIXTURES_ROOT / "plugins"
168-
allennlp_server_fixtures_root = plugins_root / "allennlp_server"
169-
170-
sys.argv = ["allennlp"]
171-
172-
with pip_install(
173-
allennlp_server_fixtures_root, "allennlp_server"
174-
), io.StringIO() as buf, redirect_stdout(buf):
175-
main()
176-
output = buf.getvalue()
177-
178-
self.assertIn(" serve", output)

allennlp/tests/common/plugins_test.py

+1-191
Original file line numberDiff line numberDiff line change
@@ -1,222 +1,32 @@
1-
import distutils.dir_util
2-
import tempfile
3-
4-
import pytest
51
from overrides import overrides
62

73
from allennlp.commands import Subcommand
84
from allennlp.common.plugins import (
9-
discover_file_plugins,
10-
discover_namespace_plugins,
115
discover_plugins,
126
import_plugins,
137
)
148
from allennlp.common.testing import AllenNlpTestCase
159
from allennlp.common.util import pushd
16-
from allennlp.tests.common.plugins_util import pip_install
1710

1811

1912
class TestPlugins(AllenNlpTestCase):
2013
@overrides
2114
def setUp(self):
2215
super().setUp()
2316
self.plugins_root = self.FIXTURES_ROOT / "plugins"
24-
# "a" sets a "global" namespace plugin, because it's gonna be installed with pip.
25-
self.project_a_fixtures_root = self.plugins_root / "project_a"
26-
# "b" sets a "local" namespace plugin, because it's supposed to be run from that directory.
27-
self.project_b_fixtures_root = self.plugins_root / "project_b"
28-
# "c" sets a "global" namespace plugin, because it's gonna be installed with pip.
29-
self.project_c_fixtures_root = self.plugins_root / "project_c"
30-
# "d" sets a "local" file plugin, because it's supposed to be run from that directory
31-
# and has a ".allennlp_plugins" file in it.
32-
self.project_d_fixtures_root = self.plugins_root / "project_d"
3317

3418
def test_no_plugins(self):
3519
available_plugins = set(discover_plugins())
3620
self.assertSetEqual(set(), available_plugins)
3721

38-
def test_namespace_package_does_not_exist(self):
39-
available_plugins = set(discover_namespace_plugins("dummy_namespace"))
40-
self.assertSetEqual(set(), available_plugins)
41-
42-
def test_file_plugins_does_not_exist(self):
43-
available_plugins = set(discover_file_plugins("dummy_file"))
44-
self.assertSetEqual(set(), available_plugins)
45-
46-
def test_global_namespace_plugin(self):
47-
available_plugins = set(discover_plugins())
48-
self.assertSetEqual(set(), available_plugins)
49-
50-
with pip_install(self.project_a_fixtures_root, "a"):
51-
available_plugins = set(discover_plugins())
52-
self.assertSetEqual({"allennlp_plugins.a"}, available_plugins)
53-
54-
import_plugins()
55-
subcommands_available = Subcommand.list_available()
56-
self.assertIn("a", subcommands_available)
57-
58-
def test_two_global_namespace_plugins(self):
59-
available_plugins = set(discover_plugins())
60-
self.assertSetEqual(set(), available_plugins)
61-
62-
with pip_install(self.project_a_fixtures_root, "a"), pip_install(
63-
self.project_c_fixtures_root, "c"
64-
):
65-
available_plugins = set(discover_plugins())
66-
self.assertSetEqual({"allennlp_plugins.a", "allennlp_plugins.c"}, available_plugins)
67-
68-
import_plugins()
69-
subcommands_available = Subcommand.list_available()
70-
self.assertIn("a", subcommands_available)
71-
self.assertIn("c", subcommands_available)
72-
73-
def test_local_namespace_plugin(self):
74-
available_plugins = set(discover_plugins())
75-
self.assertSetEqual(set(), available_plugins)
76-
77-
with pushd(self.project_b_fixtures_root):
78-
available_plugins = set(discover_plugins())
79-
self.assertSetEqual({"allennlp_plugins.b"}, available_plugins)
80-
81-
import_plugins()
82-
subcommands_available = Subcommand.list_available()
83-
self.assertIn("b", subcommands_available)
84-
8522
def test_file_plugin(self):
8623
available_plugins = set(discover_plugins())
8724
self.assertSetEqual(set(), available_plugins)
8825

89-
with pushd(self.project_d_fixtures_root):
26+
with pushd(self.plugins_root):
9027
available_plugins = set(discover_plugins())
9128
self.assertSetEqual({"d"}, available_plugins)
9229

9330
import_plugins()
9431
subcommands_available = Subcommand.list_available()
9532
self.assertIn("d", subcommands_available)
96-
97-
def test_local_namespace_plugin_different_path(self):
98-
available_plugins = set(discover_plugins())
99-
self.assertSetEqual(set(), available_plugins)
100-
101-
with tempfile.TemporaryDirectory() as temp_dir_b:
102-
distutils.dir_util.copy_tree(self.project_b_fixtures_root, temp_dir_b)
103-
104-
# We move to another directory with a different plugin "b", as if it were another
105-
# separate project which is not installed ("local" usage of the plugin declared in
106-
# the namespace).
107-
with pushd(temp_dir_b):
108-
available_plugins = set(discover_plugins())
109-
self.assertSetEqual({"allennlp_plugins.b"}, available_plugins)
110-
111-
import_plugins()
112-
subcommands_available = Subcommand.list_available()
113-
self.assertIn("b", subcommands_available)
114-
115-
def test_local_and_two_global_namespace_plugins(self):
116-
available_plugins = set(discover_plugins())
117-
self.assertSetEqual(set(), available_plugins)
118-
119-
# We make plugins "a" and "c" available as packages, each from other directories,
120-
# as if they were separate installed projects ("global" usage of the plugins).
121-
# We move to another directory with a different plugin "b", as if it were another separate
122-
# project which is not installed ("local" usage of the plugin declared in the namespace).
123-
with pip_install(self.project_a_fixtures_root, "a"), pip_install(
124-
self.project_c_fixtures_root, "c"
125-
), pushd(self.project_b_fixtures_root):
126-
available_plugins = set(discover_plugins())
127-
self.assertSetEqual(
128-
{"allennlp_plugins.a", "allennlp_plugins.b", "allennlp_plugins.c"},
129-
available_plugins,
130-
)
131-
132-
import_plugins()
133-
subcommands_available = Subcommand.list_available()
134-
self.assertIn("a", subcommands_available)
135-
self.assertIn("b", subcommands_available)
136-
self.assertIn("c", subcommands_available)
137-
138-
def test_file_and_two_global_namespace_plugins(self):
139-
available_plugins = set(discover_plugins())
140-
self.assertSetEqual(set(), available_plugins)
141-
142-
# We make plugins "a" and "c" available as packages, each from other directories,
143-
# as if they were separate installed projects ("global" usage of the plugins).
144-
# We move to another directory with a different plugin "b", as if it were another separate
145-
# project which is not installed ("local" usage of the plugin declared in a file).
146-
with pip_install(self.project_a_fixtures_root, "a"), pip_install(
147-
self.project_c_fixtures_root, "c"
148-
), pushd(self.project_d_fixtures_root):
149-
available_plugins = set(discover_plugins())
150-
self.assertSetEqual(
151-
{"allennlp_plugins.a", "allennlp_plugins.c", "d"}, available_plugins
152-
)
153-
154-
import_plugins()
155-
subcommands_available = Subcommand.list_available()
156-
self.assertIn("a", subcommands_available)
157-
self.assertIn("c", subcommands_available)
158-
self.assertIn("d", subcommands_available)
159-
160-
def test_reload_plugins_adds_new(self):
161-
available_plugins = set(discover_plugins())
162-
self.assertSetEqual(set(), available_plugins)
163-
164-
with pip_install(self.project_a_fixtures_root, "a"):
165-
available_plugins = set(discover_plugins())
166-
self.assertSetEqual({"allennlp_plugins.a"}, available_plugins)
167-
168-
import_plugins()
169-
subcommands_available = Subcommand.list_available()
170-
self.assertIn("a", subcommands_available)
171-
172-
with pip_install(self.project_c_fixtures_root, "c"):
173-
available_plugins = set(discover_plugins())
174-
self.assertSetEqual({"allennlp_plugins.a", "allennlp_plugins.c"}, available_plugins)
175-
176-
import_plugins()
177-
subcommands_available = Subcommand.list_available()
178-
self.assertIn("a", subcommands_available)
179-
self.assertIn("c", subcommands_available)
180-
181-
@pytest.mark.skip("Plugin unloading is not supported.")
182-
def test_unload_plugin(self):
183-
available_plugins = set(discover_plugins())
184-
self.assertSetEqual(set(), available_plugins)
185-
186-
with pip_install(self.project_a_fixtures_root, "a"):
187-
available_plugins = set(discover_plugins())
188-
self.assertSetEqual({"allennlp_plugins.a"}, available_plugins)
189-
190-
import_plugins()
191-
subcommands_available = Subcommand.list_available()
192-
self.assertIn("a", subcommands_available)
193-
194-
available_plugins = set(discover_plugins())
195-
self.assertSetEqual(set(), available_plugins)
196-
197-
import_plugins()
198-
subcommands_available = Subcommand.list_available()
199-
self.assertNotIn("a", subcommands_available)
200-
201-
@pytest.mark.skip("Plugin unloading is not supported.")
202-
def test_reload_plugins_removes_one_adds_one(self):
203-
available_plugins = set(discover_plugins())
204-
self.assertSetEqual(set(), available_plugins)
205-
206-
with pip_install(self.project_a_fixtures_root, "a"):
207-
available_plugins = set(discover_plugins())
208-
self.assertSetEqual({"allennlp_plugins.a"}, available_plugins)
209-
210-
import_plugins()
211-
subcommands_available = Subcommand.list_available()
212-
self.assertIn("a", subcommands_available)
213-
self.assertNotIn("c", subcommands_available)
214-
215-
with pip_install(self.project_c_fixtures_root, "c"):
216-
available_plugins = set(discover_plugins())
217-
self.assertSetEqual({"allennlp_plugins.c"}, available_plugins)
218-
219-
import_plugins()
220-
subcommands_available = Subcommand.list_available()
221-
self.assertNotIn("a", subcommands_available)
222-
self.assertIn("c", subcommands_available)

allennlp/tests/common/plugins_util.py

-26
This file was deleted.

allennlp/tests/fixtures/plugins/allennlp_server/allennlp_plugins/allennlp_server/__init__.py

-1
This file was deleted.

0 commit comments

Comments
 (0)