From 28a12c7b2fb152b19913b3f22f92d3f77ff672b9 Mon Sep 17 00:00:00 2001 From: Olivier Parcollet Date: Sun, 1 Jun 2014 15:59:11 +0200 Subject: [PATCH] ipython triqs magic v1 - first version of triqs magic for the notebook - add add_preamble to the wrap_generator. --- pytriqs/CMakeLists.txt | 3 + pytriqs/magic.py.in | 217 +++++++++++++++++++++++ pytriqs/wrap_generator/wrap_generator.py | 7 + pytriqs/wrap_generator/wrapper.mako.cpp | 2 + 4 files changed, 229 insertions(+) create mode 100644 pytriqs/magic.py.in diff --git a/pytriqs/CMakeLists.txt b/pytriqs/CMakeLists.txt index 9084ee25..c653a78d 100644 --- a/pytriqs/CMakeLists.txt +++ b/pytriqs/CMakeLists.txt @@ -17,11 +17,14 @@ find_package(PythonWrapperMacro) SET(PYTHON_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/__init__.py ${CMAKE_CURRENT_BINARY_DIR}/version.py + ${CMAKE_CURRENT_BINARY_DIR}/magic.py ) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/version.py.in ${CMAKE_CURRENT_BINARY_DIR}/version.py) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/utility/mpi.py.in ${CMAKE_CURRENT_BINARY_DIR}/utility/mpi.py) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/magic.py.in ${CMAKE_CURRENT_BINARY_DIR}/magic.py @ONLY) +install (FILES ${PYTHON_SOURCES} DESTINATION ${TRIQS_PYTHON_LIB_DEST}) install (FILES ${PYTHON_SOURCES} DESTINATION ${TRIQS_PYTHON_LIB_DEST}) # All subdirs diff --git a/pytriqs/magic.py.in b/pytriqs/magic.py.in new file mode 100644 index 00000000..c3e18e40 --- /dev/null +++ b/pytriqs/magic.py.in @@ -0,0 +1,217 @@ +""" +===================== +Triqs magic +===================== + +{TRIQS_DOC} + +""" +import imp,os,sys,subprocess, hashlib +from IPython.core.error import UsageError +from IPython.core.magic import Magics, magics_class, line_magic, cell_magic +from IPython.core import display, magic_arguments +from IPython.utils import py3compat +from IPython.utils.io import capture_output +from IPython.utils.path import get_ipython_cache_dir + +__version__ = '0.1.0' + +triqs_path = "@CMAKE_INSTALL_PREFIX@" +converter_include_path = triqs_path + "/include/pytriqs/converters/" +generator_path = triqs_path + "/share/triqs/wrap_generator" + +cmakelist = """ +list(APPEND CMAKE_MODULE_PATH %s/share/triqs/cmake) +cmake_minimum_required(VERSION 2.8) +project(triqs_magic CXX) +set(CMAKE_BUILD_TYPE Release) +option(BUILD_SHARED_LIBS "Build shared libraries" ON) +find_package(TRIQS REQUIRED) +include_directories(${CMAKE_SOURCE_DIR} ${TRIQS_INCLUDE_ALL}) +add_library(ext MODULE ext_wrap.cpp) +#add_library(ext MODULE ext.cpp ext_wrap.cpp) +set_target_properties(ext PROPERTIES PREFIX "") #eliminate the lib in front of the module name +target_link_libraries(ext ${TRIQS_LIBRARY_ALL}) +"""%triqs_path + +mod_dict = { + 'gf' : +""" +module.use_module('gf') +module.add_using('namespace triqs::gfs') +""", + 'parameters' : +""" +module.use_module('parameters') +module.add_using('namespace triqs::params') +""",} + +def desc_file(c_decl, use) : + s = """ +from wrap_generator import * +module = module_(full_name = "ext", doc = "") +module.add_include("") +module.add_using("namespace triqs::arrays") +module.add_preamble('#include "./ext.cpp"') +""" + + mod_list = sum((x.split(',') for x in use), []) + for m in mod_list : + s+= mod_dict[m] + + for f in c_decl : + s+= 'module.add_function("%s")\n'%f + + return s + "module.generate_code()\n" + +@magics_class +class TriqsMagics(Magics): + + def __init__(self, shell): + super(TriqsMagics, self).__init__(shell=shell) + self._reloads = {} + self._code_cache = {} + self._lib_dir = os.path.join(get_ipython_cache_dir(), 'triqs') + if not os.path.exists(self._lib_dir): + os.makedirs(self._lib_dir) + + def _import_all(self, module, verbosity=0): + imported = [] + for k, v in module.__dict__.items(): + if not k.startswith('__'): + self.shell.push({k: v}) + imported.append(k) + if verbosity > 0 and imported: + print("\nOk. The following objects are ready to use: %s" % ", ".join(imported)) + + def extract_signature(self,code): + acc = [] + for l in code.splitlines() : + ll = l.strip() + if ll.startswith("#") or ll.startswith("using") : continue + acc.append(l) + if l.count('{') : break + s = ''.join(acc).split('{',1)[0] # everything before { + #print "signature", s + return [s] + + @magic_arguments.magic_arguments() + @magic_arguments.argument( + "-v", "--verbosity", action="count", default=0, + help="increase output verbosity" + ) + @magic_arguments.argument( + '-u', "--use", action='append', default=[], + help="""Modules used""" + ) + @cell_magic + def triqs(self, line, cell=None): + """Compile and import everything from a Triqs code cell. + + The content of the cell is written to a `.cpp` file in the + directory `IPYTHONDIR/triqs` using a filename with the hash of the + code. A python wrapper is then generated from the function signature. + Those files are then compiled. + The resulting module is imported and all of its symbols are injected into the user's namespace. + + Usage + ===== + Prepend ``%%triqs`` to your triqs code in a cell:: + + ``%%triqs + + ! put your code here. + `` + """ + try: + # custom saved arguments + saved_defaults = vars( + magic_arguments.parse_argstring(self.triqs, + self.shell.db['triqs'])) + self.triqs.parser.set_defaults(**saved_defaults) + except KeyError: + saved_defaults = {'verbosity': 0} + + if '-v' in line: + self.triqs.parser.set_defaults(verbosity=0) + + args = magic_arguments.parse_argstring(self.triqs, line) + + code = cell if cell.endswith('\n') else cell + '\n' + key = code, line, sys.version_info, sys.executable + + c_decl = self.extract_signature(code) + if args.verbosity>1 : + print "Found function of signature" + for f in c_decl : + print f + + module_name = "ext" + module_dirname = os.path.join(self._lib_dir, "_triqs_magic_" + hashlib.md5(str(key).encode('utf-8')).hexdigest()) + module_path = os.path.join(module_dirname, 'ext.so') + try : + os.mkdir(module_dirname) + except : + pass + + old_cwd = os.getcwd() + try: + os.chdir(module_dirname) + + with open('ext_desc.py', 'w') as f: + f.write(desc_file(c_decl, args.use)) + + with open('CMakeLists.txt', 'w') as f: + f.write(cmakelist) + + with open('ext.cpp', 'w') as f: + f.write(code) + + # Call the wrapper generator + command_generator = """ + PYTHONPATH={generator_path} python ext_desc.py {generator_path}/wrapper.mako.cpp ext_wrap.cpp {generator_path}/py_converter_wrapper.mako.hpp conv.hpp {converter_include_path} + """.format(**globals()) + + try : + out_generator = subprocess.check_output(command_generator, stderr=subprocess.STDOUT, shell=True) + except subprocess.CalledProcessError as E : + print '---------- Wrapper generator error -------\n' + E.output + return + + if args.verbosity>0 : print "---------- Wrapper generator ------", out_generator + + # Call cmake + command_cmake = "cmake . -DTRIQS_PATH=" + triqs_path + + try : + out_cmake = subprocess.check_output(command_cmake, stderr=subprocess.STDOUT, shell=True) + except subprocess.CalledProcessError as E : + print '---------- Cmake error -------\n' + E.output + return + + if args.verbosity>0 : print "---------- Cmake ------", out_cmake + + # Call make + command_make = "make -j2" + + try : + out_make = subprocess.check_output(command_make, stderr=subprocess.STDOUT, shell=True) + except subprocess.CalledProcessError as E : + print '---------- Make error -------\n' + E.output + return + + if args.verbosity>0 : print "---------- Make ------", out_make + + finally: + os.chdir(old_cwd) + + self._code_cache[key] = module_path + module = imp.load_dynamic(module_name, module_path) + self._import_all(module, verbosity=args.verbosity) + +__doc__ = __doc__.format(TRIQS_DOC=' ' * 8 + TriqsMagics.triqs.__doc__) + +def load_ipython_extension(ip): + """Load the extension in IPython.""" + ip.register_magics(TriqsMagics) + diff --git a/pytriqs/wrap_generator/wrap_generator.py b/pytriqs/wrap_generator/wrap_generator.py index f62c3efa..ae2c1fe1 100644 --- a/pytriqs/wrap_generator/wrap_generator.py +++ b/pytriqs/wrap_generator/wrap_generator.py @@ -591,6 +591,7 @@ class module_ : self.python_functions = {} self.hidden_python_functions = {} self.module_path_list = [] + self._preamble = '' def add_class(self, cls): """ @@ -666,6 +667,12 @@ class module_ : """ self.using.append(ns) + def add_preamble(self, preamble) : + """ + Add the using statement into the generated wrapper (and NOT the header). + """ + self._preamble += preamble + '\n' + def use_module(self, modulename) : """ From the name of the module : diff --git a/pytriqs/wrap_generator/wrapper.mako.cpp b/pytriqs/wrap_generator/wrapper.mako.cpp index 06d07a58..ddf71ba6 100644 --- a/pytriqs/wrap_generator/wrapper.mako.cpp +++ b/pytriqs/wrap_generator/wrapper.mako.cpp @@ -19,6 +19,8 @@ using ${ns}; #include using namespace triqs::py_tools; +${module._preamble} + //--------------------- a dict of python function used in the module but not exposed to user (cf init function) ---------------- %if len(module.python_functions) + len(module.hidden_python_functions) > 0 :