From 7a677f768094fecfbb04e7b803c99cc2787153a2 Mon Sep 17 00:00:00 2001 From: Josh Blum Date: Sat, 27 Jul 2013 01:17:46 -0700 Subject: gras: work on module loading and python factory naming convention, path convention, env var convention, And, make a python factory -- but its a little halfassed. You see, we didnt do the swig director thing yet, so C++ cant yet create python blocks from the factory. --- CMakeLists.txt | 3 +++ lib/CMakeLists.txt | 2 -- lib/module_loader.cpp | 33 ++++++++++++++++++++----------- python/gras/CMakeLists.txt | 10 +++++++++- python/gras/GRAS_Factory.i | 16 ++++++++++++++- python/gras/GRAS_Loader.py | 48 +++++++++++++++++++++++++++++++++++++++++++++ python/gras/__init__.py | 1 + tests/CMakeLists.txt | 5 +++++ tests/example_module.cpp | 2 +- tests/example_module.py | 16 +++++++++++++++ tests/module_loader_test.py | 8 ++++++-- 11 files changed, 126 insertions(+), 18 deletions(-) create mode 100644 python/gras/GRAS_Loader.py create mode 100644 tests/example_module.py diff --git a/CMakeLists.txt b/CMakeLists.txt index fbab4a3..c3c9437 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,6 +45,9 @@ if(MSVC) add_definitions(-DBOOST_ALL_DYN_LINK) #setup boost auto-linking in msvc endif(MSVC) +file(TO_NATIVE_PATH "${CMAKE_INSTALL_PREFIX}" GRAS_ROOT) +STRING(REPLACE "\\" "\\\\" GRAS_ROOT ${GRAS_ROOT}) + ######################################################################## # Component names for install rules ######################################################################## diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 749be10..ab94525 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -39,8 +39,6 @@ list(APPEND GRAS_SOURCES ${apology_sources}) ######################################################################## # Setup Module Loader ######################################################################## -file(TO_NATIVE_PATH "${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}/gras/modules" GRAS_MODULE_PATH) -STRING(REPLACE "\\" "\\\\" GRAS_MODULE_PATH ${GRAS_MODULE_PATH}) configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/module_loader.cpp ${CMAKE_CURRENT_BINARY_DIR}/module_loader.cpp diff --git a/lib/module_loader.cpp b/lib/module_loader.cpp index 00c34ab..d64722d 100644 --- a/lib/module_loader.cpp +++ b/lib/module_loader.cpp @@ -45,19 +45,30 @@ static void load_all_modules_in_path(const fs::path &path) ) load_all_modules_in_path(dir_itr->path()); } -GRAS_STATIC_BLOCK(gras_module_loader) +static void load_modules_from_paths(const std::string &paths, const fs::path &suffix) { - std::string search_paths = "@GRAS_MODULE_PATH@"; - const char *module_path_env = std::getenv("GRAS_MODULE_PATH"); - if (module_path_env != NULL) - { - search_paths += SEP; - search_paths += module_path_env; - } - if (search_paths.empty()) return; - BOOST_FOREACH(const std::string &path, boost::tokenizer > (search_paths, boost::char_separator(SEP))) + if (paths.empty()) return; + BOOST_FOREACH(const std::string &path, boost::tokenizer > (paths, boost::char_separator(SEP))) { if (path.empty()) continue; - load_all_modules_in_path(fs::path(path)); + load_all_modules_in_path(fs::path(path) / suffix); } } + +static std::string my_get_env(const std::string &name, const std::string &defalt) +{ + const char *env_var = std::getenv(name.c_str()); + return (env_var != NULL)? env_var : defalt; +} + +GRAS_STATIC_BLOCK(gras_module_loader) +{ + //!search the GRAS_ROOT directory for this install + load_modules_from_paths(my_get_env("GRAS_ROOT", "@GRAS_ROOT@"), fs::path("") / "lib@LIB_SUFFIX@" / "gras" / "modules"); + + //!search the GRAS_PATH search directories for modules + load_modules_from_paths(my_get_env("GRAS_PATH", ""), fs::path("") / "lib@LIB_SUFFIX@" / "gras" / "modules"); + + //!search the explicit module paths + load_modules_from_paths(my_get_env("GRAS_MODULE_PATH", ""), fs::path("")); +} diff --git a/python/gras/CMakeLists.txt b/python/gras/CMakeLists.txt index de6175e..dda4934 100644 --- a/python/gras/CMakeLists.txt +++ b/python/gras/CMakeLists.txt @@ -79,8 +79,16 @@ endforeach(gras_swig_module) ######################################################################## # install other python files ######################################################################## +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/GRAS_Loader.py + ${CMAKE_CURRENT_BINARY_DIR}/GRAS_Loader.py +@ONLY) + GR_PYTHON_INSTALL( - FILES __init__.py GRAS_Utils.py + FILES + __init__.py + GRAS_Utils.py + ${CMAKE_CURRENT_BINARY_DIR}/GRAS_Loader.py DESTINATION ${GR_PYTHON_DIR}/gras COMPONENT ${GRAS_COMP_PYTHON} ) diff --git a/python/gras/GRAS_Factory.i b/python/gras/GRAS_Factory.i index 01b51b9..1124963 100644 --- a/python/gras/GRAS_Factory.i +++ b/python/gras/GRAS_Factory.i @@ -27,12 +27,26 @@ namespace gras //////////////////////////////////////////////////////////////////////// // Create python make method for the factory //////////////////////////////////////////////////////////////////////// +%pythoncode %{ +#TODO we need to register this into the real factory +_py_factory = dict() +%} + %extend gras::Factory { %insert("python") %{ @staticmethod - def make(name, *args): + def register_make(name, fcn): + #TODO we need to register this into the real factory + _py_factory[name] = fcn + + @staticmethod + def make(name, *args, **kwargs): + + #first try the local to python py factory #TODO real factory + if name in _py_factory: return _py_factory[name](*args, **kwargs) + from PMC import PMC_M pmcargs = PMC_M(list(args)) return Factory._handle_make(name, pmcargs) diff --git a/python/gras/GRAS_Loader.py b/python/gras/GRAS_Loader.py new file mode 100644 index 0000000..b70bc60 --- /dev/null +++ b/python/gras/GRAS_Loader.py @@ -0,0 +1,48 @@ +# Copyright (C) by Josh Blum. See LICENSE.txt for licensing information. + +import os +import sys + +#try to import module +#http://effbot.org/zone/import-string.htm +def __try_module_import(filename): + directory, module_name = os.path.split(filename) + module_name = os.path.splitext(module_name)[0] + + path = list(sys.path) + sys.path.insert(0, directory) + try: + module = __import__(module_name) + except Exception as ex: + print 'Could not import', filename, ex + finally: + sys.path[:] = path # restore + +#recursive search for modules in path +def __module_import(p): + if not os.path.exists(p): return + if os.path.isfile(p): + return __try_module_import(p) + if not os.path.isdir(p): return + if os.path.exists(os.path.join(p, '__init__.py')): + return __try_module_import(p) + for sub in os.listdir(p): + name, ext = os.path.splitext(sub) + #prefer .pyo over .pyc, prefer .pyc over .py + has_pyc = os.path.exists(os.path.join(p, name+'.pyc')) + has_pyo = os.path.exists(os.path.join(p, name+'.pyo')) + if ext == ".py" and (has_pyc or has_pyo): continue + if ext == ".pyc" and has_pyo: continue + __module_import(os.path.join(p, sub)) + +#separate the paths and load each one +def __load_modules_from_paths(paths, suffix): + if not paths: return + for path in paths.split(os.pathsep): + if not path: continue + if suffix: path = os.path.join(path, suffix) + __module_import(path) + +__load_modules_from_paths(os.getenv("GRAS_ROOT", "@GRAS_ROOT@"), os.path.join("lib@LIB_SUFFIX@", "gras", "python")) +__load_modules_from_paths(os.getenv("GRAS_PATH", ""), os.path.join("lib@LIB_SUFFIX@", "gras", "python")) +__load_modules_from_paths(os.getenv("GRAS_PYTHON_PATH", ""), "") diff --git a/python/gras/__init__.py b/python/gras/__init__.py index 3c16388..6dcd4cd 100644 --- a/python/gras/__init__.py +++ b/python/gras/__init__.py @@ -18,3 +18,4 @@ from GRAS_PyBlock import PyBlock as Block from GRAS_PyHierBlocks import PyHierBlock as HierBlock from GRAS_PyHierBlocks import PyTopBlock as TopBlock from GRAS_ThreadPool import ThreadPoolConfig, ThreadPool +import GRAS_Loader diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 5c76dbe..3c82320 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -4,6 +4,10 @@ include(GrTest) +#define these blank so they dont interfere with tests +list(APPEND GR_TEST_ENVIRONS "GRAS_ROOT=") +list(APPEND GR_TEST_ENVIRONS "GRAS_PATH=") + ######################################################################## # unit test suite ######################################################################## @@ -64,4 +68,5 @@ file(TO_NATIVE_PATH "${example_module_location}" example_module_location) message(STATUS "example_module_location: ${example_module_location}") list(APPEND GR_TEST_ENVIRONS "GRAS_MODULE_PATH=${example_module_location}") +list(APPEND GR_TEST_ENVIRONS "GRAS_PYTHON_PATH=${CMAKE_CURRENT_SOURCE_DIR}/example_module.py") GR_ADD_TEST(module_loader_test ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/module_loader_test.py) diff --git a/tests/example_module.cpp b/tests/example_module.cpp index 2191650..d0316f8 100644 --- a/tests/example_module.cpp +++ b/tests/example_module.cpp @@ -31,4 +31,4 @@ gras::Block *make_my_block(void) return new MyBlock(); } -GRAS_REGISTER_FACTORY("/tests/my_block", make_my_block) +GRAS_REGISTER_FACTORY("/tests/my_block0", make_my_block) diff --git a/tests/example_module.py b/tests/example_module.py new file mode 100644 index 0000000..6c29435 --- /dev/null +++ b/tests/example_module.py @@ -0,0 +1,16 @@ +# Copyright (C) by Josh Blum. See LICENSE.txt for licensing information. + +import gras + +class MyBlock(gras.Block): + def __init__(self): + gras.Block.__init__(self, "MyBlock") + self.foo = 0 + self.register_call("get_num", self.get_num) + + def work(self, *args): pass + + def get_num(self): + return 42 + +gras.Factory.register_make("/tests/my_block1", MyBlock) diff --git a/tests/module_loader_test.py b/tests/module_loader_test.py index 8b7a0c9..4a8d64f 100644 --- a/tests/module_loader_test.py +++ b/tests/module_loader_test.py @@ -5,8 +5,12 @@ import gras class ModuleLoaderTest(unittest.TestCase): - def test_load_module(self): - my_block = gras.Factory.make("/tests/my_block") + def test_load_module_cpp(self): + my_block = gras.Factory.make("/tests/my_block0") + self.assertEqual(my_block.get_num(), 42) + + def test_load_module_py(self): + my_block = gras.Factory.make("/tests/my_block1") self.assertEqual(my_block.get_num(), 42) if __name__ == '__main__': -- cgit