Skip to content

Commit 59df551

Browse files
committed
Add module discovery via pkg_resources EntryPoints.
1 parent 984773a commit 59df551

File tree

1 file changed

+58
-4
lines changed

1 file changed

+58
-4
lines changed

bitbot/ModuleManager.py

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
import enum, gc, glob, importlib, importlib.util, io, inspect, os, sys
1+
import enum, gc, glob, importlib, importlib.util, itertools, io, inspect, os
2+
import sys
3+
from pkg_resources import iter_entry_points
24
import typing, uuid
35
from . import Config, EventManager, Exports, IRCBot, Logging, Timers, utils
46

@@ -117,19 +119,62 @@ def __init__(self,
117119
def _list_modules(self, directory: str
118120
) -> typing.Dict[str, ModuleDefinition]:
119121
modules = []
122+
120123
for file_module in glob.glob(os.path.join(directory, "*.py")):
121-
modules.append(self.define_module(ModuleType.FILE, file_module))
124+
# Excluse __init__.py, etc.
125+
if not file_module.rsplit(os.path.sep)[-1].startswith('_'):
126+
modules.append(self.define_module(ModuleType.FILE, file_module))
122127

123128
for directory_module in glob.glob(os.path.join(
124129
directory, "*", "__init__.py")):
125130
modules.append(self.define_module(ModuleType.DIRECTORY,
126131
directory_module))
127132
return {definition.name: definition for definition in modules}
128133

134+
def _list_installed_modules(self, entry_point_group
135+
) -> typing.Dict[str, ModuleDefinition]:
136+
"""Finds modules installed using a pkg_resources EntryPoint.
137+
138+
They are installed by `setuptools` by using:
139+
140+
```
141+
setup(
142+
# ...
143+
entry_points={
144+
'bitbot.extra_modules': [
145+
'module_name = your_package_name.your_module_name:Module',
146+
# ...
147+
}
148+
}
149+
)
150+
```
151+
152+
(replace only `module_name`, `your_package_name`, and
153+
`your_module_name` on the example above)
154+
"""
155+
modules = {}
156+
157+
for entry_point in iter_entry_points(entry_point_group):
158+
module_class = entry_point.load()
159+
path = sys.modules[module_class.__module__].__file__
160+
if path.rsplit(os.path.sep, 1)[-1] == '__init__.py':
161+
type = ModuleType.DIRECTORY
162+
else:
163+
type = ModuleType.FILE
164+
modules[entry_point.name] = self.define_module(type, path)
165+
166+
return modules
167+
129168
def list_modules(self, whitelist: typing.List[str],
130169
blacklist: typing.List[str]) -> typing.Dict[str, ModuleDefinition]:
131-
core_modules = self._list_modules(self._core_modules)
132-
extra_modules: typing.Dict[str, ModuleDefinition] = {}
170+
"""Discovers modules that are either installed or in one of the
171+
directories listed by `[self._core_modules] + self._extra_modules`."""
172+
173+
core_modules = self._list_installed_modules('bitbot.core_modules')
174+
extra_modules = self._list_installed_modules('bitbot.extra_modules')
175+
176+
for name, module in self._list_modules(self._core_modules).items():
177+
core_modules[name] = module
133178

134179
for directory in self._extra_modules:
135180
for name, module in self._list_modules(directory).items():
@@ -179,6 +224,15 @@ def _module_name(self, path: str) -> str:
179224
return os.path.basename(path).rsplit(".py", 1)[0].lower()
180225
def _module_paths(self, name: str) -> typing.List[str]:
181226
paths = []
227+
228+
entry_points = itertools.chain(
229+
iter_entry_points('bitbot.core_modules', name),
230+
iter_entry_points('bitbot.extra_modules', name))
231+
232+
for entry_point in entry_points:
233+
module_class = entry_point.load()
234+
paths.append(sys.modules[module_class.__module__].__path__) # type: ignore
235+
182236
for directory in [self._core_modules]+self._extra_modules:
183237
paths.append(os.path.join(directory, name))
184238
return paths

0 commit comments

Comments
 (0)