Skip to content

Commit

Permalink
implemented plugin engine (fix rh-lab-q#11)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jan Silhan authored and auchytil committed Aug 25, 2014
1 parent 22301b0 commit a8e4b3f
Show file tree
Hide file tree
Showing 7 changed files with 117 additions and 21 deletions.
24 changes: 12 additions & 12 deletions rpg/__init__.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
from rpg.plugin_engine import PluginEngine, phases


class Base(object):

"""Base class that is controlled by RPM GUI"""

def __init__(self):
self._project_builder = ProjectBuilder()
self.spec = Spec()
self.sack = None # TODO dnf sack
self._package_builder = PackageBuilder()
self._plugin_engine = PluginEngine()
self._source_loader = SourceLoader(source_extraction_path)
self._plugin_engine = PluginEngine(self.spec, self.sack)
self._source_loader = SourceLoader(self.source_extraction_path)
self._copr_uploader = CoprUploader()
self.spec = Spec()
# TODO run "dnf makecache" in the background

@property
def source_extraction_path(self):
Expand Down Expand Up @@ -46,7 +50,7 @@ def process_archive_or_dir(self, path):

def run_raw_sources_analysis(self):
"""executed in background after dir/tarball/SRPM selection"""
self._plugin_engine.execute_phase(BEFORE_PATCHES_APLIED,
self._plugin_engine.execute_phase(phases[0],
self.source_extraction_path)

def apply_patches(self, ordered_patches):
Expand All @@ -55,7 +59,7 @@ def apply_patches(self, ordered_patches):

def run_pathed_sources_analysis(self):
"""executed in background after patches are applied"""
self._plugin_engine.execute_phase(AFTER_PATCHES_APLIED,
self._plugin_engine.execute_phase(phases[1],
self.source_extraction_path)

def build_project(self):
Expand All @@ -65,7 +69,7 @@ def build_project(self):

def run_installed_files_analysis(self):
"""executed in background after successful project build"""
self._plugin_engine.execute_phase(AFTER_PROJECT_BUILD,
self._plugin_engine.execute_phase(phases[2],
self.project_build_path)

def build_packages(self, *distros):
Expand All @@ -75,6 +79,7 @@ def build_packages(self, *distros):
self.distro)

class Predictor:

"""Predictor class is used for autocompletion of field,
every guess_* method takes prefix of result string
and returns list of strings matched ordered by their rank"""
Expand All @@ -100,8 +105,3 @@ def guess_dependency(self):

def guess_license(self):
pass


class Plugin:
"""class from which are plugins derived"""
pass
67 changes: 60 additions & 7 deletions rpg/plugin_engine.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
# phases:
BEFORE_PATCHES_APLIED, AFTER_PATCHES_APPLIED, AFTER_PROJECT_BUILD = range(3)
from rpg.plugin import Plugin
import inspect
import os.path

phases = ("before_patches_aplied", "after_patches_applied",
"after_project_build")


class PluginEngine:

"""PluginEngine class is responsible for executing properly plugins.
Plugin is class that implements methods which take file/dir and SPEC
as params.
Expand All @@ -12,13 +18,60 @@ class PluginEngine:
@file('relative/path/from/project/root/dir') that will proceed only
relevant files"""

def __init__(self, spec):
def __init__(self, spec, sack):
self.spec = spec
self.sack = sack
self.plugins = set()

def execute_phase(self, phase, project_root_dir):
def execute_phase(self, phase, project_dir):
"""trigger all plugin methods that are subscribed to the phase"""
pass

def load_plugins(dir):
if phase not in phases:
# TODO log warning
return
for plugin in self.plugins:
try:
method = getattr(plugin, phase)
except AttributeError:
continue
if callable(method):
# TODO log info execution of plugin
try:
method(project_dir, self.spec, self.sack)
except Exception:
pass # TODO log error

def load_plugins(self, path):
"""finds all plugins in dir and it's subdirectories"""
pass

pyfiles = path.rglob('*.py')
for pyfile in pyfiles:
splitted_path = _os_path_split(str(pyfile)[:-3])
path_to_file = '.'.join(splitted_path[:-1])
imported_path = __import__(
path_to_file, fromlist=[splitted_path[-1]])
imported_file = getattr(imported_path, splitted_path[-1])
for var_name in dir(imported_file):
attr = getattr(imported_file, var_name)
if inspect.isclass(attr) and attr != Plugin and \
issubclass(attr, Plugin):
# TODO info log initialization of plugin
try:
self.plugins.add(attr())
except Exception:
pass # TODO log error


def _os_path_split(path):
parts = []
while True:
newpath, tail = os.path.split(path)
if newpath == path:
assert not tail
if path:
parts.append(path)
break
parts.append(tail)
path = newpath
parts.reverse()
return parts
7 changes: 5 additions & 2 deletions rpg/plugins/misc/find_patch.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
from rpg import Plugin
from rpg.plugin import Plugin
import subprocess


class FindPatchPlugin(Plugin):

def before_patches_applied(self, project_dir, spec, sack):
patches = [(f, f.stat().st_mtime) for f in project_dir.iterdir()
if _is_patch(f)]
patches_by_modification = sorted(patches, key=lambda m: m[1])
spec.tags["patch"] = list(map(lambda p: str(p[0]), patches_by_modification))
spec.tags["patch"] = list(
map(lambda p: str(p[0]), patches_by_modification))


def _is_patch(path):
if path.is_dir():
Expand Down
7 changes: 7 additions & 0 deletions tests/project/py/plugin0.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from rpg.plugin import Plugin


class TestPlugin(Plugin):

def before_patches_applied(self, project_dir, spec, sack):
pass
5 changes: 5 additions & 0 deletions tests/project/py/sourcecode.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import rpg


class A:
pass
1 change: 1 addition & 0 deletions tests/support.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from pathlib import Path
from unittest import mock, TestCase


class PluginTestCase(TestCase):
test_project_dir = Path("tests/project")
sack = mock.MagicMock()
Expand Down
27 changes: 27 additions & 0 deletions tests/test_plugin_engine.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from support import PluginTestCase
from rpg import plugin_engine as engine
from tests.project.py.plugin0 import TestPlugin
from unittest import mock


class PluginEngineTest(PluginTestCase):

def setUp(self):
self.plugin_engine = engine.PluginEngine(self.spec, self.sack)

def test_load_plugins(self):
self.plugin_dir = self.test_project_dir / "py"
self.plugin_engine.load_plugins(self.plugin_dir)
self.assertEqual(len(self.plugin_engine.plugins), 1)
self.assertTrue(
isinstance(self.plugin_engine.plugins.pop(), TestPlugin))

def test_execute_phase(self):
self.plugin_engine.plugins = [mock.MagicMock()]
self.plugin_engine.execute_phase(
engine.phases[0], self.test_project_dir)
expected_call = [
mock.call(self.test_project_dir, self.spec, self.sack)]
plugin_call = getattr(
self.plugin_engine.plugins[0], engine.phases[0]).call_args_list
self.assertEqual(plugin_call, expected_call)

0 comments on commit a8e4b3f

Please sign in to comment.