Skip to content
Snippets Groups Projects
python_export_maker.py 6.53 KiB
Newer Older
"""Creates a simple template for exporting a class to Python
"""
import optparse
import os
def get_exportfile(headerfile):
    """
    For the given header path create a path to the corresponding
    export file in the PythonInterface
    """
    # We need to find the submodule from the Mantid package
    submodule = get_submodule(headerfile)
    frameworkdir = get_frameworkdir(headerfile)
    exportpath = os.path.join(frameworkdir, 'PythonInterface', 'mantid', submodule, 'src', 'Exports')
    exportfile = os.path.join(exportpath, os.path.basename(headerfile).replace('.h','.cpp'))
    return exportfile

def get_unittest_file(headerfile):
    """
    For the given header path create a path to the corresponding
    Python unit test file
    """
    frameworkdir = get_frameworkdir(headerfile)
    submodule = get_submodule(headerfile)
    testpath = os.path.join(frameworkdir, 'PythonInterface', 'test',
                            'python','mantid',submodule)
    return os.path.join(testpath, os.path.basename(headerfile).replace('.h','Test.py'))
def get_submodule(headerfile):
    """
    Returns the corresponding Python submodule for the header
    """
    return get_namespace(headerfile).lower()

def get_namespace(headerfile):
    """
    Returns the Mantid namespace that the header resides in
    """
    matches = re.match(r".*inc(/|\\)Mantid(\w+)(/|\\).*\.h", headerfile)
        namespace = matches.group(2)
    else:
        raise RuntimeError("Unknown header path style. Cannot extract Mantid namespace name.")
    return namespace

def get_frameworkdir(headerfile):
    """
    Returns the Framework directory
    """
    if 'Framework' in headerfile:
        matches = re.match(r"(.*Framework(/|\\)).*\.h", headerfile)
        if matches:
            frameworkdir = matches.group(1)
        else:
            raise RuntimeError("Unknown header path style. Cannot extract Framework directory.")
    else:
        raise RuntimeError("Script must be run from outside the Framework directory")
    return frameworkdir
def get_classname(headerfile):
    """
    Returns the classname from a given file. At the moment it just uses the file name
    """
    filename = os.path.basename(headerfile)
    return os.path.splitext(filename)[0]

def get_include(headerfile):
    """
    Returns the relative include path for inclusion in the code
    """
    matches = re.match(r".*inc(/|\\)(Mantid[a-z,A-z]*(/|\\).*\.h)", headerfile)
        includefile = matches.group(2)
    else:
        raise RuntimeError("Unable to determine include path from given header")
    # Make sure the include only has forward slases
    includefile = includefile.replace("\\", "/")
    return includefile
def get_modulepath(frameworkdir, submodule):
    """Creates a path to the requested submodule
    """
    return os.path.join(frameworkdir, 'PythonInterface', 'mantid',submodule)

def write_export_file(headerfile, overwrite):
    """Write the export cpp file
    """
    # Where are we writing the output
    exportfile = get_exportfile(headerfile)
    print 'Writing export file \"%s\" '% os.path.basename(exportfile)
    if os.path.exists(exportfile) and not overwrite:
        raise RuntimeError("Export file '%s' already exists, use the --overwrite option to overwrite the file." % exportfile)
    namespace = get_namespace(headerfile)
    classname = get_classname(headerfile)
    include = get_include(headerfile)

    cppcode = """#include "%(header)s"
#include <boost/python/class.hpp>

using Mantid::%(namespace)s::%(class)s;

void export_%(class)s()
{
  class_<%(class)s,boost::noncopyable>("%(class)s", no_init)
    ;
}

"""
    cppfile = file(exportfile, 'w')
    cppfile.write(cppcode % {'header':include, 'namespace':namespace,'class':classname})
    cppfile.close()
    print 'Generated export file "%s"' % os.path.basename(exportfile)
    print
    print "  ** Add this to the EXPORT_FILES variable in '%s'" % \
       os.path.join(get_modulepath(get_frameworkdir(headerfile), get_submodule(headerfile)), 'CMakeLists.txt')
def write_unittest(headerfile, overwrite):
    """
       Write a unit test for the given header
    """
    filename = get_unittest_file(headerfile)
    print 'Writing unit test \"%s\" '% os.path.basename(filename)
    if os.path.exists(filename) and not overwrite:
        raise RuntimeError("A unit test file '%s' already exists, use the --overwrite-test option to overwrite the file." % filename)
    classname = get_classname(headerfile)
    pytest = \
"""import unittest
from mantid import %(classname)s

class %(classname)sTest(unittest.TestCase):

    def test_something(self):
        self.fail("Test something")

if __name__ == '__main__':
    unittest.main()
"""
    unittest = open(filename, 'w')
    unittest.write(pytest % {'classname':classname})
    unittest.close()

    print 'Generated unit test file "%s"' % os.path.basename(filename)
    print
    print "  ** Add this to the TEST_PY_FILES variable in '%s'" % \
        os.path.join('PythonInterface', 'CMakeLists.txt')
def main():
    """Main function
    """
    parser = optparse.OptionParser(usage="usage: %prog [options] headerfile ", description="Creates a simple template for exporting a class to Python along with a unit test")
    parser.add_option("--overwrite", "-o", dest='overwrite', action='store_true', default=False, help='If the file already exists, overwrite it')
    parser.add_option("--export-only", "-e", dest='export_only', action='store_true', default=False, help='If True only attempt to generate the export file')
    parser.add_option("--test-only", "-t", dest='test_only', action='store_true', default=False, help='If True only attempt to generate the unit test')
    parser.add_option("--overwrite-test", "-w", dest='overwrite_test', action='store_true', default=False, help='If true overwrite a test file if it exists')

    (options, args) = parser.parse_args()
        parser.error("Incorrect number of arguments")
    headerfile = args[0]
    if not os.path.exists(headerfile):
        parser.error("Invalid header path")
    # Sanity
    if options.export_only and options.test_only:
        parser.error("The export_only and test_only options are mutually exclusive, please specify only one.")

    if not options.test_only:
        write_export_file(os.path.abspath(headerfile), options.overwrite)

    # Unit test
    if not options.export_only:
        write_unittest(os.path.abspath(headerfile), options.overwrite_test)

if __name__ == '__main__':
    main()