Merge branch '637_friendlier_hooks'

This commit is contained in:
Christopher Allan Webber 2013-04-10 17:53:05 -05:00
commit 761e26bb29
9 changed files with 315 additions and 10 deletions

View File

@ -18,7 +18,7 @@ import os
import sys import sys
from celery import Celery from celery import Celery
from mediagoblin.tools.pluginapi import PluginManager from mediagoblin.tools.pluginapi import callable_runall
MANDATORY_CELERY_IMPORTS = ['mediagoblin.processing.task'] MANDATORY_CELERY_IMPORTS = ['mediagoblin.processing.task']
@ -66,8 +66,7 @@ def setup_celery_app(app_config, global_config,
celery_app = Celery() celery_app = Celery()
celery_app.config_from_object(celery_settings) celery_app.config_from_object(celery_settings)
for callable_hook in PluginManager().get_hook_callables('celery_setup'): callable_runall('celery_setup', celery_app)
callable_hook(celery_app)
def setup_celery_from_config(app_config, global_config, def setup_celery_from_config(app_config, global_config,

View File

@ -22,7 +22,7 @@ from celery.signals import setup_logging
from mediagoblin import app, mg_globals from mediagoblin import app, mg_globals
from mediagoblin.init.celery import setup_celery_from_config from mediagoblin.init.celery import setup_celery_from_config
from mediagoblin.tools.pluginapi import PluginManager from mediagoblin.tools.pluginapi import callable_runall
OUR_MODULENAME = __name__ OUR_MODULENAME = __name__
@ -47,9 +47,7 @@ def setup_logging_from_paste_ini(loglevel, **kw):
logging.config.fileConfig(logging_conf_file) logging.config.fileConfig(logging_conf_file)
for callable_hook in \ callable_runall('celery_logging_setup')
PluginManager().get_hook_callables('celery_logging_setup'):
callable_hook()
setup_logging.connect(setup_logging_from_paste_ini) setup_logging.connect(setup_logging_from_paste_ini)

View File

@ -59,6 +59,4 @@ def setup_plugins():
pman.register_hooks(plugin.hooks) pman.register_hooks(plugin.hooks)
# Execute anything registered to the setup hook. # Execute anything registered to the setup hook.
setup_list = pman.get_hook_callables('setup') pluginapi.callable_runall('setup')
for fun in setup_list:
fun()

View File

@ -15,7 +15,10 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import sys import sys
from configobj import ConfigObj from configobj import ConfigObj
import pytest
from mediagoblin import mg_globals from mediagoblin import mg_globals
from mediagoblin.init.plugins import setup_plugins from mediagoblin.init.plugins import setup_plugins
from mediagoblin.tools import pluginapi from mediagoblin.tools import pluginapi
@ -172,3 +175,111 @@ def test_disabled_plugin():
# Make sure we didn't load the plugin # Make sure we didn't load the plugin
assert len(pman.plugins) == 0 assert len(pman.plugins) == 0
@with_cleanup()
def test_callable_runone():
"""
Test the callable_runone method
"""
cfg = build_config([
('mediagoblin', {}, []),
('plugins', {}, [
('mediagoblin.tests.testplugins.callables1', {}, []),
('mediagoblin.tests.testplugins.callables2', {}, []),
('mediagoblin.tests.testplugins.callables3', {}, []),
])
])
mg_globals.app_config = cfg['mediagoblin']
mg_globals.global_config = cfg
setup_plugins()
# Just one hook provided
call_log = []
assert pluginapi.callable_runone(
"just_one", call_log) == "Called just once"
assert call_log == ["expect this one call"]
# Nothing provided and unhandled not okay
call_log = []
with pytest.raises(pluginapi.UnhandledCallable):
pluginapi.callable_runone(
"nothing_handling", call_log)
assert call_log == []
# Nothing provided and unhandled okay
call_log = []
assert pluginapi.callable_runone(
"nothing_handling", call_log, unhandled_okay=True) is None
assert call_log == []
# Multiple provided, go with the first!
call_log = []
assert pluginapi.callable_runone(
"multi_handle", call_log) == "the first returns"
assert call_log == ["Hi, I'm the first"]
# Multiple provided, one has CantHandleIt
call_log = []
assert pluginapi.callable_runone(
"multi_handle_with_canthandle",
call_log) == "the second returns"
assert call_log == ["Hi, I'm the second"]
@with_cleanup()
def test_callable_runall():
"""
Test the callable_runall method
"""
cfg = build_config([
('mediagoblin', {}, []),
('plugins', {}, [
('mediagoblin.tests.testplugins.callables1', {}, []),
('mediagoblin.tests.testplugins.callables2', {}, []),
('mediagoblin.tests.testplugins.callables3', {}, []),
])
])
mg_globals.app_config = cfg['mediagoblin']
mg_globals.global_config = cfg
setup_plugins()
# Just one hook, check results
call_log = []
assert pluginapi.callable_runall(
"just_one", call_log) == ["Called just once", None, None]
assert call_log == ["expect this one call"]
# None provided, check results
call_log = []
assert pluginapi.callable_runall(
"nothing_handling", call_log) == []
assert call_log == []
# Multiple provided, check results
call_log = []
assert pluginapi.callable_runall(
"multi_handle", call_log) == [
"the first returns",
"the second returns",
"the third returns",
]
assert call_log == [
"Hi, I'm the first",
"Hi, I'm the second",
"Hi, I'm the third"]
# Multiple provided, one has CantHandleIt, check results
call_log = []
assert pluginapi.callable_runall(
"multi_handle_with_canthandle", call_log) == [
"the second returns",
"the third returns",
]
assert call_log == [
"Hi, I'm the second",
"Hi, I'm the third"]

View File

@ -0,0 +1,15 @@
# GNU MediaGoblin -- federated, autonomous media hosting
# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

View File

@ -0,0 +1,41 @@
# GNU MediaGoblin -- federated, autonomous media hosting
# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from mediagoblin.tools.pluginapi import CantHandleIt
def setup_plugin():
pass
def just_one(call_log):
call_log.append("expect this one call")
return "Called just once"
def multi_handle(call_log):
call_log.append("Hi, I'm the first")
return "the first returns"
def multi_handle_with_canthandle(call_log):
raise CantHandleIt("I just can't accept this stupid method")
hooks = {
'setup': setup_plugin,
'just_one': just_one,
'multi_handle': multi_handle,
'multi_handle_with_canthandle': multi_handle_with_canthandle,
}

View File

@ -0,0 +1,38 @@
# GNU MediaGoblin -- federated, autonomous media hosting
# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
def setup_plugin():
pass
def just_one(call_log):
assert "SHOULD NOT HAPPEN"
def multi_handle(call_log):
call_log.append("Hi, I'm the second")
return "the second returns"
def multi_handle_with_canthandle(call_log):
call_log.append("Hi, I'm the second")
return "the second returns"
hooks = {
'setup': setup_plugin,
'just_one': just_one,
'multi_handle': multi_handle,
'multi_handle_with_canthandle': multi_handle_with_canthandle,
}

View File

@ -0,0 +1,38 @@
# GNU MediaGoblin -- federated, autonomous media hosting
# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
def setup_plugin():
pass
def just_one(call_log):
assert "SHOULD NOT HAPPEN"
def multi_handle(call_log):
call_log.append("Hi, I'm the third")
return "the third returns"
def multi_handle_with_canthandle(call_log):
call_log.append("Hi, I'm the third")
return "the third returns"
hooks = {
'setup': setup_plugin,
'just_one': just_one,
'multi_handle': multi_handle,
'multi_handle_with_canthandle': multi_handle_with_canthandle,
}

View File

@ -272,3 +272,70 @@ def get_hook_templates(hook_name):
A list of strings representing template paths. A list of strings representing template paths.
""" """
return PluginManager().get_template_hooks(hook_name) return PluginManager().get_template_hooks(hook_name)
###########################
# Callable convenience code
###########################
class CantHandleIt(Exception):
"""
A callable may call this method if they look at the relevant
arguments passed and decide it's not possible for them to handle
things.
"""
pass
class UnhandledCallable(Exception):
"""
Raise this method if no callables were available to handle the
specified hook. Only used by callable_runone.
"""
pass
def callable_runone(hookname, *args, **kwargs):
"""
Run the callable hook HOOKNAME... run until the first response,
then return.
This function will run stop at the first hook that handles the
result. Hooks raising CantHandleIt will be skipped.
Unless unhandled_okay is True, this will error out if no hooks
have been registered to handle this function.
"""
callables = PluginManager().get_hook_callables(hookname)
unhandled_okay = kwargs.pop("unhandled_okay", False)
for callable in callables:
try:
return callable(*args, **kwargs)
except CantHandleIt:
continue
if unhandled_okay is False:
raise UnhandledCallable(
"No hooks registered capable of handling '%s'" % hookname)
def callable_runall(hookname, *args, **kwargs):
"""
Run all callables for HOOKNAME.
This method will run *all* hooks that handle this method (skipping
those that raise CantHandleIt), and will return a list of all
results.
"""
callables = PluginManager().get_hook_callables(hookname)
results = []
for callable in callables:
try:
results.append(callable(*args, **kwargs))
except CantHandleIt:
continue
return results