This is an automated email from the git hooks/post-receive script.
firstyear pushed a commit to branch master
in repository 389-ds-base.
commit bf919d1d0eefabb60155063dd22aa08b8a7d9938
Author: Jan Rusnacko <jrusnack(a)redhat.com>
Date: Thu Oct 10 18:19:15 2013 +0200
Initial commit with DSModuleProxy.
In order to keep implementation of class DSInstance organized, we
implement all functionality as functions in separate modules. All
functions accept DSInstance object as their first argument by rule.
Even though it`s beneficial to define everythin as function in
separate module, in runtime it`s nicer to have methods on DSInstance.
ModuleProxy serves two purposes:
- takes modules and all functions within and adds them to DSInstance
as methods
- creates separate namespaces for all modules within DSInstance
---
src/lib389/lib389/__init__.py | 0
src/lib389/lib389/ds_instance.py | 82 ++++++++++++++++++++++++++
src/lib389/tests/__init__.py | 0
src/lib389/tests/conftest.py | 106 ++++++++++++++++++++++++++++++++++
src/lib389/tests/test_module_proxy.py | 45 +++++++++++++++
5 files changed, 233 insertions(+)
diff --git a/src/lib389/lib389/__init__.py b/src/lib389/lib389/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/src/lib389/lib389/ds_instance.py b/src/lib389/lib389/ds_instance.py
new file mode 100644
index 0000000..7545eee
--- /dev/null
+++ b/src/lib389/lib389/ds_instance.py
@@ -0,0 +1,82 @@
+import os, re, sys
+
+class DSDecorator(object):
+ """ Implements decorator that can be instantiated during runtime.
+
+ Accepts function and instance of DSInstance. When called,
+ it will call passed function with instance of DSInstance
+ passed as the first argument.
+ """
+ def __init__(self, fun, ds_instance):
+ self.fun = fun
+ self.ds_instance = ds_instance
+
+ def __call__(self, *args, **kwargs):
+ if args is None and kwargs is None:
+ return self.fun(self.ds_instance)
+ elif args is not None and kwargs is None:
+ return self.fun(self.ds_instance, *args)
+ elif args is None and kwargs is not None:
+ return self.fun(self.ds_instance, **kwargs)
+ else:
+ return self.fun(self.ds_instance, *args, **kwargs)
+
+class DSModuleProxy(object):
+ """ Proxy with DS-decorated functions from modules in directory.
+
+ DSModuleProxy object acts as a proxy to functions defined in modules
+ stored in directory. Proxy itself can have other proxies as
+ attributes, so returned object follows the structure of passed
+ directory. All funcions from all modules are decorated with
+ DSDecorator - DSInstance object will be passed to them as first
+ argument.
+
+ Kudos to Milan Falesnik <mfalesni(a)redhat.com>
+ """
+
+ def __init__(self, module_name):
+ self.name = module_name
+
+ @classmethod
+ def populate_with_proxies(cls, ds, obj, directory):
+ """
+ Returns a proxy with decorated functions from modules in directory.
+ """
+ if not os.path.isdir(directory):
+ raise RuntimeError("Last argument %s was not directory" %
directory)
+ for item in os.listdir(directory):
+
+ # case when item is directory
+ if os.path.isdir(directory + "/" + item):
+ # Recursively call self on all subdirectories
+ proxy = cls(item)
+ cls.populate_with_proxies(ds, proxy, directory + "/" + item)
+ setattr(obj, item, proxy)
+
+ # case when item is python module
+ elif (os.path.isfile(directory + "/" + item) and
+ re.match(r'(.*\/)?[^_/].*[^_/]\.py$', item)):
+
+ # get the module object corresponding to processed file
+ item = item.replace('.py', '')
+ to_import = (directory + "/" +
item).replace('/','.')
+ __import__(to_import)
+ module = sys.modules[to_import]
+ proxy = cls(item)
+
+ # for each function from module create decorated one and keep it
+ for attr in dir(module):
+ fun = getattr(module, attr)
+ if callable(fun):
+ decorated = DSDecorator(fun, ds)
+ setattr(proxy, attr, decorated)
+ setattr(obj, item, proxy)
+
+ else:
+ # skip anything that is not directory or python module
+ pass
+
+class DSInstance(object):
+
+ def __init__(self):
+ DSModuleProxy.populate_with_proxies(self, self, 'dsmodules')
diff --git a/src/lib389/tests/__init__.py b/src/lib389/tests/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/src/lib389/tests/conftest.py b/src/lib389/tests/conftest.py
new file mode 100644
index 0000000..763774c
--- /dev/null
+++ b/src/lib389/tests/conftest.py
@@ -0,0 +1,106 @@
+
+import string
+import os
+import sys
+import pytest
+from tempfile import mkdtemp
+from shutil import rmtree
+
+def ldrop(numchar, inputstr):
+ """
+ Drop numchar characters from the beginning of each line in inputstring.
+ """
+ return "\n".join([x[numchar:] for x in string.split(inputstr,
"\n")])
+
+(a)pytest.fixture
+def fake_ds_class():
+ """
+ Returns fake DSInstance-like class
+ """
+ class FakeDSInstance(object):
+ def return_one(self):
+ return 1
+
+ return FakeDSInstance
+
+(a)pytest.fixture(scope="module")
+def fake_ds_modules(request):
+ """
+ Returns path to directory containing fake modules.
+ """
+
+ # module_dir contains fake ds modules, that can be used for testing
+ module_dir = mkdtemp()
+ old_cwd = os.getcwd()
+
+ # when fixture is destroyed, we change back to old cwd
+ # and delete fake module dir
+ def fin():
+ os.chdir(old_cwd)
+ rmtree(module_dir)
+ request.addfinalizer(fin)
+
+ # make module_dir a python package
+ with open(module_dir + "/__init__.py", 'a'):
+ os.utime(module_dir + "/__init__.py", None)
+
+ # create module module_dir/repl.py with few functions
+ with open(module_dir + "/repl.py", "w") as repl_file:
+ repl_module = """
+ def with_no_arg(ds):
+ return ds
+
+ def with_one_arg(ds, arg):
+ return ds, arg
+
+ def return_argument_given(ds, arg):
+ return arg
+
+ def with_kwarg(ds, kwarg=2):
+ return kwarg
+
+ def with_all(ds, arg1, arg2, kwarg1=1, kwarg2=2):
+ return arg1, arg2, kwarg1, kwarg2
+
+ """
+ # Drop 12 chars from left and write to file
+ repl_file.write(ldrop(12, repl_module))
+
+ os.mkdir(module_dir + "/plugin")
+
+ # make module_dir/plugin a python package
+ with open(module_dir + "/plugin/__init__.py", 'a'):
+ os.utime(module_dir + "/plugin/__init__.py", None)
+
+ # create module module_dir/plugin/automember.py with few functions
+ with open(module_dir + "/plugin/automember.py", "w") as
automem_file:
+ automember_module = """
+ def with_no_arg(ds):
+ return ds
+
+ def with_one_arg(ds, arg):
+ return ds, arg
+
+ def calling_ds_method(ds, arg):
+ return (ds, ds.return_one(), arg)
+
+ def calling_repl(ds, arg1, arg2):
+ return ds.repl.return_argument_given(1) + arg1 + arg2
+
+ def calling_repl_with_kwarg2(ds):
+ return ds.repl.with_kwarg()
+
+ def calling_repl_with_kwarg1(ds):
+ return ds.repl.with_kwarg(kwarg=1)
+
+ def with_all(ds, arg1, arg2, kwarg1=1, kwarg2=2):
+ a, b, c, d = ds.repl.with_all(1, 4, kwarg1=3)
+ return a + arg1, b + arg2, c + kwarg1, d + kwarg2
+
+ """
+ # Drop 12 chars from left and write to file
+ automem_file.write(ldrop(12, automember_module))
+
+ os.chdir(os.path.dirname(module_dir))
+ sys.path.append(os.path.dirname(module_dir))
+ return module_dir
diff --git a/src/lib389/tests/test_module_proxy.py
b/src/lib389/tests/test_module_proxy.py
new file mode 100644
index 0000000..e21271e
--- /dev/null
+++ b/src/lib389/tests/test_module_proxy.py
@@ -0,0 +1,45 @@
+
+import os
+from lib389.ds_instance import DSModuleProxy
+
+def test_module_proxy_fun_with_no_arg(fake_ds_class, fake_ds_modules):
+ ds_inst = fake_ds_class()
+ DSModuleProxy.populate_with_proxies(ds_inst, ds_inst,
os.path.basename(fake_ds_modules))
+ assert ds_inst.repl.with_no_arg() == ds_inst
+
+def test_module_proxy_fun_with_one_arg(fake_ds_class, fake_ds_modules):
+ ds_inst = fake_ds_class()
+ DSModuleProxy.populate_with_proxies(ds_inst, ds_inst,
os.path.basename(fake_ds_modules))
+ assert ds_inst.repl.with_one_arg(1) == (ds_inst, 1)
+
+def test_module_proxy_plugin_fun_with_no_arg(fake_ds_class, fake_ds_modules):
+ ds_inst = fake_ds_class()
+ DSModuleProxy.populate_with_proxies(ds_inst, ds_inst,
os.path.basename(fake_ds_modules))
+ assert ds_inst.plugin.automember.with_no_arg() == ds_inst
+
+def test_module_proxy_plugin_fun_with_one_arg(fake_ds_class, fake_ds_modules):
+ ds_inst = fake_ds_class()
+ DSModuleProxy.populate_with_proxies(ds_inst, ds_inst,
os.path.basename(fake_ds_modules))
+ assert ds_inst.plugin.automember.with_one_arg(2) == (ds_inst, 2)
+
+def test_module_proxy_plugin_call_ds_method(fake_ds_class, fake_ds_modules):
+ ds_inst = fake_ds_class()
+ DSModuleProxy.populate_with_proxies(ds_inst, ds_inst,
os.path.basename(fake_ds_modules))
+ assert ds_inst.plugin.automember.calling_ds_method(2) == (ds_inst, 1, 2)
+
+def test_module_proxy_plugin_call_another_module(fake_ds_class, fake_ds_modules):
+ ds_inst = fake_ds_class()
+ DSModuleProxy.populate_with_proxies(ds_inst, ds_inst,
os.path.basename(fake_ds_modules))
+ assert ds_inst.plugin.automember.calling_repl(2, 3) == 6
+
+def test_module_proxy_plugin_call_another_module_kwarg(fake_ds_class, fake_ds_modules):
+ ds_inst = fake_ds_class()
+ DSModuleProxy.populate_with_proxies(ds_inst, ds_inst,
os.path.basename(fake_ds_modules))
+ assert ds_inst.plugin.automember.calling_repl_with_kwarg1() == 1
+ assert ds_inst.plugin.automember.calling_repl_with_kwarg2() == 2
+
+def test_module_proxy_plugin_call_with_all(fake_ds_class, fake_ds_modules):
+ ds_inst = fake_ds_class()
+ DSModuleProxy.populate_with_proxies(ds_inst, ds_inst,
os.path.basename(fake_ds_modules))
+ assert ds_inst.plugin.automember.with_all(0, 3, 2, 1) == (1, 7, 5, 3)
+
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.