Skip to content

Commit

Permalink
enabling third party plugin support, fixes #19
Browse files Browse the repository at this point in the history
  • Loading branch information
mriehl committed Oct 14, 2013
1 parent fe486d7 commit ed11e01
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 31 deletions.
12 changes: 12 additions & 0 deletions src/main/python/pybuilder/pluginloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,18 @@ def load_plugin(self, project, name):
raise MissingPluginException(name, import_error)


class ThirdPartyPluginLoader(PluginLoader):
def load_plugin(self, project, name):
self.logger.debug("Trying to load third party plugin '%s'", name)
thirdparty_plugin = "%s" % name
try:
__import__(thirdparty_plugin)
self.logger.debug("Found third party plugin '%s'", thirdparty_plugin)
return sys.modules[thirdparty_plugin]
except ImportError as import_error:
raise MissingPluginException(name, import_error)


class DispatchingPluginLoader(PluginLoader):
def __init__(self, logger, *loader):
super(DispatchingPluginLoader, self).__init__(logger)
Expand Down
83 changes: 53 additions & 30 deletions src/main/python/pybuilder/reactor.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,17 @@
import imp
import os.path

from pybuilder.core import TASK_ATTRIBUTE, DEPENDS_ATTRIBUTE,\
DESCRIPTION_ATTRIBUTE, AFTER_ATTRIBUTE, BEFORE_ATTRIBUTE,\
INITIALIZER_ATTRIBUTE, ACTION_ATTRIBUTE, ONLY_ONCE_ATTRIBUTE, Project,\
NAME_ATTRIBUTE, ENVIRONMENTS_ATTRIBUTE
from pybuilder.core import (
TASK_ATTRIBUTE, DEPENDS_ATTRIBUTE, DESCRIPTION_ATTRIBUTE, AFTER_ATTRIBUTE, BEFORE_ATTRIBUTE,
INITIALIZER_ATTRIBUTE, ACTION_ATTRIBUTE, ONLY_ONCE_ATTRIBUTE, Project, NAME_ATTRIBUTE, ENVIRONMENTS_ATTRIBUTE)
from pybuilder.errors import PythonbuilderException, ProjectValidationFailedException
from pybuilder.pluginloader import BuiltinPluginLoader
from pybuilder.pluginloader import BuiltinPluginLoader, DispatchingPluginLoader, ThirdPartyPluginLoader
from pybuilder.utils import as_list
from pybuilder.execution import Action, Initializer, Task


class BuildSummary(object):

def __init__(self, project, task_execution_summaries):
self.project = project
self.task_summaries = task_execution_summaries
Expand All @@ -44,7 +44,10 @@ def __init__(self, logger, execution_manager, plugin_loader=None):
self.logger = logger
self.execution_manager = execution_manager
if not plugin_loader:
self.plugin_loader = BuiltinPluginLoader(self.logger)
builtin_plugin_loader = BuiltinPluginLoader(self.logger)
thirdparty_plugin_loader = ThirdPartyPluginLoader(self.logger)
self.plugin_loader = DispatchingPluginLoader(
self.logger, builtin_plugin_loader, thirdparty_plugin_loader)
else:
self.plugin_loader = plugin_loader
self._plugins = []
Expand Down Expand Up @@ -76,7 +79,8 @@ def prepare_build(self,
project_descriptor="build.py"):
Reactor._current_instance = self

project_directory, project_descriptor = self.verify_project_directory(project_directory, project_descriptor)
project_directory, project_descriptor = self.verify_project_directory(
project_directory, project_descriptor)

self.logger.debug("Loading project module from %s", project_descriptor)

Expand All @@ -97,9 +101,11 @@ def build(self, tasks=[], environments=[]):
Reactor._current_instance = self

if environments:
self.logger.info("Activated environments: %s", ", ".join(environments))
self.logger.info(
"Activated environments: %s", ", ".join(environments))

self.execution_manager.execute_initializers(environments, logger=self.logger, project=self.project)
self.execution_manager.execute_initializers(
environments, logger=self.logger, project=self.project)

self.log_project_properties()

Expand All @@ -114,9 +120,11 @@ def build(self, tasks=[], environments=[]):
raise PythonbuilderException("No default task given.")

execution_plan = self.execution_manager.build_execution_plan(tasks)
self.logger.debug("Execution plan is %s", ", ".join([task.name for task in execution_plan]))
self.logger.debug("Execution plan is %s", ", ".join(
[task.name for task in execution_plan]))

self.logger.info("Building %s version %s", self.project.name, self.project.version)
self.logger.info(
"Building %s version %s", self.project.name, self.project.version)
self.logger.info("Executing build in %s", self.project.basedir)

if len(tasks) == 1:
Expand All @@ -125,10 +133,11 @@ def build(self, tasks=[], environments=[]):
list_of_tasks = ", ".join(tasks)
self.logger.info("Going to execute tasks: %s", list_of_tasks)

task_execution_summaries = self.execution_manager.execute_execution_plan(execution_plan,
logger=self.logger,
project=self.project,
reactor=self)
task_execution_summaries = self.execution_manager.execute_execution_plan(
execution_plan,
logger=self.logger,
project=self.project,
reactor=self)

return BuildSummary(self.project, task_execution_summaries)

Expand All @@ -142,7 +151,8 @@ def execute_task(self, task_name):

def override_properties(self, property_overrides):
for property_override in property_overrides:
self.project.set_property(property_override, property_overrides[property_override])
self.project.set_property(
property_override, property_overrides[property_override])

def log_project_properties(self):
formatted = ""
Expand All @@ -163,31 +173,38 @@ def collect_tasks_and_actions_and_initializers(self, project_module):
name = getattr(candidate, NAME_ATTRIBUTE)
elif hasattr(candidate, "__name__"):
name = candidate.__name__
description = getattr(candidate, DESCRIPTION_ATTRIBUTE) if hasattr(candidate, DESCRIPTION_ATTRIBUTE) else ""
description = getattr(candidate, DESCRIPTION_ATTRIBUTE) if hasattr(
candidate, DESCRIPTION_ATTRIBUTE) else ""

if hasattr(candidate, TASK_ATTRIBUTE) and getattr(candidate, TASK_ATTRIBUTE):
dependencies = getattr(candidate, DEPENDS_ATTRIBUTE) if hasattr(candidate, DEPENDS_ATTRIBUTE) else None
dependencies = getattr(candidate, DEPENDS_ATTRIBUTE) if hasattr(
candidate, DEPENDS_ATTRIBUTE) else None

self.logger.debug("Found task %s", name)
self.execution_manager.register_task(Task(name, candidate, dependencies, description))
self.execution_manager.register_task(
Task(name, candidate, dependencies, description))

elif hasattr(candidate, ACTION_ATTRIBUTE) and getattr(candidate, ACTION_ATTRIBUTE):
before = getattr(candidate, BEFORE_ATTRIBUTE) if hasattr(candidate, BEFORE_ATTRIBUTE) else None
after = getattr(candidate, AFTER_ATTRIBUTE) if hasattr(candidate, AFTER_ATTRIBUTE) else None
before = getattr(candidate, BEFORE_ATTRIBUTE) if hasattr(
candidate, BEFORE_ATTRIBUTE) else None
after = getattr(candidate, AFTER_ATTRIBUTE) if hasattr(
candidate, AFTER_ATTRIBUTE) else None

only_once = False
if hasattr(candidate, ONLY_ONCE_ATTRIBUTE):
only_once = getattr(candidate, ONLY_ONCE_ATTRIBUTE)

self.logger.debug("Found action %s", name)
self.execution_manager.register_action(Action(name, candidate, before, after, description, only_once))
self.execution_manager.register_action(
Action(name, candidate, before, after, description, only_once))

elif hasattr(candidate, INITIALIZER_ATTRIBUTE) and getattr(candidate, INITIALIZER_ATTRIBUTE):
environments = []
if hasattr(candidate, ENVIRONMENTS_ATTRIBUTE):
environments = getattr(candidate, ENVIRONMENTS_ATTRIBUTE)

self.execution_manager.register_initializer(Initializer(name, candidate, environments, description))
self.execution_manager.register_initializer(
Initializer(name, candidate, environments, description))

def apply_project_attributes(self):
self.propagate_property("name")
Expand All @@ -209,24 +226,30 @@ def load_project_module(self, project_descriptor):
try:
return imp.load_source("build", project_descriptor)
except ImportError as e:
raise PythonbuilderException("Error importing project descriptor %s: %s" % (project_descriptor, e))
raise PythonbuilderException(
"Error importing project descriptor %s: %s" % (project_descriptor, e))

def verify_project_directory(self, project_directory, project_descriptor):
project_directory = os.path.abspath(project_directory)

if not os.path.exists(project_directory):
raise PythonbuilderException("Project directory does not exist: %s", project_directory)
raise PythonbuilderException(
"Project directory does not exist: %s", project_directory)

if not os.path.isdir(project_directory):
raise PythonbuilderException("Project directory is not a directory: %s", project_directory)
raise PythonbuilderException(
"Project directory is not a directory: %s", project_directory)

project_descriptor_full_path = os.path.join(project_directory, project_descriptor)
project_descriptor_full_path = os.path.join(
project_directory, project_descriptor)

if not os.path.exists(project_descriptor_full_path):
raise PythonbuilderException("Project directory does not contain descriptor file: %s",
project_descriptor_full_path)
raise PythonbuilderException(
"Project directory does not contain descriptor file: %s",
project_descriptor_full_path)

if not os.path.isfile(project_descriptor_full_path):
raise PythonbuilderException("Project descriptor is not a file: %s", project_descriptor_full_path)
raise PythonbuilderException(
"Project descriptor is not a file: %s", project_descriptor_full_path)

return project_directory, project_descriptor_full_path
39 changes: 38 additions & 1 deletion src/unittest/python/pluginloader_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,44 @@
from test_utils import mock

from pybuilder.errors import MissingPluginException
from pybuilder.pluginloader import BuiltinPluginLoader, DispatchingPluginLoader
from pybuilder.pluginloader import BuiltinPluginLoader, DispatchingPluginLoader, ThirdPartyPluginLoader


class ThirdPartyPluginLoaderTest (unittest.TestCase):

def setUp(self):
super(ThirdPartyPluginLoaderTest, self).setUp()
self.project = mock()
self.loader = ThirdPartyPluginLoader(mock())

def tearDown(self):
super(ThirdPartyPluginLoaderTest, self).tearDown()
unstub()

def test_should_raise_exception_when_requiring_plugin_and_plugin_is_not_found(self):
when(builtin_module).__import__(
"spam").thenRaise(ImportError())

self.assertRaises(
MissingPluginException, self.loader.load_plugin, self.project, "spam")

verify(builtin_module).__import__("spam")

def test_should_import_plugin_when_requiring_plugin_and_plugin_is_found_as_third_party(self):
old_module = sys.modules.get("spam")
try:
plugin_module = mock()
sys.modules["spam"] = plugin_module
when(builtin_module).__import__(
"spam").thenReturn(plugin_module)

self.loader.load_plugin(self.project, "spam")

verify(builtin_module).__import__("spam")
finally:
del sys.modules["spam"]
if old_module:
sys.modules["spam"] = old_module


class BuiltinPluginLoaderTest (unittest.TestCase):
Expand Down

0 comments on commit ed11e01

Please sign in to comment.