commit b8252d95e6dde675849d6e9e9e7e35913081a27a Author: Lokendra Singh Date: Wed Jun 26 16:18:03 2019 +0530 Swig wrapper lrx-proc -m (#22) * Fixed: LRXProcessor::processME print to FILE *output, instead of stdout * SWIG Python wrapper guarded by --enable-python-bindings diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a70a55d --- /dev/null +++ b/.gitignore @@ -0,0 +1,72 @@ +*.pyc +**/*.deps/ +/.ccls-cache/ +/*.pc +/aclocal.m4 +/autom4te.cache +/compile +/config.guess +/config.log +/config.status +/config.sub +/configure +/depcomp +/INSTALL +/install-sh +/m4/libtool.m4 +/m4/lt~obsolete.m4 +/m4/ltoptions.m4 +/m4/ltsugar.m4 +/m4/ltversion.m4 +/Makefile +/Makefile.in +/missing + +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +lrx-comp +lrx-proc +lrx_config.h +lrx_config.h.in* +multitrans +stamp-h1 + +/python/Makefile.in +/python/Makefile +/python/lex_tools_wrap.cpp +/python/lextools.py +/python/setup.py +/python/build* +*.egg-info/ +*.egg diff --git a/Makefile.am b/Makefile.am index 6b97e19..1db3a16 100644 --- a/Makefile.am +++ b/Makefile.am @@ -21,4 +21,8 @@ if HAVE_IRSTLM bin_PROGRAMS += irstlm_ranker endif +if HAVE_PYTHON_BINDINGS +SUBDIRS = python +endif + EXTRA_DIST = lrx_compiler.h lrx_processor.h multi_translator.h tagger_output_processor.h diff --git a/configure.ac b/configure.ac index 71074fa..518f73c 100644 --- a/configure.ac +++ b/configure.ac @@ -85,13 +85,21 @@ AX_CHECK_COMPILE_FLAG([-std=c++20], [CXXFLAGS="$CXXFLAGS -std=c++20"], [ ]) ]) -AC_CONFIG_FILES([ - Makefile - ]) -AC_OUTPUT +AC_CONFIG_FILES([python/setup.py]) AS_IF([test "x$irstlm" == "xno"], [AC_MSG_NOTICE([IRSTLM is not enabled; you will not be able run monolingual rule-learning; enable using --with-irstlm])]) AS_IF([test "x$yasmet" == "xno"], [AC_MSG_NOTICE([YASMET is not enabled; you will not be able run maximum-entropy training; enable using --with-yasmet])]) + +AM_PATH_PYTHON([3.4], [], [AC_MSG_WARN([Can't generate SWIG wrapper without Python])]) + +AC_ARG_ENABLE([python-bindings], + AS_HELP_STRING([--enable-python-bindings], + [build python bindings (default=disabled)]), + [enable_python_bindings=$enableval], + [enable_python_bindings=no]) +AM_CONDITIONAL([HAVE_PYTHON_BINDINGS], [test x$enable_python_bindings = xyes]) + +AC_OUTPUT([Makefile python/Makefile]) diff --git a/lrx_processor.cc b/lrx_processor.cc index 14b241a..1965066 100644 --- a/lrx_processor.cc +++ b/lrx_processor.cc @@ -974,7 +974,7 @@ LRXProcessor::processME(FILE *input, FILE *output) continue; } - fwprintf(stdout, L"%S^%S/", blanks[spos].c_str(), sl[spos].c_str()); + fwprintf(output, L"%S^%S/", blanks[spos].c_str(), sl[spos].c_str()); vector::iterator ti; vector::iterator penum = tl[spos].end(); penum--; @@ -1012,16 +1012,16 @@ LRXProcessor::processME(FILE *input, FILE *output) //fwprintf(stderr, L"MAX: %.5f = %S\n", l_max, ti_max.c_str()); fwprintf(stderr, L"%d:SELECT:%.5f:%S:%S\n", lineno, l_max, sl[spos].c_str(), ti_max.c_str()); } - fwprintf(stdout, L"%S", ti_max.c_str()); + fwprintf(output, L"%S", ti_max.c_str()); } else { for(ti = tl[spos].begin(); ti != tl[spos].end(); ti++) { - fwprintf(stdout, L"%S", ti->c_str()); + fwprintf(output, L"%S", ti->c_str()); if(ti != penum) { - fwprintf(stdout, L"/"); + fwprintf(output, L"/"); } } } @@ -1030,18 +1030,18 @@ LRXProcessor::processME(FILE *input, FILE *output) { for(ti = tl[spos].begin(); ti != tl[spos].end(); ti++) { - fwprintf(stdout, L"%S", ti->c_str()); + fwprintf(output, L"%S", ti->c_str()); if(ti != penum) { - fwprintf(stdout, L"/"); + fwprintf(output, L"/"); } } } - fwprintf(stdout, L"$"); + fwprintf(output, L"$"); if(debugMode) { - fwprintf(stdout, L"%d", spos); + fwprintf(output, L"%d", spos); } } diff --git a/python/Makefile.am b/python/Makefile.am new file mode 100644 index 0000000..e670cb7 --- /dev/null +++ b/python/Makefile.am @@ -0,0 +1,9 @@ +SWIG_INTERFACE = lex_tools.i + +BUILT_SOURCES = %_wrap.cpp + +%_wrap.cpp: $(SWIG_INTERFACE) setup.py + $(PYTHON) setup.py build + +install-exec-local: + $(PYTHON) setup.py install --prefix=$(DESTDIR)$(prefix) diff --git a/python/lex_tools.i b/python/lex_tools.i new file mode 100644 index 0000000..9bc71ff --- /dev/null +++ b/python/lex_tools.i @@ -0,0 +1,57 @@ +%module lextools + +%{ +#define SWIG_FILE_WITH_INIT +#include + + +class LRX: public LRXProcessor +{ +public: + /** + * Imitates functionality of lrx_proc using file path + */ + void lrx_proc(char arg, char *dictionary_path, char *input_path, char *output_path) + { + bool useMaxEnt = false; + FILE *dictionary = fopen(dictionary_path, "rb"); + load(dictionary); + FILE *input = fopen(input_path, "r"), *output = fopen(output_path, "w"); + switch(arg) + { + case 'm': + useMaxEnt = true; + break; + default: + useMaxEnt = false; + } + init(); + if(useMaxEnt) + { + processME(input, output); + } + else + { + process(input, output); + } + fclose(dictionary); + fclose(input); + fclose(output); + } +}; + +%} + + +%include +%include + + +class LRX: public LRXProcessor +{ +public: + /** + * Imitates functionality of lrx_proc using file path + */ + void lrx_proc(char arg, char *dictionary_path, char *input_path, char *output_path); +}; diff --git a/python/setup.py.in b/python/setup.py.in new file mode 100644 index 0000000..7e5ed56 --- /dev/null +++ b/python/setup.py.in @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 + +''' +Setup for SWIG Python bindings for lex-tools +''' +from os import path +from distutils.core import Extension, setup +from distutils.command.build import build + + +class CustomBuild(build): + sub_commands = [ + ('build_ext', build.has_ext_modules), + ('build_py', build.has_pure_modules), + ('build_clib', build.has_c_libraries), + ('build_scripts', build.has_scripts), + ] + + +def get_sources(): + sources = ['lex_tools.i'] + cc_sources = ['lrx_processor.cc'] + rel_path = '..' + sources.extend(path.join(rel_path, f) for f in cc_sources) + return sources + +def get_include_dirs(): + # Remove '-I' from Flags, as python add '-I' on its own + dirs = '@LTTOOLBOX_CFLAGS@'.replace('-I', '').split() + dirs += '@LIBXML_LIBS@'.replace('-I', '').split() + return dirs + ['..'] + + +lextools_module = Extension( + name='_lextools', + sources=get_sources(), + swig_opts=['-c++', '-I..', '-Wall']+'@LTTOOLBOX_CFLAGS@'.split(), + include_dirs=get_include_dirs(), + library_dirs=['/usr/include/libxml2', '/usr/local/lib'], + extra_compile_args='@CXXFLAGS@'.split(), + extra_link_args=['-lxml2', '-llttoolbox3'], +) + +setup( + name='@PACKAGE@', + version='@PACKAGE_VERSION@', + description='SWIG interface to @PACKAGE_NAME@', + long_description='SWIG interface to @PACKAGE_NAME@ for use in apertium-python', + # TODO: author, maintainer, url + author_email='@PACKAGE_BUGREPORT@', + license='GPL-3.0+', + maintainer_email='@PACKAGE_BUGREPORT@', + cmdclass={'build': CustomBuild}, + ext_modules=[lextools_module], + py_modules=['lextools'], +)