diff options
Diffstat (limited to 'src/python')
46 files changed, 5908 insertions, 0 deletions
diff --git a/src/python/Makefile.am b/src/python/Makefile.am new file mode 100644 index 0000000..a1199fd --- /dev/null +++ b/src/python/Makefile.am @@ -0,0 +1,140 @@ +if BUILD_PYTHON + +AM_CPPFLAGS = \ + -I$(top_srcdir)/include \ + -I$(top_srcdir)/src/include \ + $(BOOST_CPPFLAGS) \ + $(PYTHON_CFLAGS) \ + $(MDDS_CFLAGS) \ + $(LIBIXION_CFLAGS) + +pyexec_LTLIBRARIES = _orcus.la _orcus_json.la + +_orcus_la_SOURCES = \ + python.cpp \ + global.hpp \ + global.cpp \ + memory.hpp \ + memory.cpp \ + root.hpp \ + root.cpp \ + xlsx.hpp \ + xlsx.cpp \ + xls_xml.hpp \ + xls_xml.cpp \ + ods.hpp \ + ods.cpp \ + csv.hpp \ + csv.cpp \ + gnumeric.hpp \ + gnumeric.cpp + +_orcus_la_LDFLAGS = -module -avoid-version -export-symbols-regex PyInit__orcus +_orcus_la_LIBADD = \ + ../liborcus/liborcus-@ORCUS_API_VERSION@.la \ + ../parser/liborcus-parser-@ORCUS_API_VERSION@.la \ + $(PYTHON_LIBS) + +if BUILD_SPREADSHEET_MODEL + +_orcus_la_SOURCES += \ + document.hpp \ + document.cpp \ + sheet.hpp \ + sheet.cpp \ + sheet_rows.hpp \ + sheet_rows.cpp \ + cell.hpp \ + cell.cpp \ + formula_token.hpp \ + formula_token.cpp \ + formula_tokens.hpp \ + formula_tokens.cpp \ + named_expressions.hpp \ + named_expressions.cpp \ + named_expression.hpp \ + named_expression.cpp + +_orcus_la_LIBADD += \ + ../spreadsheet/liborcus-spreadsheet-model-@ORCUS_API_VERSION@.la \ + $(LIBIXION_LIBS) + +endif # BUILD_SPREADSHEET_MODEL + +_orcus_json_la_SOURCES = \ + json.cpp + +_orcus_json_la_LDFLAGS = -module -avoid-version -export-symbols-regex PyInit__orcus_json +_orcus_json_la_LIBADD = \ + ../liborcus/liborcus-@ORCUS_API_VERSION@.la \ + ../parser/liborcus-parser-@ORCUS_API_VERSION@.la \ + $(PYTHON_LIBS) + +orcusdir = $(pythondir)/orcus +orcustoolsdir = $(pythondir)/orcus/tools + +orcus_DATA = \ + ./orcus/__init__.py \ + ./orcus/csv.py \ + ./orcus/gnumeric.py \ + ./orcus/json.py \ + ./orcus/ods.py \ + ./orcus/xls_xml.py \ + ./orcus/xlsx.py + +orcustools_DATA = \ + ./orcus/tools/__init__.py \ + ./orcus/tools/bugzilla.py \ + ./orcus/tools/file_processor.py + +EXTRA_DIST = \ + ./orcus/__init__.py \ + ./orcus/csv.py \ + ./orcus/gnumeric.py \ + ./orcus/json.py \ + ./orcus/ods.py \ + ./orcus/xls_xml.py \ + ./orcus/xlsx.py \ + ./orcus/tools/__init__.py \ + ./orcus/tools/bugzilla.py \ + ./orcus/tools/file_processor.py + +AM_TESTS_ENVIRONMENT = \ + PYTHONPATH=$(top_srcdir)/src/python:.libs$${PYTHONPATH:+:$${PYTHONPATH}}; export PYTHONPATH; \ + BUILDDIR=$(top_builddir); export BUILDDIR; + +TESTS = \ + ../../test/python/test_json.py \ + ../../test/python/test_module.py \ + ../../test/python/test_csv.py \ + ../../test/python/test_csv_export.py + +if WITH_PYTHON_XLSX + +TESTS += ../../test/python/test_xlsx.py +AM_TESTS_ENVIRONMENT += export WITH_PYTHON_XLSX=1; + +endif # WITH_PYTHON_XLSX + +if WITH_PYTHON_ODS + +TESTS += ../../test/python/test_ods.py +AM_TESTS_ENVIRONMENT += export WITH_PYTHON_ODS=1; + +endif # WITH_PYTHON_ODS + +if WITH_PYTHON_XLS_XML + +TESTS += ../../test/python/test_xls_xml.py +AM_TESTS_ENVIRONMENT += export WITH_PYTHON_XLS_XML=1; + +endif # WITH_PYTHON_XLS_XML + +if WITH_PYTHON_GNUMERIC + +TESTS += ../../test/python/test_gnumeric.py +AM_TESTS_ENVIRONMENT += export WITH_PYTHON_GNUMERIC=1; + +endif # WITH_PYTHON_GNUMERIC + +endif # BUILD_PYTHON diff --git a/src/python/Makefile.in b/src/python/Makefile.in new file mode 100644 index 0000000..fff782a --- /dev/null +++ b/src/python/Makefile.in @@ -0,0 +1,1405 @@ +# Makefile.in generated by automake 1.16.5 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2021 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +@BUILD_PYTHON_TRUE@@BUILD_SPREADSHEET_MODEL_TRUE@am__append_1 = \ +@BUILD_PYTHON_TRUE@@BUILD_SPREADSHEET_MODEL_TRUE@ document.hpp \ +@BUILD_PYTHON_TRUE@@BUILD_SPREADSHEET_MODEL_TRUE@ document.cpp \ +@BUILD_PYTHON_TRUE@@BUILD_SPREADSHEET_MODEL_TRUE@ sheet.hpp \ +@BUILD_PYTHON_TRUE@@BUILD_SPREADSHEET_MODEL_TRUE@ sheet.cpp \ +@BUILD_PYTHON_TRUE@@BUILD_SPREADSHEET_MODEL_TRUE@ sheet_rows.hpp \ +@BUILD_PYTHON_TRUE@@BUILD_SPREADSHEET_MODEL_TRUE@ sheet_rows.cpp \ +@BUILD_PYTHON_TRUE@@BUILD_SPREADSHEET_MODEL_TRUE@ cell.hpp \ +@BUILD_PYTHON_TRUE@@BUILD_SPREADSHEET_MODEL_TRUE@ cell.cpp \ +@BUILD_PYTHON_TRUE@@BUILD_SPREADSHEET_MODEL_TRUE@ formula_token.hpp \ +@BUILD_PYTHON_TRUE@@BUILD_SPREADSHEET_MODEL_TRUE@ formula_token.cpp \ +@BUILD_PYTHON_TRUE@@BUILD_SPREADSHEET_MODEL_TRUE@ formula_tokens.hpp \ +@BUILD_PYTHON_TRUE@@BUILD_SPREADSHEET_MODEL_TRUE@ formula_tokens.cpp \ +@BUILD_PYTHON_TRUE@@BUILD_SPREADSHEET_MODEL_TRUE@ named_expressions.hpp \ +@BUILD_PYTHON_TRUE@@BUILD_SPREADSHEET_MODEL_TRUE@ named_expressions.cpp \ +@BUILD_PYTHON_TRUE@@BUILD_SPREADSHEET_MODEL_TRUE@ named_expression.hpp \ +@BUILD_PYTHON_TRUE@@BUILD_SPREADSHEET_MODEL_TRUE@ named_expression.cpp + +@BUILD_PYTHON_TRUE@@BUILD_SPREADSHEET_MODEL_TRUE@am__append_2 = \ +@BUILD_PYTHON_TRUE@@BUILD_SPREADSHEET_MODEL_TRUE@ ../spreadsheet/liborcus-spreadsheet-model-@ORCUS_API_VERSION@.la \ +@BUILD_PYTHON_TRUE@@BUILD_SPREADSHEET_MODEL_TRUE@ $(LIBIXION_LIBS) + +@BUILD_PYTHON_TRUE@@WITH_PYTHON_XLSX_TRUE@am__append_3 = ../../test/python/test_xlsx.py +@BUILD_PYTHON_TRUE@@WITH_PYTHON_XLSX_TRUE@am__append_4 = export WITH_PYTHON_XLSX=1; +@BUILD_PYTHON_TRUE@@WITH_PYTHON_ODS_TRUE@am__append_5 = ../../test/python/test_ods.py +@BUILD_PYTHON_TRUE@@WITH_PYTHON_ODS_TRUE@am__append_6 = export WITH_PYTHON_ODS=1; +@BUILD_PYTHON_TRUE@@WITH_PYTHON_XLS_XML_TRUE@am__append_7 = ../../test/python/test_xls_xml.py +@BUILD_PYTHON_TRUE@@WITH_PYTHON_XLS_XML_TRUE@am__append_8 = export WITH_PYTHON_XLS_XML=1; +@BUILD_PYTHON_TRUE@@WITH_PYTHON_GNUMERIC_TRUE@am__append_9 = ../../test/python/test_gnumeric.py +@BUILD_PYTHON_TRUE@@WITH_PYTHON_GNUMERIC_TRUE@am__append_10 = export WITH_PYTHON_GNUMERIC=1; +subdir = src/python +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \ + $(top_srcdir)/m4/ax_cxx_compile_stdcxx_17.m4 \ + $(top_srcdir)/m4/boost.m4 $(top_srcdir)/m4/libtool.m4 \ + $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ + $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/m4/m4_ax_valgrind_check.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(pyexecdir)" "$(DESTDIR)$(orcusdir)" \ + "$(DESTDIR)$(orcustoolsdir)" +LTLIBRARIES = $(pyexec_LTLIBRARIES) +am__DEPENDENCIES_1 = +@BUILD_PYTHON_TRUE@@BUILD_SPREADSHEET_MODEL_TRUE@am__DEPENDENCIES_2 = ../spreadsheet/liborcus-spreadsheet-model-@ORCUS_API_VERSION@.la \ +@BUILD_PYTHON_TRUE@@BUILD_SPREADSHEET_MODEL_TRUE@ $(am__DEPENDENCIES_1) +@BUILD_PYTHON_TRUE@_orcus_la_DEPENDENCIES = ../liborcus/liborcus-@ORCUS_API_VERSION@.la \ +@BUILD_PYTHON_TRUE@ ../parser/liborcus-parser-@ORCUS_API_VERSION@.la \ +@BUILD_PYTHON_TRUE@ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2) +am___orcus_la_SOURCES_DIST = python.cpp global.hpp global.cpp \ + memory.hpp memory.cpp root.hpp root.cpp xlsx.hpp xlsx.cpp \ + xls_xml.hpp xls_xml.cpp ods.hpp ods.cpp csv.hpp csv.cpp \ + gnumeric.hpp gnumeric.cpp document.hpp document.cpp sheet.hpp \ + sheet.cpp sheet_rows.hpp sheet_rows.cpp cell.hpp cell.cpp \ + formula_token.hpp formula_token.cpp formula_tokens.hpp \ + formula_tokens.cpp named_expressions.hpp named_expressions.cpp \ + named_expression.hpp named_expression.cpp +@BUILD_PYTHON_TRUE@@BUILD_SPREADSHEET_MODEL_TRUE@am__objects_1 = \ +@BUILD_PYTHON_TRUE@@BUILD_SPREADSHEET_MODEL_TRUE@ document.lo \ +@BUILD_PYTHON_TRUE@@BUILD_SPREADSHEET_MODEL_TRUE@ sheet.lo \ +@BUILD_PYTHON_TRUE@@BUILD_SPREADSHEET_MODEL_TRUE@ sheet_rows.lo \ +@BUILD_PYTHON_TRUE@@BUILD_SPREADSHEET_MODEL_TRUE@ cell.lo \ +@BUILD_PYTHON_TRUE@@BUILD_SPREADSHEET_MODEL_TRUE@ formula_token.lo \ +@BUILD_PYTHON_TRUE@@BUILD_SPREADSHEET_MODEL_TRUE@ formula_tokens.lo \ +@BUILD_PYTHON_TRUE@@BUILD_SPREADSHEET_MODEL_TRUE@ named_expressions.lo \ +@BUILD_PYTHON_TRUE@@BUILD_SPREADSHEET_MODEL_TRUE@ named_expression.lo +@BUILD_PYTHON_TRUE@am__orcus_la_OBJECTS = python.lo global.lo \ +@BUILD_PYTHON_TRUE@ memory.lo root.lo xlsx.lo xls_xml.lo ods.lo \ +@BUILD_PYTHON_TRUE@ csv.lo gnumeric.lo $(am__objects_1) +_orcus_la_OBJECTS = $(am__orcus_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +_orcus_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ + $(CXXFLAGS) $(_orcus_la_LDFLAGS) $(LDFLAGS) -o $@ +@BUILD_PYTHON_TRUE@am__orcus_la_rpath = -rpath $(pyexecdir) +@BUILD_PYTHON_TRUE@_orcus_json_la_DEPENDENCIES = ../liborcus/liborcus-@ORCUS_API_VERSION@.la \ +@BUILD_PYTHON_TRUE@ ../parser/liborcus-parser-@ORCUS_API_VERSION@.la \ +@BUILD_PYTHON_TRUE@ $(am__DEPENDENCIES_1) +am___orcus_json_la_SOURCES_DIST = json.cpp +@BUILD_PYTHON_TRUE@am__orcus_json_la_OBJECTS = json.lo +_orcus_json_la_OBJECTS = $(am__orcus_json_la_OBJECTS) +_orcus_json_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \ + $(AM_CXXFLAGS) $(CXXFLAGS) $(_orcus_json_la_LDFLAGS) \ + $(LDFLAGS) -o $@ +@BUILD_PYTHON_TRUE@am__orcus_json_la_rpath = -rpath $(pyexecdir) +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/cell.Plo ./$(DEPDIR)/csv.Plo \ + ./$(DEPDIR)/document.Plo ./$(DEPDIR)/formula_token.Plo \ + ./$(DEPDIR)/formula_tokens.Plo ./$(DEPDIR)/global.Plo \ + ./$(DEPDIR)/gnumeric.Plo ./$(DEPDIR)/json.Plo \ + ./$(DEPDIR)/memory.Plo ./$(DEPDIR)/named_expression.Plo \ + ./$(DEPDIR)/named_expressions.Plo ./$(DEPDIR)/ods.Plo \ + ./$(DEPDIR)/python.Plo ./$(DEPDIR)/root.Plo \ + ./$(DEPDIR)/sheet.Plo ./$(DEPDIR)/sheet_rows.Plo \ + ./$(DEPDIR)/xls_xml.Plo ./$(DEPDIR)/xlsx.Plo +am__mv = mv -f +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CXXFLAGS) $(CXXFLAGS) +AM_V_CXX = $(am__v_CXX_@AM_V@) +am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) +am__v_CXX_0 = @echo " CXX " $@; +am__v_CXX_1 = +CXXLD = $(CXX) +CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ + $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) +am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) +am__v_CXXLD_0 = @echo " CXXLD " $@; +am__v_CXXLD_1 = +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(_orcus_la_SOURCES) $(_orcus_json_la_SOURCES) +DIST_SOURCES = $(am___orcus_la_SOURCES_DIST) \ + $(am___orcus_json_la_SOURCES_DIST) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +DATA = $(orcus_DATA) $(orcustools_DATA) +am__extra_recursive_targets = check-valgrind-recursive \ + check-valgrind-memcheck-recursive \ + check-valgrind-helgrind-recursive check-valgrind-drd-recursive \ + check-valgrind-sgcheck-recursive +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +am__tty_colors_dummy = \ + mgn= red= grn= lgn= blu= brg= std=; \ + am__color_tests=no +am__tty_colors = { \ + $(am__tty_colors_dummy); \ + if test "X$(AM_COLOR_TESTS)" = Xno; then \ + am__color_tests=no; \ + elif test "X$(AM_COLOR_TESTS)" = Xalways; then \ + am__color_tests=yes; \ + elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \ + am__color_tests=yes; \ + fi; \ + if test $$am__color_tests = yes; then \ + red='[0;31m'; \ + grn='[0;32m'; \ + lgn='[1;32m'; \ + blu='[1;34m'; \ + mgn='[0;35m'; \ + brg='[1m'; \ + std='[m'; \ + fi; \ +} +am__recheck_rx = ^[ ]*:recheck:[ ]* +am__global_test_result_rx = ^[ ]*:global-test-result:[ ]* +am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]* +# A command that, given a newline-separated list of test names on the +# standard input, print the name of the tests that are to be re-run +# upon "make recheck". +am__list_recheck_tests = $(AWK) '{ \ + recheck = 1; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + { \ + if ((getline line2 < ($$0 ".log")) < 0) \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \ + { \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \ + { \ + break; \ + } \ + }; \ + if (recheck) \ + print $$0; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# A command that, given a newline-separated list of test names on the +# standard input, create the global log from their .trs and .log files. +am__create_global_log = $(AWK) ' \ +function fatal(msg) \ +{ \ + print "fatal: making $@: " msg | "cat >&2"; \ + exit 1; \ +} \ +function rst_section(header) \ +{ \ + print header; \ + len = length(header); \ + for (i = 1; i <= len; i = i + 1) \ + printf "="; \ + printf "\n\n"; \ +} \ +{ \ + copy_in_global_log = 1; \ + global_test_result = "RUN"; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".trs"); \ + if (line ~ /$(am__global_test_result_rx)/) \ + { \ + sub("$(am__global_test_result_rx)", "", line); \ + sub("[ ]*$$", "", line); \ + global_test_result = line; \ + } \ + else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \ + copy_in_global_log = 0; \ + }; \ + if (copy_in_global_log) \ + { \ + rst_section(global_test_result ": " $$0); \ + while ((rc = (getline line < ($$0 ".log"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".log"); \ + print line; \ + }; \ + printf "\n"; \ + }; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# Restructured Text title. +am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; } +# Solaris 10 'make', and several other traditional 'make' implementations, +# pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it +# by disabling -e (using the XSI extension "set +e") if it's set. +am__sh_e_setup = case $$- in *e*) set +e;; esac +# Default flags passed to test drivers. +am__common_driver_flags = \ + --color-tests "$$am__color_tests" \ + --enable-hard-errors "$$am__enable_hard_errors" \ + --expect-failure "$$am__expect_failure" +# To be inserted before the command running the test. Creates the +# directory for the log if needed. Stores in $dir the directory +# containing $f, in $tst the test, in $log the log. Executes the +# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and +# passes TESTS_ENVIRONMENT. Set up options for the wrapper that +# will run the test scripts (or their associated LOG_COMPILER, if +# thy have one). +am__check_pre = \ +$(am__sh_e_setup); \ +$(am__vpath_adj_setup) $(am__vpath_adj) \ +$(am__tty_colors); \ +srcdir=$(srcdir); export srcdir; \ +case "$@" in \ + */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \ + *) am__odir=.;; \ +esac; \ +test "x$$am__odir" = x"." || test -d "$$am__odir" \ + || $(MKDIR_P) "$$am__odir" || exit $$?; \ +if test -f "./$$f"; then dir=./; \ +elif test -f "$$f"; then dir=; \ +else dir="$(srcdir)/"; fi; \ +tst=$$dir$$f; log='$@'; \ +if test -n '$(DISABLE_HARD_ERRORS)'; then \ + am__enable_hard_errors=no; \ +else \ + am__enable_hard_errors=yes; \ +fi; \ +case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \ + am__expect_failure=yes;; \ + *) \ + am__expect_failure=no;; \ +esac; \ +$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT) +# A shell command to get the names of the tests scripts with any registered +# extension removed (i.e., equivalently, the names of the test logs, with +# the '.log' extension removed). The result is saved in the shell variable +# '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly, +# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)", +# since that might cause problem with VPATH rewrites for suffix-less tests. +# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'. +am__set_TESTS_bases = \ + bases='$(TEST_LOGS)'; \ + bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \ + bases=`echo $$bases` +AM_TESTSUITE_SUMMARY_HEADER = ' for $(PACKAGE_STRING)' +RECHECK_LOGS = $(TEST_LOGS) +AM_RECURSIVE_TARGETS = check recheck +TEST_SUITE_LOG = test-suite.log +TEST_EXTENSIONS = @EXEEXT@ .test +LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver +LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS) +am__set_b = \ + case '$@' in \ + */*) \ + case '$*' in \ + */*) b='$*';; \ + *) b=`echo '$@' | sed 's/\.log$$//'`; \ + esac;; \ + *) \ + b='$*';; \ + esac +am__test_logs1 = $(TESTS:=.log) +am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log) +TEST_LOGS = $(am__test_logs2:.test.log=.log) +TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver +TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \ + $(TEST_LOG_FLAGS) +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp \ + $(top_srcdir)/test-driver +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AS = @AS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BOOST_CPPFLAGS = @BOOST_CPPFLAGS@ +BOOST_DATE_TIME_LDFLAGS = @BOOST_DATE_TIME_LDFLAGS@ +BOOST_DATE_TIME_LDPATH = @BOOST_DATE_TIME_LDPATH@ +BOOST_DATE_TIME_LIBS = @BOOST_DATE_TIME_LIBS@ +BOOST_FILESYSTEM_LDFLAGS = @BOOST_FILESYSTEM_LDFLAGS@ +BOOST_FILESYSTEM_LDPATH = @BOOST_FILESYSTEM_LDPATH@ +BOOST_FILESYSTEM_LIBS = @BOOST_FILESYSTEM_LIBS@ +BOOST_IOSTREAMS_LDFLAGS = @BOOST_IOSTREAMS_LDFLAGS@ +BOOST_IOSTREAMS_LDPATH = @BOOST_IOSTREAMS_LDPATH@ +BOOST_IOSTREAMS_LIBS = @BOOST_IOSTREAMS_LIBS@ +BOOST_LDPATH = @BOOST_LDPATH@ +BOOST_PROGRAM_OPTIONS_LDFLAGS = @BOOST_PROGRAM_OPTIONS_LDFLAGS@ +BOOST_PROGRAM_OPTIONS_LDPATH = @BOOST_PROGRAM_OPTIONS_LDPATH@ +BOOST_PROGRAM_OPTIONS_LIBS = @BOOST_PROGRAM_OPTIONS_LIBS@ +BOOST_ROOT = @BOOST_ROOT@ +BOOST_SYSTEM_LDFLAGS = @BOOST_SYSTEM_LDFLAGS@ +BOOST_SYSTEM_LDPATH = @BOOST_SYSTEM_LDPATH@ +BOOST_SYSTEM_LIBS = @BOOST_SYSTEM_LIBS@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CSCOPE = @CSCOPE@ +CTAGS = @CTAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DISTCHECK_CONFIGURE_FLAGS = @DISTCHECK_CONFIGURE_FLAGS@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ENABLE_VALGRIND_drd = @ENABLE_VALGRIND_drd@ +ENABLE_VALGRIND_helgrind = @ENABLE_VALGRIND_helgrind@ +ENABLE_VALGRIND_memcheck = @ENABLE_VALGRIND_memcheck@ +ENABLE_VALGRIND_sgcheck = @ENABLE_VALGRIND_sgcheck@ +ETAGS = @ETAGS@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GREP = @GREP@ +HAVE_CXX17 = @HAVE_CXX17@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +IXION_REQUIRED_API_VERSION = @IXION_REQUIRED_API_VERSION@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBIXION_CFLAGS = @LIBIXION_CFLAGS@ +LIBIXION_LIBS = @LIBIXION_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MDDS_CFLAGS = @MDDS_CFLAGS@ +MDDS_LIBS = @MDDS_LIBS@ +MKDIR_P = @MKDIR_P@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +ORCUS_API_VERSION = @ORCUS_API_VERSION@ +ORCUS_MAJOR_VERSION = @ORCUS_MAJOR_VERSION@ +ORCUS_MICRO_VERSION = @ORCUS_MICRO_VERSION@ +ORCUS_MINOR_VERSION = @ORCUS_MINOR_VERSION@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PARQUET_CFLAGS = @PARQUET_CFLAGS@ +PARQUET_LIBS = @PARQUET_LIBS@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +POW_LIB = @POW_LIB@ +PYTHON = @PYTHON@ +PYTHON_CFLAGS = @PYTHON_CFLAGS@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_LIBS = @PYTHON_LIBS@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VALGRIND = @VALGRIND@ +VALGRIND_ENABLED = @VALGRIND_ENABLED@ +VERSION = @VERSION@ +ZLIB_CFLAGS = @ZLIB_CFLAGS@ +ZLIB_LIBS = @ZLIB_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +valgrind_enabled_tools = @valgrind_enabled_tools@ +valgrind_tools = @valgrind_tools@ +@BUILD_PYTHON_TRUE@AM_CPPFLAGS = \ +@BUILD_PYTHON_TRUE@ -I$(top_srcdir)/include \ +@BUILD_PYTHON_TRUE@ -I$(top_srcdir)/src/include \ +@BUILD_PYTHON_TRUE@ $(BOOST_CPPFLAGS) \ +@BUILD_PYTHON_TRUE@ $(PYTHON_CFLAGS) \ +@BUILD_PYTHON_TRUE@ $(MDDS_CFLAGS) \ +@BUILD_PYTHON_TRUE@ $(LIBIXION_CFLAGS) + +@BUILD_PYTHON_TRUE@pyexec_LTLIBRARIES = _orcus.la _orcus_json.la +@BUILD_PYTHON_TRUE@_orcus_la_SOURCES = python.cpp global.hpp \ +@BUILD_PYTHON_TRUE@ global.cpp memory.hpp memory.cpp root.hpp \ +@BUILD_PYTHON_TRUE@ root.cpp xlsx.hpp xlsx.cpp xls_xml.hpp \ +@BUILD_PYTHON_TRUE@ xls_xml.cpp ods.hpp ods.cpp csv.hpp csv.cpp \ +@BUILD_PYTHON_TRUE@ gnumeric.hpp gnumeric.cpp $(am__append_1) +@BUILD_PYTHON_TRUE@_orcus_la_LDFLAGS = -module -avoid-version -export-symbols-regex PyInit__orcus +@BUILD_PYTHON_TRUE@_orcus_la_LIBADD = ../liborcus/liborcus-@ORCUS_API_VERSION@.la \ +@BUILD_PYTHON_TRUE@ ../parser/liborcus-parser-@ORCUS_API_VERSION@.la \ +@BUILD_PYTHON_TRUE@ $(PYTHON_LIBS) $(am__append_2) +@BUILD_PYTHON_TRUE@_orcus_json_la_SOURCES = \ +@BUILD_PYTHON_TRUE@ json.cpp + +@BUILD_PYTHON_TRUE@_orcus_json_la_LDFLAGS = -module -avoid-version -export-symbols-regex PyInit__orcus_json +@BUILD_PYTHON_TRUE@_orcus_json_la_LIBADD = \ +@BUILD_PYTHON_TRUE@ ../liborcus/liborcus-@ORCUS_API_VERSION@.la \ +@BUILD_PYTHON_TRUE@ ../parser/liborcus-parser-@ORCUS_API_VERSION@.la \ +@BUILD_PYTHON_TRUE@ $(PYTHON_LIBS) + +@BUILD_PYTHON_TRUE@orcusdir = $(pythondir)/orcus +@BUILD_PYTHON_TRUE@orcustoolsdir = $(pythondir)/orcus/tools +@BUILD_PYTHON_TRUE@orcus_DATA = \ +@BUILD_PYTHON_TRUE@ ./orcus/__init__.py \ +@BUILD_PYTHON_TRUE@ ./orcus/csv.py \ +@BUILD_PYTHON_TRUE@ ./orcus/gnumeric.py \ +@BUILD_PYTHON_TRUE@ ./orcus/json.py \ +@BUILD_PYTHON_TRUE@ ./orcus/ods.py \ +@BUILD_PYTHON_TRUE@ ./orcus/xls_xml.py \ +@BUILD_PYTHON_TRUE@ ./orcus/xlsx.py + +@BUILD_PYTHON_TRUE@orcustools_DATA = \ +@BUILD_PYTHON_TRUE@ ./orcus/tools/__init__.py \ +@BUILD_PYTHON_TRUE@ ./orcus/tools/bugzilla.py \ +@BUILD_PYTHON_TRUE@ ./orcus/tools/file_processor.py + +@BUILD_PYTHON_TRUE@EXTRA_DIST = \ +@BUILD_PYTHON_TRUE@ ./orcus/__init__.py \ +@BUILD_PYTHON_TRUE@ ./orcus/csv.py \ +@BUILD_PYTHON_TRUE@ ./orcus/gnumeric.py \ +@BUILD_PYTHON_TRUE@ ./orcus/json.py \ +@BUILD_PYTHON_TRUE@ ./orcus/ods.py \ +@BUILD_PYTHON_TRUE@ ./orcus/xls_xml.py \ +@BUILD_PYTHON_TRUE@ ./orcus/xlsx.py \ +@BUILD_PYTHON_TRUE@ ./orcus/tools/__init__.py \ +@BUILD_PYTHON_TRUE@ ./orcus/tools/bugzilla.py \ +@BUILD_PYTHON_TRUE@ ./orcus/tools/file_processor.py + +@BUILD_PYTHON_TRUE@AM_TESTS_ENVIRONMENT = PYTHONPATH=$(top_srcdir)/src/python:.libs$${PYTHONPATH:+:$${PYTHONPATH}}; \ +@BUILD_PYTHON_TRUE@ export PYTHONPATH; \ +@BUILD_PYTHON_TRUE@ BUILDDIR=$(top_builddir); export BUILDDIR; \ +@BUILD_PYTHON_TRUE@ $(am__append_4) $(am__append_6) \ +@BUILD_PYTHON_TRUE@ $(am__append_8) $(am__append_10) +@BUILD_PYTHON_TRUE@TESTS = ../../test/python/test_json.py \ +@BUILD_PYTHON_TRUE@ ../../test/python/test_module.py \ +@BUILD_PYTHON_TRUE@ ../../test/python/test_csv.py \ +@BUILD_PYTHON_TRUE@ ../../test/python/test_csv_export.py \ +@BUILD_PYTHON_TRUE@ $(am__append_3) $(am__append_5) \ +@BUILD_PYTHON_TRUE@ $(am__append_7) $(am__append_9) +all: all-am + +.SUFFIXES: +.SUFFIXES: .cpp .lo .log .o .obj .test .test$(EXEEXT) .trs +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/python/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/python/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +install-pyexecLTLIBRARIES: $(pyexec_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(pyexec_LTLIBRARIES)'; test -n "$(pyexecdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(pyexecdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pyexecdir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(pyexecdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(pyexecdir)"; \ + } + +uninstall-pyexecLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(pyexec_LTLIBRARIES)'; test -n "$(pyexecdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(pyexecdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(pyexecdir)/$$f"; \ + done + +clean-pyexecLTLIBRARIES: + -test -z "$(pyexec_LTLIBRARIES)" || rm -f $(pyexec_LTLIBRARIES) + @list='$(pyexec_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +_orcus.la: $(_orcus_la_OBJECTS) $(_orcus_la_DEPENDENCIES) $(EXTRA__orcus_la_DEPENDENCIES) + $(AM_V_CXXLD)$(_orcus_la_LINK) $(am__orcus_la_rpath) $(_orcus_la_OBJECTS) $(_orcus_la_LIBADD) $(LIBS) + +_orcus_json.la: $(_orcus_json_la_OBJECTS) $(_orcus_json_la_DEPENDENCIES) $(EXTRA__orcus_json_la_DEPENDENCIES) + $(AM_V_CXXLD)$(_orcus_json_la_LINK) $(am__orcus_json_la_rpath) $(_orcus_json_la_OBJECTS) $(_orcus_json_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cell.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/csv.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/document.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/formula_token.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/formula_tokens.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/global.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gnumeric.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/json.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/memory.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/named_expression.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/named_expressions.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ods.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/python.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/root.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sheet.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sheet_rows.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xls_xml.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xlsx.Plo@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.cpp.o: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< + +.cpp.obj: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cpp.lo: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-orcusDATA: $(orcus_DATA) + @$(NORMAL_INSTALL) + @list='$(orcus_DATA)'; test -n "$(orcusdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(orcusdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(orcusdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(orcusdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(orcusdir)" || exit $$?; \ + done + +uninstall-orcusDATA: + @$(NORMAL_UNINSTALL) + @list='$(orcus_DATA)'; test -n "$(orcusdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(orcusdir)'; $(am__uninstall_files_from_dir) +install-orcustoolsDATA: $(orcustools_DATA) + @$(NORMAL_INSTALL) + @list='$(orcustools_DATA)'; test -n "$(orcustoolsdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(orcustoolsdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(orcustoolsdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(orcustoolsdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(orcustoolsdir)" || exit $$?; \ + done + +uninstall-orcustoolsDATA: + @$(NORMAL_UNINSTALL) + @list='$(orcustools_DATA)'; test -n "$(orcustoolsdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(orcustoolsdir)'; $(am__uninstall_files_from_dir) +check-valgrind-local: +check-valgrind-memcheck-local: +check-valgrind-helgrind-local: +check-valgrind-drd-local: +check-valgrind-sgcheck-local: + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +# Recover from deleted '.trs' file; this should ensure that +# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create +# both 'foo.log' and 'foo.trs'. Break the recipe in two subshells +# to avoid problems with "make -n". +.log.trs: + rm -f $< $@ + $(MAKE) $(AM_MAKEFLAGS) $< + +# Leading 'am--fnord' is there to ensure the list of targets does not +# expand to empty, as could happen e.g. with make check TESTS=''. +am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck) +am--force-recheck: + @: + +$(TEST_SUITE_LOG): $(TEST_LOGS) + @$(am__set_TESTS_bases); \ + am__f_ok () { test -f "$$1" && test -r "$$1"; }; \ + redo_bases=`for i in $$bases; do \ + am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \ + done`; \ + if test -n "$$redo_bases"; then \ + redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \ + redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \ + if $(am__make_dryrun); then :; else \ + rm -f $$redo_logs && rm -f $$redo_results || exit 1; \ + fi; \ + fi; \ + if test -n "$$am__remaking_logs"; then \ + echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \ + "recursion detected" >&2; \ + elif test -n "$$redo_logs"; then \ + am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \ + fi; \ + if $(am__make_dryrun); then :; else \ + st=0; \ + errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \ + for i in $$redo_bases; do \ + test -f $$i.trs && test -r $$i.trs \ + || { echo "$$errmsg $$i.trs" >&2; st=1; }; \ + test -f $$i.log && test -r $$i.log \ + || { echo "$$errmsg $$i.log" >&2; st=1; }; \ + done; \ + test $$st -eq 0 || exit 1; \ + fi + @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \ + ws='[ ]'; \ + results=`for b in $$bases; do echo $$b.trs; done`; \ + test -n "$$results" || results=/dev/null; \ + all=` grep "^$$ws*:test-result:" $$results | wc -l`; \ + pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \ + fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \ + skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \ + xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \ + xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \ + error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \ + if test `expr $$fail + $$xpass + $$error` -eq 0; then \ + success=true; \ + else \ + success=false; \ + fi; \ + br='==================='; br=$$br$$br$$br$$br; \ + result_count () \ + { \ + if test x"$$1" = x"--maybe-color"; then \ + maybe_colorize=yes; \ + elif test x"$$1" = x"--no-color"; then \ + maybe_colorize=no; \ + else \ + echo "$@: invalid 'result_count' usage" >&2; exit 4; \ + fi; \ + shift; \ + desc=$$1 count=$$2; \ + if test $$maybe_colorize = yes && test $$count -gt 0; then \ + color_start=$$3 color_end=$$std; \ + else \ + color_start= color_end=; \ + fi; \ + echo "$${color_start}# $$desc $$count$${color_end}"; \ + }; \ + create_testsuite_report () \ + { \ + result_count $$1 "TOTAL:" $$all "$$brg"; \ + result_count $$1 "PASS: " $$pass "$$grn"; \ + result_count $$1 "SKIP: " $$skip "$$blu"; \ + result_count $$1 "XFAIL:" $$xfail "$$lgn"; \ + result_count $$1 "FAIL: " $$fail "$$red"; \ + result_count $$1 "XPASS:" $$xpass "$$red"; \ + result_count $$1 "ERROR:" $$error "$$mgn"; \ + }; \ + { \ + echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \ + $(am__rst_title); \ + create_testsuite_report --no-color; \ + echo; \ + echo ".. contents:: :depth: 2"; \ + echo; \ + for b in $$bases; do echo $$b; done \ + | $(am__create_global_log); \ + } >$(TEST_SUITE_LOG).tmp || exit 1; \ + mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \ + if $$success; then \ + col="$$grn"; \ + else \ + col="$$red"; \ + test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \ + fi; \ + echo "$${col}$$br$${std}"; \ + echo "$${col}Testsuite summary"$(AM_TESTSUITE_SUMMARY_HEADER)"$${std}"; \ + echo "$${col}$$br$${std}"; \ + create_testsuite_report --maybe-color; \ + echo "$$col$$br$$std"; \ + if $$success; then :; else \ + echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \ + if test -n "$(PACKAGE_BUGREPORT)"; then \ + echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \ + fi; \ + echo "$$col$$br$$std"; \ + fi; \ + $$success || exit 1 + +check-TESTS: + @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list + @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list + @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + @set +e; $(am__set_TESTS_bases); \ + log_list=`for i in $$bases; do echo $$i.log; done`; \ + trs_list=`for i in $$bases; do echo $$i.trs; done`; \ + log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \ + $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \ + exit $$?; +recheck: all + @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + @set +e; $(am__set_TESTS_bases); \ + bases=`for i in $$bases; do echo $$i; done \ + | $(am__list_recheck_tests)` || exit 1; \ + log_list=`for i in $$bases; do echo $$i.log; done`; \ + log_list=`echo $$log_list`; \ + $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \ + am__force_recheck=am--force-recheck \ + TEST_LOGS="$$log_list"; \ + exit $$? +../../test/python/test_json.py.log: ../../test/python/test_json.py + @p='../../test/python/test_json.py'; \ + b='../../test/python/test_json.py'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +../../test/python/test_module.py.log: ../../test/python/test_module.py + @p='../../test/python/test_module.py'; \ + b='../../test/python/test_module.py'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +../../test/python/test_csv.py.log: ../../test/python/test_csv.py + @p='../../test/python/test_csv.py'; \ + b='../../test/python/test_csv.py'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +../../test/python/test_csv_export.py.log: ../../test/python/test_csv_export.py + @p='../../test/python/test_csv_export.py'; \ + b='../../test/python/test_csv_export.py'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +../../test/python/test_xlsx.py.log: ../../test/python/test_xlsx.py + @p='../../test/python/test_xlsx.py'; \ + b='../../test/python/test_xlsx.py'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +../../test/python/test_ods.py.log: ../../test/python/test_ods.py + @p='../../test/python/test_ods.py'; \ + b='../../test/python/test_ods.py'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +../../test/python/test_xls_xml.py.log: ../../test/python/test_xls_xml.py + @p='../../test/python/test_xls_xml.py'; \ + b='../../test/python/test_xls_xml.py'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +../../test/python/test_gnumeric.py.log: ../../test/python/test_gnumeric.py + @p='../../test/python/test_gnumeric.py'; \ + b='../../test/python/test_gnumeric.py'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +.test.log: + @p='$<'; \ + $(am__set_b); \ + $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +@am__EXEEXT_TRUE@.test$(EXEEXT).log: +@am__EXEEXT_TRUE@ @p='$<'; \ +@am__EXEEXT_TRUE@ $(am__set_b); \ +@am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ +@am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \ +@am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ +@am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT) +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am + $(MAKE) $(AM_MAKEFLAGS) check-TESTS +check: check-am +all-am: Makefile $(LTLIBRARIES) $(DATA) +installdirs: + for dir in "$(DESTDIR)$(pyexecdir)" "$(DESTDIR)$(orcusdir)" "$(DESTDIR)$(orcustoolsdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS) + -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs) + -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +check-valgrind: check-valgrind-am + +check-valgrind-am: check-valgrind-local + +check-valgrind-drd: check-valgrind-drd-am + +check-valgrind-drd-am: check-valgrind-drd-local + +check-valgrind-helgrind: check-valgrind-helgrind-am + +check-valgrind-helgrind-am: check-valgrind-helgrind-local + +check-valgrind-memcheck: check-valgrind-memcheck-am + +check-valgrind-memcheck-am: check-valgrind-memcheck-local + +check-valgrind-sgcheck: check-valgrind-sgcheck-am + +check-valgrind-sgcheck-am: check-valgrind-sgcheck-local + +clean: clean-am + +clean-am: clean-generic clean-libtool clean-pyexecLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/cell.Plo + -rm -f ./$(DEPDIR)/csv.Plo + -rm -f ./$(DEPDIR)/document.Plo + -rm -f ./$(DEPDIR)/formula_token.Plo + -rm -f ./$(DEPDIR)/formula_tokens.Plo + -rm -f ./$(DEPDIR)/global.Plo + -rm -f ./$(DEPDIR)/gnumeric.Plo + -rm -f ./$(DEPDIR)/json.Plo + -rm -f ./$(DEPDIR)/memory.Plo + -rm -f ./$(DEPDIR)/named_expression.Plo + -rm -f ./$(DEPDIR)/named_expressions.Plo + -rm -f ./$(DEPDIR)/ods.Plo + -rm -f ./$(DEPDIR)/python.Plo + -rm -f ./$(DEPDIR)/root.Plo + -rm -f ./$(DEPDIR)/sheet.Plo + -rm -f ./$(DEPDIR)/sheet_rows.Plo + -rm -f ./$(DEPDIR)/xls_xml.Plo + -rm -f ./$(DEPDIR)/xlsx.Plo + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-orcusDATA install-orcustoolsDATA + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-pyexecLTLIBRARIES + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f ./$(DEPDIR)/cell.Plo + -rm -f ./$(DEPDIR)/csv.Plo + -rm -f ./$(DEPDIR)/document.Plo + -rm -f ./$(DEPDIR)/formula_token.Plo + -rm -f ./$(DEPDIR)/formula_tokens.Plo + -rm -f ./$(DEPDIR)/global.Plo + -rm -f ./$(DEPDIR)/gnumeric.Plo + -rm -f ./$(DEPDIR)/json.Plo + -rm -f ./$(DEPDIR)/memory.Plo + -rm -f ./$(DEPDIR)/named_expression.Plo + -rm -f ./$(DEPDIR)/named_expressions.Plo + -rm -f ./$(DEPDIR)/ods.Plo + -rm -f ./$(DEPDIR)/python.Plo + -rm -f ./$(DEPDIR)/root.Plo + -rm -f ./$(DEPDIR)/sheet.Plo + -rm -f ./$(DEPDIR)/sheet_rows.Plo + -rm -f ./$(DEPDIR)/xls_xml.Plo + -rm -f ./$(DEPDIR)/xlsx.Plo + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-orcusDATA uninstall-orcustoolsDATA \ + uninstall-pyexecLTLIBRARIES + +.MAKE: check-am install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-TESTS \ + check-am check-valgrind-am check-valgrind-drd-am \ + check-valgrind-drd-local check-valgrind-helgrind-am \ + check-valgrind-helgrind-local check-valgrind-local \ + check-valgrind-memcheck-am check-valgrind-memcheck-local \ + check-valgrind-sgcheck-am check-valgrind-sgcheck-local clean \ + clean-generic clean-libtool clean-pyexecLTLIBRARIES \ + cscopelist-am ctags ctags-am distclean distclean-compile \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-orcusDATA \ + install-orcustoolsDATA install-pdf install-pdf-am install-ps \ + install-ps-am install-pyexecLTLIBRARIES install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + recheck tags tags-am uninstall uninstall-am \ + uninstall-orcusDATA uninstall-orcustoolsDATA \ + uninstall-pyexecLTLIBRARIES + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/python/cell.cpp b/src/python/cell.cpp new file mode 100644 index 0000000..a4a46b0 --- /dev/null +++ b/src/python/cell.cpp @@ -0,0 +1,349 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "cell.hpp" +#include "memory.hpp" +#include "global.hpp" +#include "formula_token.hpp" +#include "formula_tokens.hpp" +#include "orcus/spreadsheet/document.hpp" + +#include <ixion/cell.hpp> +#include <ixion/formula_result.hpp> +#include <ixion/formula.hpp> +#include <ixion/formula_name_resolver.hpp> +#include <ixion/model_context.hpp> + +#include <structmember.h> +#include <string> + +namespace ss = orcus::spreadsheet; + +namespace orcus { namespace python { + +namespace { + +/** non-python part of the object. */ +struct cell_data +{ + const ss::document* doc = nullptr; + const ixion::formula_cell* formula_cell = nullptr; + ixion::abs_address_t origin; +}; + +/** + * Python object for orcus.Cell. + */ +struct pyobj_cell +{ + PyObject_HEAD + + PyObject* type; + PyObject* value; + PyObject* formula; + + cell_data* data = nullptr; +}; + +void initialize_cell_members(pyobj_cell* self) +{ + Py_INCREF(Py_None); + self->value = Py_None; + + Py_INCREF(Py_None); + self->formula = Py_None; +} + +PyObject* create_and_init_cell_object(const char* type_name) +{ + PyTypeObject* cell_type = get_cell_type(); + if (!cell_type) + { + PyErr_SetString(PyExc_RuntimeError, "Failed to get the cell type object."); + return nullptr; + } + + PyObject* obj = cell_type->tp_new(cell_type, nullptr, nullptr); + if (!obj) + { + PyErr_SetString(PyExc_RuntimeError, "Failed to instantiate a cell object."); + return nullptr; + } + + pyobj_cell* self = reinterpret_cast<pyobj_cell*>(obj); + self->type = get_python_enum_value("CellType", type_name); + initialize_cell_members(self); + + return obj; +} + +void tp_dealloc(pyobj_cell* self) +{ + delete self->data; + self->data = nullptr; + + Py_CLEAR(self->type); + Py_CLEAR(self->value); + Py_CLEAR(self->formula); + + Py_TYPE(self)->tp_free(reinterpret_cast<PyObject*>(self)); +} + +PyObject* tp_new(PyTypeObject* type, PyObject* /*args*/, PyObject* /*kwargs*/) +{ + pyobj_cell* self = (pyobj_cell*)type->tp_alloc(type, 0); + self->data = new cell_data; + return reinterpret_cast<PyObject*>(self); +} + +int tp_init(pyobj_cell* self, PyObject* args, PyObject* kwargs) +{ + static const char* kwlist[] = { "type", nullptr }; + + self->type = nullptr; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O", const_cast<char**>(kwlist), &self->type)) + return -1; + + if (!self->type) + self->type = get_python_enum_value("CellType", "UNKNOWN"); + + initialize_cell_members(self); + return 0; +} + +PyObject* cell_get_formula_tokens(PyObject* self, PyObject* /*args*/, PyObject* /*kwargs*/) +{ + pyobj_cell* obj = reinterpret_cast<pyobj_cell*>(self); + cell_data& data = *obj->data; + if (!data.formula_cell) + { + // This is not a formula cell. + Py_INCREF(Py_None); + return Py_None; + } + + const ixion::formula_tokens_t& tokens = data.formula_cell->get_tokens()->get(); + return create_formula_tokens_iterator_object(*data.doc, data.origin, tokens); +} + +PyMethodDef tp_methods[] = +{ + { "get_formula_tokens", (PyCFunction)cell_get_formula_tokens, METH_NOARGS, "Get a formula tokens iterator." }, + { nullptr } +}; + +PyMemberDef tp_members[] = +{ + { (char*)"type", T_OBJECT_EX, offsetof(pyobj_cell, type), READONLY, (char*)"cell type" }, + { (char*)"value", T_OBJECT_EX, offsetof(pyobj_cell, value), READONLY, (char*)"cell value" }, + { (char*)"formula", T_OBJECT_EX, offsetof(pyobj_cell, formula), READONLY, (char*)"formula string" }, + { nullptr } +}; + +PyTypeObject cell_type = +{ + PyVarObject_HEAD_INIT(nullptr, 0) + "orcus.Cell", // tp_name + sizeof(pyobj_cell), // tp_basicsize + 0, // tp_itemsize + (destructor)tp_dealloc, // tp_dealloc + 0, // tp_print + 0, // tp_getattr + 0, // tp_setattr + 0, // tp_compare + 0, // tp_repr + 0, // tp_as_number + 0, // tp_as_sequence + 0, // tp_as_mapping + 0, // tp_hash + 0, // tp_call + 0, // tp_str + 0, // tp_getattro + 0, // tp_setattro + 0, // tp_as_buffer + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, // tp_flags + "orcus spreadsheet cell", // tp_doc + 0, // tp_traverse + 0, // tp_clear + 0, // tp_richcompare + 0, // tp_weaklistoffset + 0, // tp_iter + 0, // tp_iternext + tp_methods, // tp_methods + tp_members, // tp_members + 0, // tp_getset + 0, // tp_base + 0, // tp_dict + 0, // tp_descr_get + 0, // tp_descr_set + 0, // tp_dictoffset + (initproc)tp_init, // tp_init + 0, // tp_alloc + tp_new, // tp_new +}; + +} // anonymous namespace + +PyObject* create_cell_object_empty() +{ + PyObject* obj = create_and_init_cell_object("EMPTY"); + if (!obj) + return nullptr; + + return obj; +} + +PyObject* create_cell_object_boolean(bool v) +{ + PyObject* obj = create_and_init_cell_object("BOOLEAN"); + if (!obj) + return nullptr; + + pyobj_cell* obj_data = reinterpret_cast<pyobj_cell*>(obj); + + if (v) + { + Py_INCREF(Py_True); + obj_data->value = Py_True; + } + else + { + Py_INCREF(Py_False); + obj_data->value = Py_False; + } + + return obj; +} + +PyObject* create_cell_object_string(const std::string* p) +{ + PyObject* obj = create_and_init_cell_object("STRING"); + if (!obj) + return nullptr; + + pyobj_cell* obj_data = reinterpret_cast<pyobj_cell*>(obj); + + if (p) + { + obj_data->value = PyUnicode_FromStringAndSize(p->data(), p->size()); + if (!obj_data->value) + { + // The string contains invalid utf-8 sequence, and the function has + // already set a python exception which needs to be cleared. + PyErr_Clear(); + Py_XDECREF(obj); + obj = create_and_init_cell_object("STRING_WITH_ERROR"); + } + } + else + { + Py_INCREF(Py_None); + obj_data->value = Py_None; + } + + return obj; +} + +PyObject* create_cell_object_numeric(double v) +{ + PyObject* obj = create_and_init_cell_object("NUMERIC"); + if (!obj) + return nullptr; + + pyobj_cell* obj_data = reinterpret_cast<pyobj_cell*>(obj); + obj_data->value = PyFloat_FromDouble(v); + + return obj; +} + +PyObject* create_cell_object_formula( + const spreadsheet::document& doc, const ixion::abs_address_t& origin, const ixion::formula_cell* fc) +{ + if (!fc) + { + PyErr_SetString(PyExc_RuntimeError, "failed to find class orcus.CellType."); + return nullptr; + } + + const ixion::formula_tokens_t& tokens = fc->get_tokens()->get(); + bool is_error = !tokens.empty() && tokens[0].opcode == ixion::fop_error; + + PyObject* obj = create_and_init_cell_object(is_error ? "FORMULA_WITH_ERROR": "FORMULA"); + if (!obj) + return nullptr; + + pyobj_cell* obj_data = reinterpret_cast<pyobj_cell*>(obj); + obj_data->data->doc = &doc; + obj_data->data->origin = origin; + obj_data->data->formula_cell = fc; + + // Create formula expression string. + auto* resolver = doc.get_formula_name_resolver(spreadsheet::formula_ref_context_t::global); + const ixion::model_context& cxt = doc.get_model_context(); + std::string formula_s = ixion::print_formula_tokens(cxt, origin, *resolver, tokens); + obj_data->formula = PyUnicode_FromStringAndSize(formula_s.data(), formula_s.size()); + + ixion::formula_result res; + + try + { + res = fc->get_result_cache( + ixion::formula_result_wait_policy_t::throw_exception); + } + catch (const std::exception&) + { + Py_INCREF(Py_None); + obj_data->value = Py_None; + return obj; + } + + switch (res.get_type()) + { + case ixion::formula_result::result_type::value: + { + obj_data->value = PyFloat_FromDouble(res.get_value()); + break; + } + case ixion::formula_result::result_type::string: + { + const std::string& s = res.get_string(); + obj_data->value = PyUnicode_FromStringAndSize(s.data(), s.size()); + break; + } + case ixion::formula_result::result_type::error: + { + ixion::formula_error_t fe = res.get_error(); + std::string_view fename = ixion::get_formula_error_name(fe); + if (!fename.empty()) + obj_data->value = PyUnicode_FromStringAndSize(fename.data(), fename.size()); + else + { + // This should not be hit, but just in case... + Py_INCREF(Py_None); + obj_data->value = Py_None; + } + break; + } + default: + { + // This should not be hit, but just in case... + Py_INCREF(Py_None); + obj_data->value = Py_None; + } + } + + return obj; +} + +PyTypeObject* get_cell_type() +{ + return &cell_type; +} + +}} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/src/python/cell.hpp b/src/python/cell.hpp new file mode 100644 index 0000000..45ea46d --- /dev/null +++ b/src/python/cell.hpp @@ -0,0 +1,44 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef INCLUDED_ORCUS_PYTHON_CELL_HPP +#define INCLUDED_ORCUS_PYTHON_CELL_HPP + +#include <string> +#include <Python.h> + +namespace ixion { + +class formula_cell; +struct abs_address_t; + +} + +namespace orcus { + +namespace spreadsheet { + +class document; + +} + +namespace python { + +PyObject* create_cell_object_empty(); +PyObject* create_cell_object_boolean(bool v); +PyObject* create_cell_object_string(const std::string* p); +PyObject* create_cell_object_numeric(double v); +PyObject* create_cell_object_formula( + const spreadsheet::document& doc, const ixion::abs_address_t& origin, const ixion::formula_cell* fc); + +PyTypeObject* get_cell_type(); + +}} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/src/python/csv.cpp b/src/python/csv.cpp new file mode 100644 index 0000000..97c5d29 --- /dev/null +++ b/src/python/csv.cpp @@ -0,0 +1,105 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "csv.hpp" +#include "global.hpp" + +#ifdef __ORCUS_PYTHON_CSV +#include "document.hpp" +#include "orcus/orcus_csv.hpp" +#include "orcus/spreadsheet/document.hpp" +#include "orcus/spreadsheet/factory.hpp" +#endif + +namespace orcus { namespace python { + +#ifdef __ORCUS_PYTHON_CSV + +namespace { + +py_unique_ptr read_stream_object_from_string(PyObject* args, PyObject* kwargs) +{ + static const char* kwlist[] = { "stream", nullptr }; + + py_unique_ptr ret; + PyObject* file = nullptr; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", const_cast<char**>(kwlist), &file)) + return ret; + + if (!file) + { + PyErr_SetString(PyExc_RuntimeError, "Invalid file object has been passed."); + return ret; + } + + PyObject* obj_str = nullptr; + + if (PyObject_HasAttrString(file, "read")) + { + PyObject* func_read = PyObject_GetAttrString(file, "read"); // new reference + obj_str = PyObject_CallFunction(func_read, nullptr); + Py_XDECREF(func_read); + } + + if (!obj_str) + { + if (PyObject_TypeCheck(file, &PyUnicode_Type)) + obj_str = PyUnicode_FromObject(file); // new reference + } + + if (!obj_str) + { + PyErr_SetString(PyExc_RuntimeError, "failed to extract bytes from this object."); + return ret; + } + + ret.reset(obj_str); + return ret; +} + +} // anonymous namespace + +PyObject* csv_read(PyObject* /*module*/, PyObject* args, PyObject* kwargs) +{ + py_unique_ptr str = read_stream_object_from_string(args, kwargs); + if (!str) + return nullptr; + + try + { + spreadsheet::range_size_t ss{1048576, 16384}; + std::unique_ptr<spreadsheet::document> doc = std::make_unique<spreadsheet::document>(ss); + spreadsheet::import_factory fact(*doc); + orcus_csv app(&fact); + + Py_ssize_t n = 0; + const char* p = PyUnicode_AsUTF8AndSize(str.get(), &n); + app.read_stream({p, static_cast<std::string_view::size_type>(n)}); + + return create_document(std::move(doc)); + } + catch (const std::exception& e) + { + set_python_exception(PyExc_RuntimeError, e); + return nullptr; + } +} + +#else + +PyObject* csv_read(PyObject*, PyObject*, PyObject*) +{ + PyErr_SetString(PyExc_RuntimeError, "The csv module is not enabled."); + return nullptr; +} + +#endif + +}} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/src/python/csv.hpp b/src/python/csv.hpp new file mode 100644 index 0000000..e9b7b44 --- /dev/null +++ b/src/python/csv.hpp @@ -0,0 +1,21 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef INCLUDED_ORCUS_PYTHON_CSV_HPP +#define INCLUDED_ORCUS_PYTHON_CSV_HPP + +#include <Python.h> + +namespace orcus { namespace python { + +PyObject* csv_read(PyObject* module, PyObject* args, PyObject* kwargs); + +}} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/src/python/document.cpp b/src/python/document.cpp new file mode 100644 index 0000000..d908668 --- /dev/null +++ b/src/python/document.cpp @@ -0,0 +1,330 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "document.hpp" +#include "sheet.hpp" +#include "global.hpp" +#include "named_expression.hpp" +#include "named_expressions.hpp" + +#include <ixion/model_context.hpp> +#include <ixion/named_expressions_iterator.hpp> +#include <structmember.h> +#include <object.h> +#include <sstream> + +using namespace std; +namespace ss = orcus::spreadsheet; + +namespace orcus { namespace python { + +document_data::~document_data() +{ +} + +namespace { + +/** + * Python object for orcus.Document. + */ +struct pyobj_document +{ + PyObject_HEAD + + PyObject* sheets; // tuple of sheet objects. + + document_data* data; +}; + +inline pyobj_document* t(PyObject* self) +{ + return reinterpret_cast<pyobj_document*>(self); +} + +void tp_dealloc(pyobj_document* self) +{ + delete self->data; + + // Destroy all sheet objects. + Py_ssize_t n = PyTuple_Size(self->sheets); + for (Py_ssize_t i = 0; i < n; ++i) + { + PyObject* o = PyTuple_GetItem(self->sheets, i); + Py_CLEAR(o); + } + Py_CLEAR(self->sheets); // and the tuple containing the sheets. + + Py_TYPE(self)->tp_free(reinterpret_cast<PyObject*>(self)); +} + +PyObject* tp_new(PyTypeObject* type, PyObject* /*args*/, PyObject* /*kwargs*/) +{ + pyobj_document* self = t(type->tp_alloc(type, 0)); + self->data = new document_data; + return reinterpret_cast<PyObject*>(self); +} + +int tp_init(pyobj_document* /*self*/, PyObject* /*args*/, PyObject* /*kwargs*/) +{ + return 0; +} + +PyObject* doc_get_named_expressions(PyObject* self, PyObject* /*args*/, PyObject* /*kwargs*/) +{ + const ss::document& doc = *t(self)->data->m_doc; + const ixion::model_context& cxt = doc.get_model_context(); + return create_named_expressions_object(-1, doc, cxt.get_named_expressions_iterator()); +} + +PyMethodDef tp_methods[] = +{ + { "get_named_expressions", (PyCFunction)doc_get_named_expressions, METH_NOARGS, "Get a named expressions iterator." }, + { nullptr } +}; + +PyMemberDef tp_members[] = +{ + { (char*)"sheets", T_OBJECT_EX, offsetof(pyobj_document, sheets), READONLY, (char*)"sheet objects" }, + { nullptr } +}; + +PyTypeObject document_type = +{ + PyVarObject_HEAD_INIT(nullptr, 0) + "orcus.Document", // tp_name + sizeof(pyobj_document), // tp_basicsize + 0, // tp_itemsize + (destructor)tp_dealloc, // tp_dealloc + 0, // tp_print + 0, // tp_getattr + 0, // tp_setattr + 0, // tp_compare + 0, // tp_repr + 0, // tp_as_number + 0, // tp_as_sequence + 0, // tp_as_mapping + 0, // tp_hash + 0, // tp_call + 0, // tp_str + 0, // tp_getattro + 0, // tp_setattro + 0, // tp_as_buffer + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, // tp_flags + "orcus document object", // tp_doc + 0, // tp_traverse + 0, // tp_clear + 0, // tp_richcompare + 0, // tp_weaklistoffset + 0, // tp_iter + 0, // tp_iternext + tp_methods, // tp_methods + tp_members, // tp_members + 0, // tp_getset + 0, // tp_base + 0, // tp_dict + 0, // tp_descr_get + 0, // tp_descr_set + 0, // tp_dictoffset + (initproc)tp_init, // tp_init + 0, // tp_alloc + tp_new, // tp_new +}; + +bool import_from_stream_object(iface::import_filter& app, PyObject* obj_bytes) +{ + const char* p = PyBytes_AsString(obj_bytes); + if (!p) + return false; + + size_t n = PyBytes_Size(obj_bytes); + + app.read_stream({p, n}); + + return true; +} + +PyObject* create_document_object() +{ + PyTypeObject* type = get_document_type(); + + PyObject* obj_doc = create_object_from_type(type); + if (!obj_doc) + return nullptr; + + type->tp_init(obj_doc, nullptr, nullptr); + + return obj_doc; +} + +void store_document(PyObject* self, std::unique_ptr<spreadsheet::document>&& doc) +{ + if (!self) + return; + + pyobj_document* pydoc = reinterpret_cast<pyobj_document*>(self); + document_data* pydoc_data = pydoc->data; + pydoc_data->m_doc = std::move(doc); + + PyTypeObject* sheet_type = get_sheet_type(); + if (!sheet_type) + return; + + // Create a tuple of sheet objects and store it with the pydoc instance. + size_t sheet_size = pydoc_data->m_doc->get_sheet_count(); + + pydoc->sheets = PyTuple_New(sheet_size); + + for (size_t i = 0; i < sheet_size; ++i) + { + spreadsheet::sheet* sheet = pydoc_data->m_doc->get_sheet(i); + if (!sheet) + continue; + + PyObject* pysheet = sheet_type->tp_new(sheet_type, nullptr, nullptr); + if (!pysheet) + continue; + + sheet_type->tp_init(pysheet, nullptr, nullptr); + + Py_INCREF(pysheet); + PyTuple_SetItem(pydoc->sheets, i, pysheet); + + store_sheet(pysheet, pydoc_data->m_doc.get(), sheet); + } +} + +} // anonoymous namespace + +PyTypeObject* get_document_type() +{ + return &document_type; +} + +document_data* get_document_data(PyObject* self) +{ + return reinterpret_cast<pyobj_document*>(self)->data; +} + +stream_with_formulas read_stream_and_formula_params_from_args(PyObject* args, PyObject* kwargs) +{ + static const char* kwlist[] = { "stream", "recalc", "error_policy", nullptr }; + + stream_with_formulas ret; + PyObject* file = nullptr; + int recalc_formula_cells = 0; + const char* error_policy_s = nullptr; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|ps", const_cast<char**>(kwlist), &file, &recalc_formula_cells, &error_policy_s)) + return ret; + + if (!file) + { + PyErr_SetString(PyExc_RuntimeError, "Invalid file object has been passed."); + return ret; + } + + PyObject* obj_bytes = nullptr; + + if (PyObject_HasAttrString(file, "read")) + { + PyObject* func_read = PyObject_GetAttrString(file, "read"); // new reference + obj_bytes = PyObject_CallFunction(func_read, nullptr); + Py_XDECREF(func_read); + } + + if (!obj_bytes) + { + if (PyObject_TypeCheck(file, &PyBytes_Type)) + obj_bytes = PyBytes_FromObject(file); + } + + if (!obj_bytes) + { + PyErr_SetString(PyExc_RuntimeError, "failed to extract bytes from this object."); + return ret; + } + + if (error_policy_s) + { + ss::formula_error_policy_t error_policy = ss::to_formula_error_policy(error_policy_s); + if (error_policy == ss::formula_error_policy_t::unknown) + { + std::ostringstream os; + os << "invalid error policy value: '" << error_policy_s << "'. The value must be either 'fail' or 'skip'."; + PyErr_SetString(PyExc_RuntimeError, os.str().data()); + return ret; + } + + ret.error_policy = error_policy; + } + + ret.stream.reset(obj_bytes); + ret.recalc_formula_cells = recalc_formula_cells != 0; + + return ret; +} + +py_unique_ptr read_stream_from_args(PyObject* args, PyObject* kwargs) +{ + static const char* kwlist[] = { "stream", nullptr }; + + py_unique_ptr obj_bytes; + PyObject* file = nullptr; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", const_cast<char**>(kwlist), &file)) + return obj_bytes; + + if (!file) + { + PyErr_SetString(PyExc_RuntimeError, "Invalid file object has been passed."); + return obj_bytes; + } + + if (PyObject_HasAttrString(file, "read")) + { + PyObject* func_read = PyObject_GetAttrString(file, "read"); // new reference + obj_bytes.reset(PyObject_CallFunction(func_read, nullptr)); + Py_XDECREF(func_read); + } + + if (!obj_bytes) + { + if (PyObject_TypeCheck(file, &PyBytes_Type)) + obj_bytes.reset(PyBytes_FromObject(file)); + } + + if (!obj_bytes) + { + PyErr_SetString(PyExc_RuntimeError, "failed to extract bytes from this object."); + return obj_bytes; + } + + return obj_bytes; +} + +PyObject* import_from_stream_into_document( + PyObject* obj_bytes, iface::import_filter& app, std::unique_ptr<spreadsheet::document>&& doc) +{ + if (!import_from_stream_object(app, obj_bytes)) + return nullptr; + + return create_document(std::move(doc)); +} + +PyObject* create_document(std::unique_ptr<spreadsheet::document>&& doc) +{ + PyObject* obj_doc = create_document_object(); + if (!obj_doc) + return nullptr; + + store_document(obj_doc, std::move(doc)); + return obj_doc; +} + +}} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/src/python/document.hpp b/src/python/document.hpp new file mode 100644 index 0000000..b52c9b8 --- /dev/null +++ b/src/python/document.hpp @@ -0,0 +1,83 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef INCLUDED_ORCUS_PYTHON_DOCUMENT_HPP +#define INCLUDED_ORCUS_PYTHON_DOCUMENT_HPP + +#include "orcus/spreadsheet/document.hpp" + +#include "memory.hpp" + +namespace orcus { namespace python { + +/** non-python part of the document object. */ +struct document_data +{ + std::unique_ptr<spreadsheet::document> m_doc; + + ~document_data(); +}; + +document_data* get_document_data(PyObject* self); + +struct stream_with_formulas +{ + py_unique_ptr stream; + bool recalc_formula_cells = false; + spreadsheet::formula_error_policy_t error_policy = spreadsheet::formula_error_policy_t::fail; +}; + +/** + * Extract a python object representing the byte stream from the arguments + * passed to the python orcus.<file format>.read() function, as well as + * several parameters related to formula calculation settings. + * + * This function handles the following python arguments: stream, recalc, and + * error_policy. + * + * @param args positional argument object. + * @param kwargs keyword argument object. + * + * @return object representing the bytes as well as formula calculation + * settings. + */ +stream_with_formulas read_stream_and_formula_params_from_args(PyObject* args, PyObject* kwargs); + +/** + * This one is similar to the function above, except that it only handles + * one argument called 'stream'. + * + * @return object representing the bytes. + */ +py_unique_ptr read_stream_from_args(PyObject* args, PyObject* kwargs); + +/** + * Import a document from a python object containing the byte stream, and + * create a python object of class orcus.Document. + * + * @param obj_bytes python object containing the byte stream. + * @param app filter instance to use to load the document. + * @param doc orcus document instance which will be stored within the python + * document object. + * + * @return python document object. + */ +PyObject* import_from_stream_into_document( + PyObject* obj_bytes, iface::import_filter& app, std::unique_ptr<spreadsheet::document>&& doc); + +PyObject* create_document(std::unique_ptr<spreadsheet::document>&& doc); + +/** + * Get the definition of the python class Document. + */ +PyTypeObject* get_document_type(); + +}} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/src/python/formula_token.cpp b/src/python/formula_token.cpp new file mode 100644 index 0000000..971d440 --- /dev/null +++ b/src/python/formula_token.cpp @@ -0,0 +1,250 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "formula_token.hpp" +#include "global.hpp" +#include "orcus/spreadsheet/document.hpp" + +#include <ixion/formula.hpp> +#include <ixion/formula_name_resolver.hpp> +#include <ixion/model_context.hpp> +#include <structmember.h> + +namespace ss = orcus::spreadsheet; + +namespace orcus { namespace python { + +namespace { + +/** non-python part of the object's internal. */ +struct data_formula_token +{ + std::string repr; +}; + +/** + * Python object for orcus.NamedExpression. + */ +struct pyobj_formula_token +{ + PyObject_HEAD + + PyObject* type; + PyObject* op; + + data_formula_token* data; +}; + +const char* to_formula_token_type(ixion::fopcode_t op) +{ + switch (op) + { + case ixion::fop_single_ref: + case ixion::fop_range_ref: + case ixion::fop_table_ref: + return "REFERENCE"; + case ixion::fop_named_expression: + return "NAME"; + case ixion::fop_function: + return "FUNCTION"; + case ixion::fop_string: + case ixion::fop_value: + return "VALUE"; + case ixion::fop_plus: + case ixion::fop_minus: + case ixion::fop_divide: + case ixion::fop_multiply: + case ixion::fop_exponent: + case ixion::fop_concat: + case ixion::fop_equal: + case ixion::fop_not_equal: + case ixion::fop_less: + case ixion::fop_greater: + case ixion::fop_less_equal: + case ixion::fop_greater_equal: + case ixion::fop_open: + case ixion::fop_close: + case ixion::fop_sep: + return "OPERATOR"; + case ixion::fop_error: + return "ERROR"; + case ixion::fop_unknown: + default: + ; + } + + return "UNKNOWN"; +} + +const char* to_formula_token_op(ixion::fopcode_t op) +{ + const char* names[] = { + "UNKNOWN", + "SINGLE_REF", + "RANGE_REF", + "TABLE_REF", + "NAMED_EXPRESSION", + "STRING", + "VALUE", + "FUNCTION", + "PLUS", + "MINUS", + "DIVIDE", + "MULTIPLY", + "EXPONENT", + "CONCAT", + "EQUAL", + "NOT_EQUAL", + "LESS", + "GREATER", + "LESS_EQUAL", + "GREATER_EQUAL", + "OPEN", + "CLOSE", + "SEP", + "ERROR", + }; + + auto n_names = std::size(names); + return op < n_names ? names[op] : names[0]; +} + +void init_members(pyobj_formula_token* self) +{ + Py_INCREF(Py_None); + self->type = Py_None; + Py_INCREF(Py_None); + self->op = Py_None; +} + +PyObject* create_and_init_formula_token_object(ixion::fopcode_t op, std::string repr) +{ + PyTypeObject* ft_type = get_formula_token_type(); + if (!ft_type) + { + PyErr_SetString(PyExc_RuntimeError, "Failed to get the formula token type object."); + return nullptr; + } + + PyObject* obj = ft_type->tp_new(ft_type, nullptr, nullptr); + if (!obj) + { + PyErr_SetString(PyExc_RuntimeError, "Failed to instantiate a formula token object."); + return nullptr; + } + + pyobj_formula_token* self = reinterpret_cast<pyobj_formula_token*>(obj); + init_members(self); + self->type = get_python_enum_value("FormulaTokenType", to_formula_token_type(op)); + self->op = get_python_enum_value("FormulaTokenOp", to_formula_token_op(op)); + self->data->repr = std::move(repr); + + return obj; +} + +void tp_dealloc(pyobj_formula_token* self) +{ + delete self->data; + self->data = nullptr; + + Py_CLEAR(self->op); + Py_CLEAR(self->type); + + Py_TYPE(self)->tp_free(reinterpret_cast<PyObject*>(self)); +} + +int tp_init(pyobj_formula_token* self, PyObject* /*args*/, PyObject* /*kwargs*/) +{ + init_members(self); + return 0; +} + +PyObject* tp_new(PyTypeObject* type, PyObject* /*args*/, PyObject* /*kwargs*/) +{ + pyobj_formula_token* self = (pyobj_formula_token*)type->tp_alloc(type, 0); + self->data = new data_formula_token; + return reinterpret_cast<PyObject*>(self); +} + +PyObject* tp_repr(pyobj_formula_token* self) +{ + return PyUnicode_FromStringAndSize(self->data->repr.data(), self->data->repr.size()); +} + +PyMemberDef tp_members[] = +{ + { (char*)"type", T_OBJECT_EX, offsetof(pyobj_formula_token, type), READONLY, (char*)"formula token type" }, + { (char*)"op", T_OBJECT_EX, offsetof(pyobj_formula_token, op), READONLY, (char*)"formula token operator" }, + { nullptr } +}; + +PyTypeObject formula_token_type = +{ + PyVarObject_HEAD_INIT(nullptr, 0) + "orcus.FormulaToken", // tp_name + sizeof(pyobj_formula_token), // tp_basicsize + 0, // tp_itemsize + (destructor)tp_dealloc, // tp_dealloc + 0, // tp_print + 0, // tp_getattr + 0, // tp_setattr + 0, // tp_compare + (reprfunc)tp_repr, // tp_repr + 0, // tp_as_number + 0, // tp_as_sequence + 0, // tp_as_mapping + 0, // tp_hash + 0, // tp_call + 0, // tp_str + 0, // tp_getattro + 0, // tp_setattro + 0, // tp_as_buffer + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, // tp_flags + "orcus spreadsheet formula token", // tp_doc + 0, // tp_traverse + 0, // tp_clear + 0, // tp_richcompare + 0, // tp_weaklistoffset + 0, // tp_iter + 0, // tp_iternext + 0, // tp_methods + tp_members, // tp_members + 0, // tp_getset + 0, // tp_base + 0, // tp_dict + 0, // tp_descr_get + 0, // tp_descr_set + 0, // tp_dictoffset + (initproc)tp_init, // tp_init + 0, // tp_alloc + tp_new, // tp_new +}; + +} // anonymous namespace + +PyObject* create_formula_token_object(const ss::document& doc, const ixion::abs_address_t& pos, const ixion::formula_token& token) +{ + const ixion::model_context& cxt = doc.get_model_context(); + auto* resolver = doc.get_formula_name_resolver(ss::formula_ref_context_t::global); + assert(resolver); + std::string ft_s = ixion::print_formula_token(cxt, pos, *resolver, token); + + PyObject* obj = create_and_init_formula_token_object(token.opcode, std::move(ft_s)); + if (!obj) + return nullptr; + + return obj; +} + +PyTypeObject* get_formula_token_type() +{ + return &formula_token_type; +} + +}} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/src/python/formula_token.hpp b/src/python/formula_token.hpp new file mode 100644 index 0000000..90b5b49 --- /dev/null +++ b/src/python/formula_token.hpp @@ -0,0 +1,40 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef INCLUDED_ORCUS_PYTHON_FORMULA_TOKEN_HPP +#define INCLUDED_ORCUS_PYTHON_FORMULA_TOKEN_HPP + +#include "orcus/spreadsheet/types.hpp" + +#include <Python.h> + +namespace ixion { + +struct abs_address_t; +class formula_token; + +} + +namespace orcus { + +namespace spreadsheet { + +class document; + +} + +namespace python { + +PyObject* create_formula_token_object(const spreadsheet::document& doc, const ixion::abs_address_t& pos, const ixion::formula_token& token); + +PyTypeObject* get_formula_token_type(); + +}} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/src/python/formula_tokens.cpp b/src/python/formula_tokens.cpp new file mode 100644 index 0000000..1c8b77a --- /dev/null +++ b/src/python/formula_tokens.cpp @@ -0,0 +1,200 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "formula_tokens.hpp" +#include "formula_token.hpp" +#include "orcus/spreadsheet/document.hpp" + +#include <ixion/formula_tokens.hpp> +#include <structmember.h> + +namespace ss = orcus::spreadsheet; + +namespace orcus { namespace python { + +namespace { + +/** non-python part. */ +struct formula_tokens_data +{ + const ss::document* doc; + ixion::abs_address_t origin; + const ixion::formula_tokens_t* tokens = nullptr; + ixion::formula_tokens_t::const_iterator pos; + ixion::formula_tokens_t::const_iterator end; +}; + +/** python object */ +struct pyobj_formula_tokens +{ + PyObject_HEAD + + formula_tokens_data* data = nullptr; +}; + +inline pyobj_formula_tokens* t(PyObject* self) +{ + return reinterpret_cast<pyobj_formula_tokens*>(self); +} + +void init_members( + pyobj_formula_tokens* self, const ss::document& doc, const ixion::abs_address_t& origin, const ixion::formula_tokens_t& tokens) +{ + assert(self->data); + self->data->doc = &doc; + self->data->origin = origin; + self->data->tokens = &tokens; +} + +void tp_dealloc(pyobj_formula_tokens* self) +{ + delete self->data; + Py_TYPE(self)->tp_free(reinterpret_cast<PyObject*>(self)); +} + +int tp_init(pyobj_formula_tokens* /*self*/, PyObject* /*args*/, PyObject* /*kwargs*/) +{ + return 0; +} + +PyObject* tp_new(PyTypeObject* type, PyObject* /*args*/, PyObject* /*kwargs*/) +{ + pyobj_formula_tokens* self = t(type->tp_alloc(type, 0)); + self->data = new formula_tokens_data; + return reinterpret_cast<PyObject*>(self); +} + +PyObject* tp_iter(PyObject* self) +{ + formula_tokens_data& data = *t(self)->data; + data.pos = data.tokens->cbegin(); + data.end = data.tokens->cend(); + + Py_INCREF(self); + return self; +} + +PyObject* tp_iternext(PyObject* self) +{ + formula_tokens_data& data = *t(self)->data; + + if (data.pos == data.end) + { + // No more elements. Stop the iteration. + PyErr_SetNone(PyExc_StopIteration); + return nullptr; + } + + PyObject* ft_obj = create_formula_token_object(*data.doc, data.origin, *data.pos); + ++data.pos; + return ft_obj; +} + +Py_ssize_t sq_length(PyObject* self) +{ + formula_tokens_data& data = *t(self)->data; + return data.tokens->size(); +} + +PySequenceMethods tp_as_sequence = +{ + sq_length, // lenfunc sq_length + 0, // binaryfunc sq_concat + 0, // ssizeargfunc sq_repeat + 0, // ssizeargfunc sq_item + 0, // void *was_sq_slice + 0, // ssizeobjargproc sq_ass_item + 0, // void *was_sq_ass_slice + 0, // objobjproc sq_contains + 0, // binaryfunc sq_inplace_concat + 0, // ssizeargfunc sq_inplace_repeat +}; + +PyMethodDef tp_methods[] = +{ + { nullptr } +}; + +PyMemberDef tp_members[] = +{ + { nullptr } +}; + +PyTypeObject formula_tokens_type = +{ + PyVarObject_HEAD_INIT(nullptr, 0) + "orcus.FormulaTokens", // tp_name + sizeof(pyobj_formula_tokens), // tp_basicsize + 0, // tp_itemsize + (destructor)tp_dealloc, // tp_dealloc + 0, // tp_print + 0, // tp_getattr + 0, // tp_setattr + 0, // tp_compare + 0, // tp_repr + 0, // tp_as_number + &tp_as_sequence, // tp_as_sequence + 0, // tp_as_mapping + 0, // tp_hash + 0, // tp_call + 0, // tp_str + 0, // tp_getattro + 0, // tp_setattro + 0, // tp_as_buffer + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, // tp_flags + "orcus spreadsheet formula tokens", // tp_doc + 0, // tp_traverse + 0, // tp_clear + 0, // tp_richcompare + 0, // tp_weaklistoffset + tp_iter, // tp_iter + tp_iternext, // tp_iternext + tp_methods, // tp_methods + tp_members, // tp_members + 0, // tp_getset + 0, // tp_base + 0, // tp_dict + 0, // tp_descr_get + 0, // tp_descr_set + 0, // tp_dictoffset + (initproc)tp_init, // tp_init + 0, // tp_alloc + tp_new, // tp_new +}; + +} // anonymous namespace + +PyObject* create_formula_tokens_iterator_object( + const ss::document& doc, const ixion::abs_address_t& origin, const ixion::formula_tokens_t& tokens) +{ + PyTypeObject* ft_type = get_formula_tokens_type(); + if (!ft_type) + { + PyErr_SetString(PyExc_RuntimeError, "Failed to get the formula tokens type object."); + return nullptr; + } + + PyObject* obj = ft_type->tp_new(ft_type, nullptr, nullptr); + if (!obj) + { + PyErr_SetString(PyExc_RuntimeError, "Failed to instantiate a formula tokens object."); + return nullptr; + } + + init_members(t(obj), doc, origin, tokens); + + return obj; +} + +PyTypeObject* get_formula_tokens_type() +{ + return &formula_tokens_type; +} + +}} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/src/python/formula_tokens.hpp b/src/python/formula_tokens.hpp new file mode 100644 index 0000000..4067452 --- /dev/null +++ b/src/python/formula_tokens.hpp @@ -0,0 +1,39 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef INCLUDED_ORCUS_PYTHON_FORMULA_TOKENS_HPP +#define INCLUDED_ORCUS_PYTHON_FORMULA_TOKENS_HPP + +#include <ixion/formula_tokens_fwd.hpp> +#include <Python.h> + +namespace ixion { + +struct abs_address_t; + +} + +namespace orcus { + +namespace spreadsheet { + +class document; + +} + +namespace python { + +PyObject* create_formula_tokens_iterator_object( + const spreadsheet::document& doc, const ixion::abs_address_t& origin, const ixion::formula_tokens_t& tokens); + +PyTypeObject* get_formula_tokens_type(); + +}} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/src/python/global.cpp b/src/python/global.cpp new file mode 100644 index 0000000..f8623e0 --- /dev/null +++ b/src/python/global.cpp @@ -0,0 +1,65 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "global.hpp" +#include "memory.hpp" + +#include <sstream> + +namespace orcus { namespace python { + +void set_python_exception(PyObject* type, const std::exception& e) +{ + std::ostringstream os; + os << "C++ exception caught: " << e.what(); + PyErr_SetString(type, os.str().data()); +} + +PyObject* get_python_enum_value(const char* enum_class_name, const char* value_name) +{ + py_scoped_ref orcus_mod = PyImport_ImportModule("orcus"); + if (!orcus_mod) + { + PyErr_SetString(PyExc_RuntimeError, "failed to import orcus module."); + return nullptr; + } + + py_scoped_ref cls = PyObject_GetAttrString(orcus_mod.get(), enum_class_name); + if (!cls) + { + std::ostringstream os; + os << "failed to find class orcus." << enum_class_name << "."; + PyErr_SetString(PyExc_RuntimeError, os.str().data()); + return nullptr; + } + + return PyObject_GetAttrString(cls.get(), value_name); +} + +PyObject* create_object_from_type(PyTypeObject* type) +{ + if (!type) + { + PyErr_SetString(PyExc_RuntimeError, "Type object is null."); + return nullptr; + } + + PyObject* obj = type->tp_new(type, nullptr, nullptr); + if (!obj) + { + std::ostringstream os; + os << "Failed to instantiate an object of type " << type->tp_name << "."; + PyErr_SetString(PyExc_RuntimeError, os.str().data()); + return nullptr; + } + + return obj; +} + +}} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/src/python/global.hpp b/src/python/global.hpp new file mode 100644 index 0000000..608a7ed --- /dev/null +++ b/src/python/global.hpp @@ -0,0 +1,26 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef INCLUDED_ORCUS_PYTHON_GLOBAL_HPP +#define INCLUDED_ORCUS_PYTHON_GLOBAL_HPP + +#include <exception> +#include <Python.h> + +namespace orcus { namespace python { + +void set_python_exception(PyObject* type, const std::exception& e); + +PyObject* get_python_enum_value(const char* enum_class_name, const char* value_name); + +PyObject* create_object_from_type(PyTypeObject* type); + +}} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/src/python/gnumeric.cpp b/src/python/gnumeric.cpp new file mode 100644 index 0000000..162f18f --- /dev/null +++ b/src/python/gnumeric.cpp @@ -0,0 +1,58 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "gnumeric.hpp" +#include "global.hpp" + +#ifdef __ORCUS_PYTHON_GNUMERIC +#include "document.hpp" +#include "orcus/orcus_gnumeric.hpp" +#include "orcus/spreadsheet/document.hpp" +#include "orcus/spreadsheet/factory.hpp" +#endif + +namespace orcus { namespace python { + +#ifdef __ORCUS_PYTHON_GNUMERIC + +PyObject* gnumeric_read(PyObject* /*module*/, PyObject* args, PyObject* kwargs) +{ + stream_with_formulas data = read_stream_and_formula_params_from_args(args, kwargs); + if (!data.stream) + return nullptr; + + try + { + spreadsheet::range_size_t ss{1048576, 16384}; + std::unique_ptr<spreadsheet::document> doc = std::make_unique<spreadsheet::document>(ss); + spreadsheet::import_factory fact(*doc); + fact.set_recalc_formula_cells(data.recalc_formula_cells); + fact.set_formula_error_policy(data.error_policy); + orcus_gnumeric app(&fact); + + return import_from_stream_into_document(data.stream.get(), app, std::move(doc)); + } + catch (const std::exception& e) + { + set_python_exception(PyExc_RuntimeError, e); + return nullptr; + } +} + +#else + +PyObject* gnumeric_read(PyObject*, PyObject*, PyObject*) +{ + PyErr_SetString(PyExc_RuntimeError, "The gnumeric module is not enabled."); + return nullptr; +} + +#endif + +}} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/src/python/gnumeric.hpp b/src/python/gnumeric.hpp new file mode 100644 index 0000000..23aeec8 --- /dev/null +++ b/src/python/gnumeric.hpp @@ -0,0 +1,21 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef INCLUDED_ORCUS_PYTHON_GNUMERIC_HPP +#define INCLUDED_ORCUS_PYTHON_GNUMERIC_HPP + +#include <Python.h> + +namespace orcus { namespace python { + +PyObject* gnumeric_read(PyObject* module, PyObject* args, PyObject* kwargs); + +}} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/src/python/json.cpp b/src/python/json.cpp new file mode 100644 index 0000000..873523b --- /dev/null +++ b/src/python/json.cpp @@ -0,0 +1,290 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "orcus/env.hpp" +#include "orcus/json_parser.hpp" +#include "orcus/json_document_tree.hpp" +#include "orcus/config.hpp" + +#include <algorithm> +#include <sstream> +#include <boost/current_function.hpp> + +#include <Python.h> + +#define GETSTATE(m) ((struct module_state*)PyModule_GetState(m)) + +using namespace std; + +namespace orcus { namespace python { + +namespace { + +class python_json_error : public general_error +{ +public: + python_json_error(const std::string& msg) : general_error("python_json_error", msg) {} +}; + +struct module_state +{ + PyObject* error; +}; + +int orcus_traverse(PyObject* m, visitproc visit, void* arg) +{ + Py_VISIT(GETSTATE(m)->error); + return 0; +} + +int orcus_clear(PyObject* m) +{ + Py_CLEAR(GETSTATE(m)->error); + return 0; +} + +struct parser_stack +{ + PyObject* key; + PyObject* node; + json::node_t type; + + parser_stack(PyObject* _node, json::node_t _type) : key(nullptr), node(_node), type(_type) {} +}; + +class json_parser_handler +{ + PyObject* m_root; + std::vector<parser_stack> m_stack; + + PyObject* push_value(PyObject* value) + { + if (!value) + { + std::ostringstream os; + os << BOOST_CURRENT_FUNCTION << ": Empty value is passed."; + throw python_json_error(os.str()); + } + + if (m_stack.empty()) + { + std::ostringstream os; + os << BOOST_CURRENT_FUNCTION << ": Stack is unexpectedly empty."; + throw python_json_error(os.str()); + } + + parser_stack& cur = m_stack.back(); + + switch (cur.type) + { + case json::node_t::array: + { + PyList_Append(cur.node, value); + return value; + } + break; + case json::node_t::object: + { + assert(cur.key); + PyDict_SetItem(cur.node, cur.key, value); + cur.key = nullptr; + return value; + } + break; + default: + Py_DECREF(value); + } + + std::ostringstream os; + os << BOOST_CURRENT_FUNCTION << ": unstackable JSON value type."; + throw python_json_error(os.str()); + } + +public: + json_parser_handler() : m_root(nullptr) {} + + ~json_parser_handler() + { + if (m_root) + Py_XDECREF(m_root); + + std::for_each(m_stack.begin(), m_stack.end(), + [](parser_stack& ps) + { + if (ps.key) + { + Py_XDECREF(ps.key); + ps.key = nullptr; + } + } + ); + } + + void begin_parse() + { + if (m_root) + { + std::ostringstream os; + os << BOOST_CURRENT_FUNCTION << ": Root JSON value already exists."; + throw python_json_error(os.str()); + } + } + + void end_parse() {} + + void begin_array() + { + if (m_root) + { + PyObject* array = push_value(PyList_New(0)); + m_stack.push_back(parser_stack(array, json::node_t::array)); + } + else + { + m_root = PyList_New(0); + m_stack.push_back(parser_stack(m_root, json::node_t::array)); + } + } + + void end_array() + { + if (m_stack.empty()) + { + std::ostringstream os; + os << BOOST_CURRENT_FUNCTION << ": Stack is unexpectedly empty."; + throw python_json_error(os.str()); + } + + m_stack.pop_back(); + } + + void begin_object() + { + if (m_root) + { + PyObject* dict = push_value(PyDict_New()); + m_stack.push_back(parser_stack(dict, json::node_t::object)); + } + else + { + m_root = PyDict_New(); + m_stack.push_back(parser_stack(m_root, json::node_t::object)); + } + } + + void object_key(std::string_view key, bool /*transient*/) + { + parser_stack& cur = m_stack.back(); + cur.key = PyUnicode_FromStringAndSize(key.data(), key.size()); + } + + void end_object() + { + if (m_stack.empty()) + { + std::ostringstream os; + os << BOOST_CURRENT_FUNCTION << ": Stack is unexpectedly empty."; + throw python_json_error(os.str()); + } + + m_stack.pop_back(); + } + + void boolean_true() + { + Py_INCREF(Py_True); + push_value(Py_True); + } + + void boolean_false() + { + Py_INCREF(Py_False); + push_value(Py_False); + } + + void null() + { + Py_INCREF(Py_None); + push_value(Py_None); + } + + void string(std::string_view val, bool /*transient*/) + { + push_value(PyUnicode_FromStringAndSize(val.data(), val.size())); + } + + void number(double val) + { + push_value(PyFloat_FromDouble(val)); + } + + PyObject* get_root() + { + PyObject* o = m_root; + m_root = nullptr; + return o; + } +}; + +PyObject* json_loads(PyObject* /*module*/, PyObject* args, PyObject* kwargs) +{ + char* stream = nullptr; + static const char* kwlist[] = { "s", nullptr }; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", const_cast<char**>(kwlist), &stream)) + { + PyErr_SetString(PyExc_TypeError, "The method must be given a string."); + return nullptr; + } + + json_parser_handler hdl; + orcus::json_parser<json_parser_handler> parser(stream, hdl); + try + { + parser.parse(); + return hdl.get_root(); + } + catch (const orcus::parse_error& e) + { + PyErr_SetString(PyExc_TypeError, e.what()); + } + return nullptr; +} + +PyMethodDef orcus_methods[] = +{ + { "loads", (PyCFunction)json_loads, METH_VARARGS | METH_KEYWORDS, "Load JSON string into a Python object." }, + { nullptr, nullptr, 0, nullptr } +}; + +struct PyModuleDef moduledef = +{ + PyModuleDef_HEAD_INIT, + "_orcus_json", + nullptr, + sizeof(struct module_state), + orcus_methods, + nullptr, + orcus_traverse, + orcus_clear, + nullptr +}; + +} + +}} + +extern "C" { + +ORCUS_DLLPUBLIC PyObject* PyInit__orcus_json() +{ + PyObject* m = PyModule_Create(&orcus::python::moduledef); + return m; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/src/python/memory.cpp b/src/python/memory.cpp new file mode 100644 index 0000000..d2bbd09 --- /dev/null +++ b/src/python/memory.cpp @@ -0,0 +1,46 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "memory.hpp" + +namespace orcus { namespace python { + +void pyobj_unique_deleter::operator ()(PyObject* p) const +{ + Py_XDECREF(p); +} + +py_scoped_ref::py_scoped_ref() : m_pyobj(nullptr) {} +py_scoped_ref::py_scoped_ref(PyObject* p) : m_pyobj(p) {} + +py_scoped_ref::~py_scoped_ref() +{ + if (m_pyobj) + Py_DECREF(m_pyobj); +} + +py_scoped_ref& py_scoped_ref::operator= (PyObject* p) +{ + if (m_pyobj) + Py_DECREF(m_pyobj); + m_pyobj = p; + return *this; +} + +PyObject* py_scoped_ref::get() +{ + return m_pyobj; +} + +py_scoped_ref::operator bool() const +{ + return m_pyobj != nullptr; +} + +}} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/src/python/memory.hpp b/src/python/memory.hpp new file mode 100644 index 0000000..ecc7d65 --- /dev/null +++ b/src/python/memory.hpp @@ -0,0 +1,41 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef INCLUDED_ORCUS_PYTHON_MEMORY_HPP +#define INCLUDED_ORCUS_PYTHON_MEMORY_HPP + +#include <memory> +#include <Python.h> + +namespace orcus { namespace python { + +struct pyobj_unique_deleter +{ + void operator() (PyObject* p) const; +}; + +using py_unique_ptr = std::unique_ptr<PyObject, pyobj_unique_deleter>; + +class py_scoped_ref +{ + PyObject* m_pyobj; +public: + py_scoped_ref(); + py_scoped_ref(PyObject* p); + ~py_scoped_ref(); + + py_scoped_ref& operator= (PyObject* p); + PyObject* get(); + operator bool() const; +}; + +}} + + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/src/python/named_expression.cpp b/src/python/named_expression.cpp new file mode 100644 index 0000000..1402daa --- /dev/null +++ b/src/python/named_expression.cpp @@ -0,0 +1,215 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "named_expression.hpp" +#include "formula_token.hpp" +#include "formula_tokens.hpp" +#include "orcus/spreadsheet/document.hpp" + +#include <ixion/formula.hpp> +#include <ixion/model_context.hpp> +#include <ixion/named_expressions_iterator.hpp> +#include <ixion/formula_name_resolver.hpp> +#include <structmember.h> + +namespace ss = orcus::spreadsheet; + +namespace orcus { namespace python { + +namespace { + +/** non-python part of the object. */ +struct named_exp_data +{ + const ss::document* doc = nullptr; + const ixion::formula_tokens_t* tokens = nullptr; + ixion::abs_address_t origin; +}; + +/** + * Python object for orcus.NamedExpression. + */ +struct pyobj_named_exp +{ + PyObject_HEAD + + PyObject* origin; + PyObject* formula; + + named_exp_data* data; +}; + +inline pyobj_named_exp* t(PyObject* self) +{ + return reinterpret_cast<pyobj_named_exp*>(self); +} + +void init_members(pyobj_named_exp* self) +{ + Py_INCREF(Py_None); + self->origin = Py_None; + + Py_INCREF(Py_None); + self->formula = Py_None; +} + +void tp_dealloc(pyobj_named_exp* self) +{ + delete self->data; + self->data = nullptr; + + Py_CLEAR(self->origin); + Py_CLEAR(self->formula); + + Py_TYPE(self)->tp_free(reinterpret_cast<PyObject*>(self)); +} + +int tp_init(pyobj_named_exp* self, PyObject* /*args*/, PyObject* /*kwargs*/) +{ + init_members(self); + return 0; +} + +PyObject* tp_new(PyTypeObject* type, PyObject* /*args*/, PyObject* /*kwargs*/) +{ + pyobj_named_exp* self = (pyobj_named_exp*)type->tp_alloc(type, 0); + self->data = new named_exp_data; + return reinterpret_cast<PyObject*>(self); +} + +PyObject* ne_get_formula_tokens(PyObject* self, PyObject* /*args*/, PyObject* /*kwargs*/) +{ + named_exp_data& data = *t(self)->data; + if (!data.tokens) + { + Py_INCREF(Py_None); + return Py_None; + } + + return create_formula_tokens_iterator_object(*data.doc, data.origin, *data.tokens); +} + +PyMethodDef tp_methods[] = +{ + { "get_formula_tokens", (PyCFunction)ne_get_formula_tokens, METH_NOARGS, "Get a formula tokens iterator." }, + { nullptr } +}; + +PyMemberDef tp_members[] = +{ + { (char*)"origin", T_OBJECT_EX, offsetof(pyobj_named_exp, origin), READONLY, (char*)"anchoring cell for the named expression" }, + { (char*)"formula", T_OBJECT_EX, offsetof(pyobj_named_exp, formula), READONLY, (char*)"formula string" }, + { nullptr } +}; + +PyTypeObject named_exp_type = +{ + PyVarObject_HEAD_INIT(nullptr, 0) + "orcus.NamedExpression", // tp_name + sizeof(pyobj_named_exp), // tp_basicsize + 0, // tp_itemsize + (destructor)tp_dealloc, // tp_dealloc + 0, // tp_print + 0, // tp_getattr + 0, // tp_setattr + 0, // tp_compare + 0, // tp_repr + 0, // tp_as_number + 0, // tp_as_sequence + 0, // tp_as_mapping + 0, // tp_hash + 0, // tp_call + 0, // tp_str + 0, // tp_getattro + 0, // tp_setattro + 0, // tp_as_buffer + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, // tp_flags + "orcus spreadsheet named expression", // tp_doc + 0, // tp_traverse + 0, // tp_clear + 0, // tp_richcompare + 0, // tp_weaklistoffset + 0, // tp_iter + 0, // tp_iternext + tp_methods, // tp_methods + tp_members, // tp_members + 0, // tp_getset + 0, // tp_base + 0, // tp_dict + 0, // tp_descr_get + 0, // tp_descr_set + 0, // tp_dictoffset + (initproc)tp_init, // tp_init + 0, // tp_alloc + tp_new, // tp_new +}; + +} // anonymous namespace + +PyObject* create_named_exp_object(const spreadsheet::document& doc, const ixion::named_expression_t* exp) +{ + PyTypeObject* named_exp_type = get_named_exp_type(); + if (!named_exp_type) + { + PyErr_SetString(PyExc_RuntimeError, "Failed to get the named expression type object."); + return nullptr; + } + + PyObject* obj = named_exp_type->tp_new(named_exp_type, nullptr, nullptr); + if (!obj) + { + PyErr_SetString(PyExc_RuntimeError, "Failed to instantiate a named expression object."); + return nullptr; + } + + pyobj_named_exp* self = reinterpret_cast<pyobj_named_exp*>(obj); + init_members(self); + + if (exp) + { + named_exp_data& data = *self->data; + data.doc = &doc; + data.origin = exp->origin; + data.tokens = &exp->tokens; + + const ixion::model_context& cxt = doc.get_model_context(); + auto* resolver = doc.get_formula_name_resolver(spreadsheet::formula_ref_context_t::global); + + // Create base + std::string origin_s = resolver->get_name(exp->origin, ixion::abs_address_t(), true); + self->origin = PyUnicode_FromStringAndSize(origin_s.data(), origin_s.size()); + + // Create formula expression string. + std::string formula_s = ixion::print_formula_tokens(cxt, exp->origin, *resolver, exp->tokens); + self->formula = PyUnicode_FromStringAndSize(formula_s.data(), formula_s.size()); + } + + return obj; +} + +PyObject* create_named_exp_dict(const ss::document& doc, ixion::named_expressions_iterator iter) +{ + PyObject* dict = PyDict_New(); + for (; iter.has(); iter.next()) + { + auto ne = iter.get(); + PyObject* name = PyUnicode_FromStringAndSize(ne.name->data(), ne.name->size()); + PyObject* tokens = create_named_exp_object(doc, ne.expression); + PyDict_SetItem(dict, name, tokens); + } + + return dict; +} + +PyTypeObject* get_named_exp_type() +{ + return &named_exp_type; +} + +}} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/src/python/named_expression.hpp b/src/python/named_expression.hpp new file mode 100644 index 0000000..5473d6f --- /dev/null +++ b/src/python/named_expression.hpp @@ -0,0 +1,42 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef INCLUDED_ORCUS_PYTHON_NAMED_EXPRESSION_HPP +#define INCLUDED_ORCUS_PYTHON_NAMED_EXPRESSION_HPP + +#include "orcus/spreadsheet/types.hpp" + +#include <Python.h> +#include <ixion/formula_tokens_fwd.hpp> + +namespace ixion { + +class named_expressions_iterator; + +} + +namespace orcus { + +namespace spreadsheet { + +class document; + +} + +namespace python { + +PyObject* create_named_exp_object(const spreadsheet::document& doc, const ixion::named_expression_t* exp); + +PyObject* create_named_exp_dict(const spreadsheet::document& doc, ixion::named_expressions_iterator iter); + +PyTypeObject* get_named_exp_type(); + +}} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/src/python/named_expressions.cpp b/src/python/named_expressions.cpp new file mode 100644 index 0000000..6faffee --- /dev/null +++ b/src/python/named_expressions.cpp @@ -0,0 +1,218 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "named_expressions.hpp" +#include "named_expression.hpp" +#include "global.hpp" +#include "orcus/spreadsheet/document.hpp" + +#include <ixion/formula.hpp> +#include <ixion/model_context.hpp> +#include <ixion/named_expressions_iterator.hpp> +#include <ixion/formula_name_resolver.hpp> +#include <structmember.h> + +namespace ss = orcus::spreadsheet; + +namespace orcus { namespace python { + +namespace { + +/** non-python part. */ +struct named_exps_data +{ + ss::sheet_t origin_sheet = -1; // -1 for global, >=0 for sheet local. + const ss::document* doc = nullptr; + ixion::named_expressions_iterator src; // original iterator to copy from. + ixion::named_expressions_iterator iter; +}; + +/** python object. */ +struct pyobj_named_exps +{ + PyObject_HEAD + + named_exps_data* data; +}; + +inline pyobj_named_exps* t(PyObject* self) +{ + return reinterpret_cast<pyobj_named_exps*>(self); +} + +PyObject* named_exps_names(PyObject* self, PyObject* /*args*/, PyObject* /*kwargs*/) +{ + named_exps_data& data = *t(self)->data; + PyObject* s = PySet_New(nullptr); + + for (auto iter = data.src; iter.has(); iter.next()) + { + const std::string* name = iter.get().name; + PySet_Add(s, PyUnicode_FromStringAndSize(name->data(), name->size())); + } + + return s; +} + +void tp_dealloc(pyobj_named_exps* self) +{ + delete self->data; + Py_TYPE(self)->tp_free(reinterpret_cast<PyObject*>(self)); +} + +int tp_init(pyobj_named_exps* /*self*/, PyObject* /*args*/, PyObject* /*kwargs*/) +{ + return 0; +} + +PyObject* tp_new(PyTypeObject* type, PyObject* /*args*/, PyObject* /*kwargs*/) +{ + pyobj_named_exps* self = t(type->tp_alloc(type, 0)); + self->data = new named_exps_data; + return reinterpret_cast<PyObject*>(self); +} + +PyObject* tp_iter(PyObject* self) +{ + named_exps_data& data = *t(self)->data; + data.iter = data.src; + + Py_INCREF(self); + return self; +} + +PyObject* tp_iternext(PyObject* self) +{ + named_exps_data& data = *t(self)->data; + auto& iter = data.iter; + + if (!iter.has()) + { + PyErr_SetNone(PyExc_StopIteration); + return nullptr; + } + + ixion::named_expressions_iterator::named_expression item = iter.get(); + iter.next(); + + PyObject* name = PyUnicode_FromStringAndSize(item.name->data(), item.name->size()); + if (!name) + return nullptr; + + PyObject* ne = create_named_exp_object(*data.doc, item.expression); + if (!ne) + return nullptr; + + PyObject* tup = PyTuple_New(2); + PyTuple_SET_ITEM(tup, 0, name); + PyTuple_SET_ITEM(tup, 1, ne); + + return tup; +} + +Py_ssize_t sq_length(PyObject* self) +{ + named_exps_data& data = *t(self)->data; + return data.src.size(); +} + +PySequenceMethods tp_as_sequence = +{ + sq_length, // lenfunc sq_length + 0, // binaryfunc sq_concat + 0, // ssizeargfunc sq_repeat + 0, // ssizeargfunc sq_item + 0, // void *was_sq_slice + 0, // ssizeobjargproc sq_ass_item + 0, // void *was_sq_ass_slice + 0, // objobjproc sq_contains + 0, // binaryfunc sq_inplace_concat + 0, // ssizeargfunc sq_inplace_repeat +}; + +PyMethodDef tp_methods[] = +{ + { "names", (PyCFunction)named_exps_names, METH_NOARGS, "Get names for all named expressions stored." }, + { nullptr } +}; + +PyMemberDef tp_members[] = +{ + { nullptr } +}; + +PyTypeObject named_exps_type = +{ + PyVarObject_HEAD_INIT(nullptr, 0) + "orcus.NamedExpressions", // tp_name + sizeof(pyobj_named_exps), // tp_basicsize + 0, // tp_itemsize + (destructor)tp_dealloc, // tp_dealloc + 0, // tp_print + 0, // tp_getattr + 0, // tp_setattr + 0, // tp_compare + 0, // tp_repr + 0, // tp_as_number + &tp_as_sequence, // tp_as_sequence + 0, // tp_as_mapping + 0, // tp_hash + 0, // tp_call + 0, // tp_str + 0, // tp_getattro + 0, // tp_setattro + 0, // tp_as_buffer + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, // tp_flags + "orcus spreadsheet formula tokens", // tp_doc + 0, // tp_traverse + 0, // tp_clear + 0, // tp_richcompare + 0, // tp_weaklistoffset + tp_iter, // tp_iter + tp_iternext, // tp_iternext + tp_methods, // tp_methods + tp_members, // tp_members + 0, // tp_getset + 0, // tp_base + 0, // tp_dict + 0, // tp_descr_get + 0, // tp_descr_set + 0, // tp_dictoffset + (initproc)tp_init, // tp_init + 0, // tp_alloc + tp_new, // tp_new +}; + +} // anonymous namespace + +PyObject* create_named_expressions_object( + spreadsheet::sheet_t origin_sheet, const spreadsheet::document& doc, ixion::named_expressions_iterator iter) +{ + PyTypeObject* type = get_named_exps_type(); + + PyObject* obj = create_object_from_type(type); + if (!obj) + return nullptr; + + type->tp_init(obj, nullptr, nullptr); + + named_exps_data& data = *t(obj)->data; + data.src = iter; + data.origin_sheet = origin_sheet; + data.doc = &doc; + + return obj; +} + +PyTypeObject* get_named_exps_type() +{ + return &named_exps_type; +} + +}} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/src/python/named_expressions.hpp b/src/python/named_expressions.hpp new file mode 100644 index 0000000..cfad195 --- /dev/null +++ b/src/python/named_expressions.hpp @@ -0,0 +1,41 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef INCLUDED_ORCUS_PYTHON_NAMED_EXPRESSIONS_HPP +#define INCLUDED_ORCUS_PYTHON_NAMED_EXPRESSIONS_HPP + +#include "orcus/spreadsheet/types.hpp" + +#include <Python.h> +#include <ixion/formula_tokens_fwd.hpp> + +namespace ixion { + +class named_expressions_iterator; + +} + +namespace orcus { + +namespace spreadsheet { + +class document; + +} + +namespace python { + +PyObject* create_named_expressions_object( + spreadsheet::sheet_t origin_sheet, const spreadsheet::document& doc, ixion::named_expressions_iterator iter); + +PyTypeObject* get_named_exps_type(); + +}} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/src/python/ods.cpp b/src/python/ods.cpp new file mode 100644 index 0000000..0a7f5ad --- /dev/null +++ b/src/python/ods.cpp @@ -0,0 +1,58 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "ods.hpp" +#include "global.hpp" + +#ifdef __ORCUS_PYTHON_ODS +#include "document.hpp" +#include "orcus/orcus_ods.hpp" +#include "orcus/spreadsheet/document.hpp" +#include "orcus/spreadsheet/factory.hpp" +#endif + +namespace orcus { namespace python { + +#ifdef __ORCUS_PYTHON_ODS + +PyObject* ods_read(PyObject* /*module*/, PyObject* args, PyObject* kwargs) +{ + stream_with_formulas data = read_stream_and_formula_params_from_args(args, kwargs); + if (!data.stream) + return nullptr; + + try + { + spreadsheet::range_size_t ss{1048576, 16384}; + std::unique_ptr<spreadsheet::document> doc = std::make_unique<spreadsheet::document>(ss); + spreadsheet::import_factory fact(*doc); + fact.set_recalc_formula_cells(data.recalc_formula_cells); + fact.set_formula_error_policy(data.error_policy); + orcus_ods app(&fact); + + return import_from_stream_into_document(data.stream.get(), app, std::move(doc)); + } + catch (const std::exception& e) + { + set_python_exception(PyExc_RuntimeError, e); + return nullptr; + } +} + +#else + +PyObject* ods_read(PyObject*, PyObject*, PyObject*) +{ + PyErr_SetString(PyExc_RuntimeError, "The ods module is not enabled."); + return nullptr; +} + +#endif + +}} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/src/python/ods.hpp b/src/python/ods.hpp new file mode 100644 index 0000000..a25c861 --- /dev/null +++ b/src/python/ods.hpp @@ -0,0 +1,21 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef INCLUDED_ORCUS_PYTHON_ODS_HPP +#define INCLUDED_ORCUS_PYTHON_ODS_HPP + +#include <Python.h> + +namespace orcus { namespace python { + +PyObject* ods_read(PyObject* module, PyObject* args, PyObject* kwargs); + +}} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/src/python/orcus/__init__.py b/src/python/orcus/__init__.py new file mode 100644 index 0000000..4914d95 --- /dev/null +++ b/src/python/orcus/__init__.py @@ -0,0 +1,111 @@ +####################################################################### +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +######################################################################## + +try: + from _orcus import * + from _orcus import __version__ +except ModuleNotFoundError: + # We do this to enable sphinx to generate documentation without having to + # build the C++ part. + pass + +from enum import Enum + + +class FormatType(Enum): + """Collection of file format types currently used in orcus.""" + + UNKNOWN = 0 + ODS = 1 + XLSX = 2 + GNUMERIC = 3 + XLS_XML = 4 + CSV = 5 + YAML = 6 + JSON = 7 + XML = 8 + PARQUET = 9 + + +class CellType(Enum): + """Collection of cell types stored in spreadsheet.""" + + UNKNOWN = 0 + EMPTY = 1 + BOOLEAN = 2 + NUMERIC = 3 + STRING = 4 + STRING_WITH_ERROR = 5 + FORMULA = 6 + FORMULA_WITH_ERROR = 7 + + +class FormulaTokenType(Enum): + """Collection of formula token types.""" + + UNKNOWN = 0 + REFERENCE = 1 + VALUE = 2 + NAME = 3 + FUNCTION = 4 + OPERATOR = 5 + ERROR = 6 + + +class FormulaTokenOp(Enum): + """Collection of formula token opcodes.""" + + UNKNOWN = 0 + SINGLE_REF = 1 + RANGE_REF = 2 + TABLE_REF = 3 + NAMED_EXPRESSION = 4 + STRING = 5 + VALUE = 6 + FUNCTION = 7 + PLUS = 8 + MINUS = 9 + DIVIDE = 10 + MULTIPLY = 11 + EXPONENT = 12 + CONCAT = 13 + EQUAL = 14 + NOT_EQUAL = 15 + LESS = 16 + GREATER = 17 + LESS_EQUAL = 18 + GREATER_EQUAL = 19 + OPEN = 20 + CLOSE = 21 + SEP = 22 + ERROR = 23 + + +def get_document_loader_module(format_type): + """Obtain a document loader module for the specified format type. + + Args: + format_type (orcus.FormatType): + Format type for which to load a document loader. + + Returns: + Document loader module. + """ + m = None + if format_type == FormatType.ODS: + from . import ods as m + elif format_type == FormatType.XLSX: + from . import xlsx as m + elif format_type == FormatType.XLS_XML: + from . import xls_xml as m + elif format_type == FormatType.GNUMERIC: + from . import gnumeric as m + elif format_type == FormatType.CSV: + from . import csv as m + + return m diff --git a/src/python/orcus/csv.py b/src/python/orcus/csv.py new file mode 100644 index 0000000..bad095f --- /dev/null +++ b/src/python/orcus/csv.py @@ -0,0 +1,10 @@ +######################################################################## +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +######################################################################## + +from _orcus import _csv_read as read + diff --git a/src/python/orcus/gnumeric.py b/src/python/orcus/gnumeric.py new file mode 100644 index 0000000..d7f0f23 --- /dev/null +++ b/src/python/orcus/gnumeric.py @@ -0,0 +1,10 @@ +######################################################################## +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +######################################################################## + +from _orcus import _gnumeric_read as read + diff --git a/src/python/orcus/json.py b/src/python/orcus/json.py new file mode 100644 index 0000000..d351766 --- /dev/null +++ b/src/python/orcus/json.py @@ -0,0 +1,10 @@ +######################################################################## +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +######################################################################## + +from _orcus_json import * + diff --git a/src/python/orcus/ods.py b/src/python/orcus/ods.py new file mode 100644 index 0000000..8bcf2b9 --- /dev/null +++ b/src/python/orcus/ods.py @@ -0,0 +1,10 @@ +######################################################################## +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +######################################################################## + +from _orcus import _ods_read as read + diff --git a/src/python/orcus/tools/__init__.py b/src/python/orcus/tools/__init__.py new file mode 100644 index 0000000..413bd0a --- /dev/null +++ b/src/python/orcus/tools/__init__.py @@ -0,0 +1,9 @@ +######################################################################## +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +######################################################################## + + diff --git a/src/python/orcus/tools/bugzilla.py b/src/python/orcus/tools/bugzilla.py new file mode 100644 index 0000000..f23b4d5 --- /dev/null +++ b/src/python/orcus/tools/bugzilla.py @@ -0,0 +1,219 @@ +######################################################################## +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +######################################################################## + +import argparse +import requests +import json +import os +import base64 +import traceback +import concurrent.futures as cf +from pathlib import Path +from urllib.parse import urlparse + + +class BugzillaAccess: + """Encapsulates access to a bugzilla server by using its REST API. + + Args: + bzurl (str): URL to the bugzilla server. + cache_dir (:obj:`pathlib.Path`): path to the cache directory. + """ + + def __init__(self, bzurl, cache_dir): + self._bzurl = bzurl + self._cache_dir = cache_dir + os.makedirs(self._cache_dir, exist_ok=True) + + def _get_cache_content(self, cache_file, func_fetch): + if os.path.isfile(cache_file): + with open(cache_file, 'r') as f: + return f.read() + + s = func_fetch() + with open(cache_file, 'w') as f: + f.write(s) + + return s + + def get_bug_ids(self, bz_params): + """Get all bug ID's for specified bugzilla query parameters. + + Args: + bz_params (dict): + dictionary containing all search parameters. Each search term + must form a single key-value pair. + + Returns (:obj:`list` of :obj:`str`): + list of bug ID strings. + """ + + def _fetch(): + r = requests.get( + f"{self._bzurl}/rest/bug", + params=bz_params + ) + + if r.status_code != 200: + raise RuntimeError(f"failed to query bug ids from the TDF bugzilla! (status:{r.status_code})") + return r.text + + escape_chars = " /" + buf = [] + for key in bz_params.keys(): + v = str(bz_params[key]) + for c in escape_chars: + v = v.replace(c, '-') + buf.append(key) + buf.append(v) + + cache_file = '-'.join(buf) + ".json" + cache_file = self._cache_dir / cache_file + s = self._get_cache_content(cache_file, _fetch) + + content = json.loads(s) + bugs = content.get("bugs") + if not bugs: + return [] + + bug_ids = [bug.get("id") for bug in bugs] + bug_ids = [x for x in filter(None, bug_ids)] + return bug_ids + + def get_attachments(self, bug_id): + """Fetch all attachments for specified bug.""" + + def _fetch(): + r = requests.get(f"{self._bzurl}/rest/bug/{bug_id}/attachment") + if r.status_code != 200: + raise RuntimeError( + f"failed to fetch the attachments for bug {bug_id}! (status:{r.status_code})") + return r.text + + cache_file = self._cache_dir / f"attachments-{bug_id}.json" + s = self._get_cache_content(cache_file, _fetch) + content = json.loads(s) + attachments = list() + for d in content["bugs"][str(bug_id)]: + data = d["data"] + if not data: + continue + bytes = base64.b64decode(data) + attachments.append({ + "content_type": d["content_type"], + "filename": d["file_name"], + "data": bytes + }) + return attachments + + +def parse_query_params(queries): + bz_params = dict() + for query in queries: + k, v = query.split('=') + if v and v[0] in ('"', "'"): + if v[0] != v[-1]: + raise argparse.ArgumentError(f"mis-matched quotes in {query}") + v = v[1:-1] + bz_params[k] = v + return bz_params + + +def _create_argparser(): + parser = argparse.ArgumentParser( + description="""This command allows you to download attachments from a +bugzilla server that supports REST API.""") + parser.add_argument( + "--outdir", "-o", type=str, required=True, + help="""output directory for downloaded files. Downloaded files are +grouped by their respective bug ID's.""") + parser.add_argument( + "--limit", type=int, default=50, + help="number of bugs to include in a single set of search results.") + parser.add_argument( + "--offset", type=int, default=0, + help="number of bugs to skip in the search results.") + parser.add_argument( + "--cont", action="store_true", default=False, + help="""when specified, the search continues after the initial batch +is returned, by retrieving the next batch of results until the entire search +results are returned. The number specified by the ``--limit`` option is used +as the batch size.""") + parser.add_argument( + "--worker", type=int, default=8, + help="number of worker threads to use for parallel downloads of files.") + parser.add_argument( + "--cache-dir", type=Path, default=Path(".bugzilla"), + help="""directory to keep downloaded bugzilla search results. The +command will not send the query request to the remote server when the results +are cached. You may want to delete the cache directory after you are finished.""") + parser.add_argument( + "--url", type=str, required=True, + help="""base URL for bugzilla service. It must begin with the +``http(s)://`` prefix.""") + parser.add_argument( + "query", type=str, nargs='*', + help="""One or more query term to use to limit your search. Each query +term must be in the form key=value. You need to quote the value string when the +value string contains whitespace character i.e. key="value with space".""") + return parser + + +def main(): + parser = _create_argparser() + args = parser.parse_args() + + bz_params = parse_query_params(args.query) + + for k, v in bz_params.items(): + print(f"{k}: {v}") + + bz_params["limit"] = args.limit + bz_params["offset"] = args.offset + + url = urlparse(args.url) + cache_dir = Path(args.cache_dir) / url.netloc + bz = BugzillaAccess(args.url, cache_dir) + + def _run(bug_id, index, totals): + """Top-level function for each worker thread.""" + width = len(str(totals)) + index_s = str(index+1) + index_s = ' ' * (width - len(index_s)) + index_s + print(f"({index_s}/{totals}) fetching attachments for bug {bug_id} ...", flush=True) + + try: + attachments = bz.get_attachments(bug_id) + for attachment in attachments: + filepath = Path(args.outdir) / url.netloc / str(bug_id) / attachment["filename"] + os.makedirs(os.path.dirname(filepath), exist_ok=True) + with open(filepath, "wb") as f: + f.write(attachment["data"]) + except Exception as e: + traceback.print_exc() + print(e) + + iter_count = 0 + while True: + bug_ids = bz.get_bug_ids(bz_params) + if not bug_ids: + return + print(f"-- iteration {iter_count+1}", flush=True) + with cf.ThreadPoolExecutor(max_workers=args.worker) as executor: + for i, bug_id in enumerate(bug_ids): + executor.submit(_run, bug_id, i, len(bug_ids)) + + if not args.cont: + return + + bz_params["offset"] += bz_params["limit"] + iter_count += 1 + + +if __name__ == "__main__": + main() diff --git a/src/python/orcus/tools/file_processor.py b/src/python/orcus/tools/file_processor.py new file mode 100644 index 0000000..472fea3 --- /dev/null +++ b/src/python/orcus/tools/file_processor.py @@ -0,0 +1,295 @@ +######################################################################## +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +######################################################################## + +import argparse +import os +import os.path +import sys +import string +import pathlib +import enum +import re +import multiprocessing as mp +import importlib.util + +import orcus + + +class _Config: + ext_good = "orcus-pf.good" + ext_bad = "orcus-pf.bad" + ext_out = "orcus-pf.out" + prefix_skip = ".orcus-pf.skip." + + +config = _Config() + + +def is_special_file(filename): + if filename.find(config.prefix_skip) >= 0: + return True + + return filename.endswith(config.ext_out) or filename.endswith(config.ext_good) or filename.endswith(config.ext_bad) + + +def skips_by_rule(filename, skip_rules): + for rule in skip_rules: + if rule.search(filename): + return True + return False + + +def sanitize_string(s): + """Replace non-printable characters with \\x[value].""" + + buf = list() + for c in s: + if c in string.printable: + buf.append(c) + else: + buf.append(f"\\x{ord(c):02X}") + + return "".join(buf) + + +class LoadStatus(enum.Enum): + SUCCESS = 0 + FAILURE = 1 + SKIPPED = 2 + + +def load_doc(bytes): + + buf = list() + + try: + format_type = orcus.detect_format(bytes) + except Exception as e: + buf.append(str(e)) + status = LoadStatus.SKIPPED + return None, status, buf + + buf.append(f"* format type: {format_type}") + buf.append(f"* size: {len(bytes)} bytes") + + doc = None + + try: + loader = orcus.get_document_loader_module(format_type) + if loader is None: + buf.append(f"unhandled format type: {format_type}") + status = LoadStatus.SKIPPED + return doc, status, buf + + status = LoadStatus.SUCCESS + doc = loader.read(bytes, error_policy="skip") + return doc, status, buf + + except Exception as e: + buf.append(f"{e.__class__.__name__}: {e}") + status = LoadStatus.FAILURE + return None, status, buf + + +def print_results(inpath): + outpath = f"{inpath}.{config.ext_out}" + with open(outpath, "r") as f: + print() + for line in f.readlines(): + print(f" {line.strip()}") + print() + + +def remove_result_files(rootdir): + for root, dir, files in os.walk(rootdir): + for filename in files: + if is_special_file(filename): + filepath = os.path.join(root, filename) + os.remove(filepath) + + +def show_result_stats(rootdir): + counts = dict(good=0, bad=0, skipped=0, unprocessed=0) + for root, dir, files in os.walk(rootdir): + for filename in files: + if is_special_file(filename): + continue + + inpath = os.path.join(root, filename) + out_filepath = f"{inpath}.{config.ext_out}" + good_filepath = f"{inpath}.{config.ext_good}" + bad_filepath = f"{inpath}.{config.ext_bad}" + if os.path.isfile(good_filepath): + counts["good"] += 1 + elif os.path.isfile(bad_filepath): + counts["bad"] += 1 + elif os.path.isfile(out_filepath): + counts["skipped"] += 1 + else: + counts["unprocessed"] += 1 + + print("* result counts") + for cat in ("good", "bad", "skipped", "unprocessed"): + print(f" * {cat}: {counts[cat]}") + + total = counts["good"] + counts["bad"] + if total: + print("* ratios") + print(f" * good: {counts['good']/total*100:.1f}%") + print(f" * bad: {counts['bad']/total*100:.1f}%") + + +def show_results(rootdir, good, bad): + for root, dir, files in os.walk(rootdir): + for filename in files: + if is_special_file(filename): + continue + inpath = os.path.join(root, filename) + good_filepath = f"{inpath}.{config.ext_good}" + bad_filepath = f"{inpath}.{config.ext_bad}" + + if os.path.isfile(good_filepath) and good: + print(sanitize_string(inpath), flush=True) + print_results(inpath) + elif os.path.isfile(bad_filepath) and bad: + print(sanitize_string(inpath), flush=True) + print_results(inpath) + else: + continue + + +def load_module_from_filepath(filepath): + if not os.path.isfile(filepath): + raise RuntimeError(f"{filepath} is not a valid file.") + + mod_name = os.path.splitext(os.path.basename(filepath))[0] + mod_name = mod_name.replace('-', '_') + spec = importlib.util.spec_from_file_location(mod_name, filepath) + mod = importlib.util.module_from_spec(spec) + spec.loader.exec_module(mod) + return mod + + +def process_filepath(i, inpath, outpath, processor_path): + mod = load_module_from_filepath(processor_path) if processor_path else None + term_buf = list() # terminal output buffer + term_buf.append(f"{i} {sanitize_string(inpath)}") + + good_filepath = f"{inpath}.{config.ext_good}" + bad_filepath = f"{inpath}.{config.ext_bad}" + + if os.path.isfile(good_filepath) or os.path.isfile(bad_filepath): + term_buf.append("already processed. skipping...") + return "\n".join(term_buf) + + success = False + with open(inpath, 'rb') as f: + bytes = f.read() + + buf = list() # non-terminal output buffer + doc, status, output = load_doc(bytes) + buf.extend(output) + if doc and mod: + buf.extend(mod.process_document(inpath, doc)) + + with open(outpath, "w") as f: + f.write("\n".join(buf)) + + term_buf.extend(buf) + + if status == LoadStatus.SUCCESS: + pathlib.Path(good_filepath).touch() + elif status == LoadStatus.FAILURE: + pathlib.Path(bad_filepath).touch() + + return "\n".join(term_buf) + + +def _create_argparser(): + parser = argparse.ArgumentParser( + description="""This script allows you to process a collection of spreadsheet documents.""") + parser.add_argument( + "--skip-file", type=argparse.FileType("r"), + help="Optional text file containing a set of regular expressions (one per line). Files that match one of these rules will be skipped.") + parser.add_argument("--processes", type=int, default=1, help="Number of worker processes to use.") + parser.add_argument("-p", "--processor", type=str, help="Python module file containing callback functions.") + parser.add_argument( + "--remove-results", action="store_true", default=False, + help="Remove all cached results files from the directory tree.") + parser.add_argument( + "--results", action="store_true", default=False, + help="Display the results of the processed files.") + parser.add_argument( + "--good", action="store_true", default=False, + help="Display the results of the successfully processed files.") + parser.add_argument( + "--bad", action="store_true", default=False, + help="Display the results of the unsuccessfully processed files.") + parser.add_argument( + "--stats", action="store_true", default=False, + help="Display statistics of the results. Use it with --results.") + parser.add_argument( + "rootdir", metavar="ROOT-DIR", + help="Root directory below which to recursively find and process test files.") + return parser + + +def main(): + parser = _create_argparser() + args = parser.parse_args() + + if args.remove_results: + remove_result_files(args.rootdir) + return + + if args.results: + if args.stats: + show_result_stats(args.rootdir) + return + + show_results(args.rootdir, args.good, args.bad) + return + + skip_rules = list() + + if args.skip_file: + for line in args.skip_file.readlines(): + line = line.strip() + if not line: + continue + rule = re.compile(line) + skip_rules.append(rule) + + # build a list of files to process. + filepaths = list() + for root, dir, files in os.walk(args.rootdir): + for filename in files: + if is_special_file(filename): + continue + + inpath = os.path.join(root, filename) + outpath = f"{inpath}.{config.ext_out}" + if skips_by_rule(inpath, skip_rules): + pathlib.Path(outpath).touch() + continue + + filepaths.append((inpath, outpath)) + + with mp.Pool(processes=args.processes) as pool: + futures = list() + for i, (inpath, outpath) in enumerate(filepaths): + future = pool.apply_async(process_filepath, (i, inpath, outpath, args.processor)) + futures.append(future) + + for future in futures: + output = future.get() + print(output) + + +if __name__ == "__main__": + main() diff --git a/src/python/orcus/xls_xml.py b/src/python/orcus/xls_xml.py new file mode 100644 index 0000000..671d39b --- /dev/null +++ b/src/python/orcus/xls_xml.py @@ -0,0 +1,10 @@ +######################################################################## +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +######################################################################## + +from _orcus import _xls_xml_read as read + diff --git a/src/python/orcus/xlsx.py b/src/python/orcus/xlsx.py new file mode 100644 index 0000000..fd24ee1 --- /dev/null +++ b/src/python/orcus/xlsx.py @@ -0,0 +1,10 @@ +######################################################################## +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +######################################################################## + +from _orcus import _xlsx_read as read + diff --git a/src/python/python.cpp b/src/python/python.cpp new file mode 100644 index 0000000..53fb867 --- /dev/null +++ b/src/python/python.cpp @@ -0,0 +1,186 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "orcus/env.hpp" +#include "orcus/info.hpp" + +#include "root.hpp" +#include "xlsx.hpp" +#include "ods.hpp" +#include "csv.hpp" +#include "xls_xml.hpp" +#include "gnumeric.hpp" + +#ifdef __ORCUS_SPREADSHEET_MODEL +#include "document.hpp" +#include "sheet.hpp" +#include "sheet_rows.hpp" +#include "cell.hpp" +#include "named_expression.hpp" +#include "named_expressions.hpp" +#include "formula_token.hpp" +#include "formula_tokens.hpp" +#endif + +#include <iostream> +#include <sstream> +#include <string> + +#include <Python.h> + +#define ORCUS_DEBUG_PYTHON 0 +#define GETSTATE(m) ((struct module_state*)PyModule_GetState(m)) + +using namespace std; + +namespace orcus { namespace python { + +namespace { + +#if ORCUS_DEBUG_PYTHON +void print_args(PyObject* args) +{ + string args_str; + PyObject* repr = PyObject_Repr(args); + if (repr) + { + Py_INCREF(repr); + args_str = PyBytes_AsString(repr); + Py_DECREF(repr); + } + cout << args_str << "\n"; +} +#endif + +PyMethodDef orcus_methods[] = +{ + { "detect_format", (PyCFunction)detect_format, METH_VARARGS | METH_KEYWORDS, + "Detect the format type of a spreadsheet file." }, + + { "_csv_read", (PyCFunction)csv_read, METH_VARARGS | METH_KEYWORDS, + "Load specified csv file into a document model." }, + + { "_ods_read", (PyCFunction)ods_read, METH_VARARGS | METH_KEYWORDS, + "Load specified ods file into a document model." }, + + { "_xlsx_read", (PyCFunction)xlsx_read, METH_VARARGS | METH_KEYWORDS, + "Load specified xlsx file into a document model." }, + + { "_xls_xml_read", (PyCFunction)xls_xml_read, METH_VARARGS | METH_KEYWORDS, + "Load specified xls-xml file into a document model." }, + + { "_gnumeric_read", (PyCFunction)gnumeric_read, METH_VARARGS | METH_KEYWORDS, + "Load specified gnumeric file into a document model." }, + + { nullptr, nullptr, 0, nullptr } +}; + +struct module_state +{ + PyObject* error; +}; + +int orcus_traverse(PyObject* m, visitproc visit, void* arg) +{ + Py_VISIT(GETSTATE(m)->error); + return 0; +} + +int orcus_clear(PyObject* m) +{ + Py_CLEAR(GETSTATE(m)->error); + return 0; +} + +[[maybe_unused]] +bool add_type_to_module(PyObject* m, PyTypeObject* typeobj, const char* type_name) +{ + if (PyType_Ready(typeobj)) + return false; + + Py_INCREF(typeobj); + if (PyModule_AddObject(m, type_name, reinterpret_cast<PyObject*>(typeobj)) < 0) + { + Py_DECREF(m); + Py_DECREF(typeobj); + return false; + } + + return true; +} + +bool populate_module_attributes(PyObject* m) +{ + std::ostringstream os; + os << orcus::get_version_major() << '.' + << orcus::get_version_minor() << '.' + << orcus::get_version_micro(); + + PyObject* version = PyUnicode_FromString(os.str().data()); + if (PyModule_AddObject(m, "__version__", version) < 0) + return false; + + return true; +} + +} + +struct PyModuleDef moduledef = +{ + PyModuleDef_HEAD_INIT, + "_orcus", + nullptr, + sizeof(struct module_state), + orcus_methods, + nullptr, + orcus_traverse, + orcus_clear, + nullptr +}; + +}} + +extern "C" { + +ORCUS_DLLPUBLIC PyObject* PyInit__orcus() +{ + PyObject* m = PyModule_Create(&orcus::python::moduledef); + if (!orcus::python::populate_module_attributes(m)) + return nullptr; + +#ifdef __ORCUS_SPREADSHEET_MODEL + if (!orcus::python::add_type_to_module(m, orcus::python::get_document_type(), "Document")) + return nullptr; + + if (!orcus::python::add_type_to_module(m, orcus::python::get_sheet_type(), "Sheet")) + return nullptr; + + if (!orcus::python::add_type_to_module(m, orcus::python::get_sheet_rows_type(), "SheetRows")) + return nullptr; + + if (!orcus::python::add_type_to_module(m, orcus::python::get_cell_type(), "Cell")) + return nullptr; + + if (!orcus::python::add_type_to_module(m, orcus::python::get_named_exp_type(), "NamedExpression")) + return nullptr; + + if (!orcus::python::add_type_to_module(m, orcus::python::get_named_exps_type(), "NamedExpressions")) + return nullptr; + + if (!orcus::python::add_type_to_module(m, orcus::python::get_formula_token_type(), "FormulaToken")) + return nullptr; + + if (!orcus::python::add_type_to_module(m, orcus::python::get_formula_tokens_type(), "FormulaTokens")) + return nullptr; +#endif + + return m; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/src/python/root.cpp b/src/python/root.cpp new file mode 100644 index 0000000..d3ac021 --- /dev/null +++ b/src/python/root.cpp @@ -0,0 +1,68 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "root.hpp" +#include "document.hpp" +#include "global.hpp" + +#include "orcus/format_detection.hpp" +#include "orcus/info.hpp" + +#include <iostream> +#include <sstream> + +#include <object.h> + +using namespace std; + +namespace orcus { namespace python { + +PyObject* detect_format(PyObject* /*module*/, PyObject* args, PyObject* kwargs) +{ + py_unique_ptr stream = read_stream_from_args(args, kwargs); + if (!stream) + return nullptr; + + char* p = nullptr; + Py_ssize_t n = 0; + if (PyBytes_AsStringAndSize(stream.get(), &p, &n) < 0) + return nullptr; + + try + { + format_t ft = orcus::detect({p, (size_t)n}); + + switch (ft) + { + case format_t::ods: + return get_python_enum_value("FormatType", "ODS"); + case format_t::xlsx: + return get_python_enum_value("FormatType", "XLSX"); + case format_t::gnumeric: + return get_python_enum_value("FormatType", "GNUMERIC"); + case format_t::xls_xml: + return get_python_enum_value("FormatType", "XLS_XML"); + case format_t::parquet: + return get_python_enum_value("FormatType", "PARQUET"); + case format_t::csv: + return get_python_enum_value("FormatType", "CSV"); + case format_t::unknown: + default: + return get_python_enum_value("FormatType", "UNKNOWN"); + } + } + catch (const std::exception&) + { + PyErr_SetString(PyExc_ValueError, "failed to perform deep detection on this file."); + return nullptr; + } +} + +}} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ + diff --git a/src/python/root.hpp b/src/python/root.hpp new file mode 100644 index 0000000..9b679bd --- /dev/null +++ b/src/python/root.hpp @@ -0,0 +1,21 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef INCLUDED_ORCUS_PYTHON_ROOT_HPP +#define INCLUDED_ORCUS_PYTHON_ROOT_HPP + +#include <Python.h> + +namespace orcus { namespace python { + +PyObject* detect_format(PyObject* module, PyObject* args, PyObject* kwargs); + +}} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/src/python/sheet.cpp b/src/python/sheet.cpp new file mode 100644 index 0000000..d4037ea --- /dev/null +++ b/src/python/sheet.cpp @@ -0,0 +1,327 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "sheet.hpp" +#include "sheet_rows.hpp" +#include "named_expression.hpp" +#include "named_expressions.hpp" + +#include <orcus/spreadsheet/types.hpp> +#include <orcus/spreadsheet/sheet.hpp> +#include <orcus/spreadsheet/document.hpp> + +#include <ixion/model_context.hpp> +#include <ixion/named_expressions_iterator.hpp> + +#include <structmember.h> +#include <bytesobject.h> +#include <iostream> +#include <sstream> +#include <cstring> + +namespace ss = orcus::spreadsheet; + +namespace orcus { namespace python { + +sheet_data::sheet_data() : m_doc(nullptr), m_sheet(nullptr) {} +sheet_data::~sheet_data() {} + +namespace { + +/** + * Python object for orcus.Sheet. + */ +struct pyobj_sheet +{ + PyObject_HEAD + + PyObject* name; + PyObject* sheet_size; + PyObject* data_size; + PyObject* named_expressions; + + sheet_data* data; +}; + +inline pyobj_sheet* t(PyObject* self) +{ + return reinterpret_cast<pyobj_sheet*>(self); +} + +inline sheet_data* get_sheet_data(PyObject* self) +{ + return t(self)->data; +} + +/** + * Get the underlying C++ sheet class instance from the python object + * representation. + */ +inline spreadsheet::sheet* get_core_sheet(PyObject* self) +{ + return get_sheet_data(self)->m_sheet; +} + +void tp_dealloc(pyobj_sheet* self) +{ + delete self->data; + + Py_CLEAR(self->name); + Py_CLEAR(self->sheet_size); + Py_CLEAR(self->data_size); + + Py_TYPE(self)->tp_free(reinterpret_cast<PyObject*>(self)); +} + +PyObject* tp_new(PyTypeObject* type, PyObject* /*args*/, PyObject* /*kwargs*/) +{ + pyobj_sheet* self = (pyobj_sheet*)type->tp_alloc(type, 0); + self->data = new sheet_data; + return reinterpret_cast<PyObject*>(self); +} + +int tp_init(pyobj_sheet* /*self*/, PyObject* /*args*/, PyObject* /*kwargs*/) +{ + return 0; +} + +PyObject* sheet_get_rows(PyObject* self, PyObject* /*args*/, PyObject* /*kwargs*/) +{ + PyTypeObject* sr_type = get_sheet_rows_type(); + + PyObject* rows = sr_type->tp_new(sr_type, nullptr, nullptr); + if (!rows) + return nullptr; + + sr_type->tp_init(rows, nullptr, nullptr); + + // Populate the sheet rows data. + sheet_data* data = get_sheet_data(self); + store_sheet_rows_data(rows, data->m_doc, data->m_sheet); + + return rows; +} + +namespace { + +format_t to_format_type_enum(PyObject* format) +{ + static const char* err_not_format_type = "An enum value of 'orcus.FormatType' was expected."; + static const char* err_format_not_supported = "Unsupported format type."; + + // Check the type name. + + PyTypeObject* type = Py_TYPE(format); + if (!type || strncmp(type->tp_name, "FormatType", 10u) != 0) + { + PyErr_SetString(PyExc_RuntimeError, err_not_format_type); + return format_t::unknown; + } + + // Now check the member name. + + PyObject* format_s = PyObject_GetAttrString(format, "name"); // new reference + if (!format_s) + { + PyErr_SetString(PyExc_RuntimeError, err_not_format_type); + return format_t::unknown; + } + + // TODO : currently we only support csv format. Change this code when we + // add more format type(s) to support. + + const char* p = PyUnicode_AsUTF8(format_s); + if (!p || strncmp(p, "CSV", 3u) != 0) + { + PyErr_SetString(PyExc_RuntimeError, err_format_not_supported); + Py_DECREF(format_s); + return format_t::unknown; + } + + Py_DECREF(format_s); + return format_t::csv; +} + +bool sheet_write_as_csv(spreadsheet::sheet* sheet, PyObject* file) +{ + std::ostringstream os; + sheet->dump_csv(os); + std::string s = os.str(); + + if (!s.empty()) + { + PyObject* func_write = PyObject_GetAttrString(file, "write"); // new reference + if (!func_write) + { + PyErr_SetString(PyExc_RuntimeError, "'write' function was expected, but not found."); + return false; + } + + // write the content as python's utf-8 string ('s'). Use 'y' to write + // it as bytes (for future reference). + PyObject_CallFunction(func_write, "s", s.data(), nullptr); + Py_XDECREF(func_write); + } + + return true; +} + +} + +PyObject* sheet_write(PyObject* self, PyObject* args, PyObject* kwargs) +{ + static const char* kwlist[] = { "file", "format", nullptr }; + + PyObject* file = nullptr; + PyObject* format = nullptr; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO", const_cast<char**>(kwlist), &file, &format)) + return nullptr; + + format_t ft = to_format_type_enum(format); + + if (ft == format_t::unknown) + return nullptr; + + spreadsheet::sheet* sheet = get_core_sheet(self); + + switch (ft) + { + case format_t::csv: + { + if (!sheet_write_as_csv(sheet, file)) + return nullptr; + + break; + } + default: + { + PyErr_SetString(PyExc_RuntimeError, "this format is not yet supported."); + return nullptr; + } + } + + Py_INCREF(Py_None); + return Py_None; +} + +PyObject* sheet_get_named_expressions(PyObject* self, PyObject* /*args*/, PyObject* /*kwargs*/) +{ + const ss::document& doc = *t(self)->data->m_doc; + ss::sheet_t si = t(self)->data->m_sheet->get_index(); + const ixion::model_context& cxt = doc.get_model_context(); + return create_named_expressions_object(si, doc, cxt.get_named_expressions_iterator(si)); +} + +PyMethodDef tp_methods[] = +{ + { "get_rows", (PyCFunction)sheet_get_rows, METH_VARARGS, "Get a sheet row iterator." }, + { "write", (PyCFunction)sheet_write, METH_VARARGS | METH_KEYWORDS, "Write sheet content to specified file object." }, + { "get_named_expressions", (PyCFunction)sheet_get_named_expressions, METH_NOARGS, "Get a named expressions iterator." }, + { nullptr } +}; + +PyMemberDef tp_members[] = +{ + { (char*)"name", T_OBJECT_EX, offsetof(pyobj_sheet, name), READONLY, (char*)"sheet name" }, + { (char*)"sheet_size", T_OBJECT_EX, offsetof(pyobj_sheet, sheet_size), READONLY, (char*)"sheet size" }, + { (char*)"data_size", T_OBJECT_EX, offsetof(pyobj_sheet, data_size), READONLY, (char*)"data size" }, + { nullptr } +}; + +PyTypeObject sheet_type = +{ + PyVarObject_HEAD_INIT(nullptr, 0) + "orcus.Sheet", // tp_name + sizeof(pyobj_sheet), // tp_basicsize + 0, // tp_itemsize + (destructor)tp_dealloc, // tp_dealloc + 0, // tp_print + 0, // tp_getattr + 0, // tp_setattr + 0, // tp_compare + 0, // tp_repr + 0, // tp_as_number + 0, // tp_as_sequence + 0, // tp_as_mapping + 0, // tp_hash + 0, // tp_call + 0, // tp_str + 0, // tp_getattro + 0, // tp_setattro + 0, // tp_as_buffer + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, // tp_flags + "orcus sheet object", // tp_doc + 0, // tp_traverse + 0, // tp_clear + 0, // tp_richcompare + 0, // tp_weaklistoffset + 0, // tp_iter + 0, // tp_iternext + tp_methods, // tp_methods + tp_members, // tp_members + 0, // tp_getset + 0, // tp_base + 0, // tp_dict + 0, // tp_descr_get + 0, // tp_descr_set + 0, // tp_dictoffset + (initproc)tp_init, // tp_init + 0, // tp_alloc + tp_new, // tp_new +}; + +} + +sheet_data* get_sheet_data(PyObject* self) +{ + return reinterpret_cast<pyobj_sheet*>(self)->data; +} + +PyTypeObject* get_sheet_type() +{ + return &sheet_type; +} + +void store_sheet( + PyObject* self, const spreadsheet::document* doc, spreadsheet::sheet* orcus_sheet) +{ + pyobj_sheet* pysheet = reinterpret_cast<pyobj_sheet*>(self); + pysheet->data->m_doc = doc; + pysheet->data->m_sheet = orcus_sheet; + + // Populate the python members. + + // Sheet name + spreadsheet::sheet_t sid = orcus_sheet->get_index(); + std::string_view sheet_name = doc->get_sheet_name(sid); + pysheet->name = PyUnicode_FromStringAndSize(sheet_name.data(), sheet_name.size()); + + // Data size - size of the data area. + ixion::abs_range_t range = orcus_sheet->get_data_range(); + if (range.valid()) + { + pysheet->data_size = PyDict_New(); + PyDict_SetItemString(pysheet->data_size, "column", PyLong_FromLong(range.last.column+1)); + PyDict_SetItemString(pysheet->data_size, "row", PyLong_FromLong(range.last.row+1)); + } + else + { + Py_INCREF(Py_None); + pysheet->data_size = Py_None; + } + + // Sheet size - size of the entire sheet. + pysheet->sheet_size = PyDict_New(); + ss::range_size_t sheet_size = doc->get_sheet_size(); + PyDict_SetItemString(pysheet->sheet_size, "column", PyLong_FromLong(sheet_size.columns)); + PyDict_SetItemString(pysheet->sheet_size, "row", PyLong_FromLong(sheet_size.rows)); +} + +}} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/src/python/sheet.hpp b/src/python/sheet.hpp new file mode 100644 index 0000000..ef349ba --- /dev/null +++ b/src/python/sheet.hpp @@ -0,0 +1,43 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef INCLUDED_ORCUS_PYTHON_SHEET_HPP +#define INCLUDED_ORCUS_PYTHON_SHEET_HPP + +#include <Python.h> + +namespace orcus { + +namespace spreadsheet { + +class sheet; +class document; + +} + +namespace python { + +/** non-python part of the sheet object. */ +struct sheet_data +{ + const spreadsheet::document* m_doc; + spreadsheet::sheet* m_sheet; + + sheet_data(); + ~sheet_data(); +}; + +PyTypeObject* get_sheet_type(); + +void store_sheet( + PyObject* self, const spreadsheet::document* doc, spreadsheet::sheet* orcus_sheet); + +}} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/src/python/sheet_rows.cpp b/src/python/sheet_rows.cpp new file mode 100644 index 0000000..be49589 --- /dev/null +++ b/src/python/sheet_rows.cpp @@ -0,0 +1,209 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "sheet_rows.hpp" +#include "memory.hpp" +#include "cell.hpp" +#include "orcus/spreadsheet/sheet.hpp" +#include "orcus/spreadsheet/document.hpp" + +#include <ixion/model_context.hpp> + +namespace orcus { namespace python { + +sheet_rows_data::sheet_rows_data() : + m_doc(nullptr), + m_sheet(nullptr), + m_range(ixion::abs_range_t::invalid), + m_current_row(-1) {} + +sheet_rows_data::~sheet_rows_data() {} + +namespace { + +/** + * Python object for orcus.SheetRows. + */ +struct pyobj_sheet_rows +{ + PyObject_HEAD + + sheet_rows_data* m_data; +}; + +void sheet_rows_dealloc(pyobj_sheet_rows* self) +{ + delete self->m_data; + + Py_TYPE(self)->tp_free(reinterpret_cast<PyObject*>(self)); +} + +PyObject* sheet_rows_new(PyTypeObject* type, PyObject* /*args*/, PyObject* /*kwargs*/) +{ + pyobj_sheet_rows* self = (pyobj_sheet_rows*)type->tp_alloc(type, 0); + self->m_data = new sheet_rows_data; + return reinterpret_cast<PyObject*>(self); +} + +int sheet_rows_init(pyobj_sheet_rows* /*self*/, PyObject* /*args*/, PyObject* /*kwargs*/) +{ + return 0; +} + +PyObject* sheet_rows_iter(PyObject* self) +{ + sheet_rows_data* data = reinterpret_cast<pyobj_sheet_rows*>(self)->m_data; + + const ixion::abs_range_t& range = data->m_range; + if (range.valid()) + { + data->m_current_row = 0; + + ixion::abs_rc_range_t sheet_range; + sheet_range.first.column = 0; + sheet_range.first.row = 0; + sheet_range.last.column = range.last.column; + sheet_range.last.row = range.last.row; + + data->m_range_iterator = data->m_doc->get_model_context().get_model_iterator( + data->m_sheet->get_index(), ixion::rc_direction_t::horizontal, sheet_range); + } + + Py_INCREF(self); + return self; +} + +PyObject* sheet_rows_iternext(PyObject* self) +{ + sheet_rows_data* data = reinterpret_cast<pyobj_sheet_rows*>(self)->m_data; + ixion::model_iterator& iter = data->m_range_iterator; + + if (!iter.has()) + { + // No more elements. Stop the iteration. + PyErr_SetNone(PyExc_StopIteration); + return nullptr; + } + + PyObject* pyobj_row = PyTuple_New(data->m_range.last.column+1); + + for (; iter.has(); iter.next()) + { + const auto& cell = iter.get(); + if (cell.row != data->m_current_row) + { + ++data->m_current_row; + assert(cell.row == data->m_current_row); + break; + } + + PyObject* obj = nullptr; + switch (cell.type) + { + case ixion::celltype_t::empty: + { + obj = create_cell_object_empty(); + break; + } + case ixion::celltype_t::boolean: + { + obj = create_cell_object_boolean(std::get<bool>(cell.value)); + break; + } + case ixion::celltype_t::numeric: + { + obj = create_cell_object_numeric(std::get<double>(cell.value)); + break; + } + case ixion::celltype_t::string: + { + ixion::string_id_t sid = std::get<ixion::string_id_t>(cell.value); + const ixion::model_context& cxt = data->m_doc->get_model_context(); + const std::string* ps = cxt.get_string(sid); + obj = create_cell_object_string(ps); + break; + } + case ixion::celltype_t::formula: + { + const ixion::formula_cell* fc = std::get<const ixion::formula_cell*>(cell.value); + ixion::abs_address_t pos(data->m_sheet->get_index(), cell.row, cell.col); + obj = create_cell_object_formula(*data->m_doc, pos, fc); + break; + } + case ixion::celltype_t::unknown: + break; + } + + if (!obj) + return nullptr; + + PyTuple_SetItem(pyobj_row, cell.col, obj); + } + + return pyobj_row; +} + +PyTypeObject sheet_rows_type = +{ + PyVarObject_HEAD_INIT(nullptr, 0) + "orcus.SheetRows", // tp_name + sizeof(pyobj_sheet_rows), // tp_basicsize + 0, // tp_itemsize + (destructor)sheet_rows_dealloc, // tp_dealloc + 0, // tp_print + 0, // tp_getattr + 0, // tp_setattr + 0, // tp_compare + 0, // tp_repr + 0, // tp_as_number + 0, // tp_as_sequence + 0, // tp_as_mapping + 0, // tp_hash + 0, // tp_call + 0, // tp_str + 0, // tp_getattro + 0, // tp_setattro + 0, // tp_as_buffer + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, // tp_flags + "orcus sheet rows iterator", // tp_doc + 0, // tp_traverse + 0, // tp_clear + 0, // tp_richcompare + 0, // tp_weaklistoffset + (getiterfunc)sheet_rows_iter, // tp_iter + (iternextfunc)sheet_rows_iternext, // tp_iternext + 0, // tp_methods + 0, // tp_members + 0, // tp_getset + 0, // tp_base + 0, // tp_dict + 0, // tp_descr_get + 0, // tp_descr_set + 0, // tp_dictoffset + (initproc)sheet_rows_init, // tp_init + 0, // tp_alloc + sheet_rows_new, // tp_new +}; + +} + +PyTypeObject* get_sheet_rows_type() +{ + return &sheet_rows_type; +} + +void store_sheet_rows_data(PyObject* self, const spreadsheet::document* orcus_doc, const spreadsheet::sheet* orcus_sheet) +{ + sheet_rows_data* data = reinterpret_cast<pyobj_sheet_rows*>(self)->m_data; + data->m_doc = orcus_doc; + data->m_sheet = orcus_sheet; + data->m_range = orcus_sheet->get_data_range(); +} + +}} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/src/python/sheet_rows.hpp b/src/python/sheet_rows.hpp new file mode 100644 index 0000000..e750098 --- /dev/null +++ b/src/python/sheet_rows.hpp @@ -0,0 +1,54 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef INCLUDED_ORCUS_PYTHON_SHEET_ROWS_HPP +#define INCLUDED_ORCUS_PYTHON_SHEET_ROWS_HPP + +#include <Python.h> +#include <ixion/address.hpp> +#include <ixion/model_iterator.hpp> + +namespace ixion { + +class formula_name_resolver; + +} + +namespace orcus { + +namespace spreadsheet { + +class sheet; +class document; + +} + +namespace python { + +/** non-python part. */ +struct sheet_rows_data +{ + const spreadsheet::document* m_doc; + const spreadsheet::sheet* m_sheet; + ixion::abs_range_t m_range; + ixion::model_iterator m_range_iterator; + + ixion::row_t m_current_row; + + sheet_rows_data(); + ~sheet_rows_data(); +}; + +PyTypeObject* get_sheet_rows_type(); + +void store_sheet_rows_data(PyObject* self, const spreadsheet::document* orcus_doc, const spreadsheet::sheet* orcus_sheet); + +}} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/src/python/xls_xml.cpp b/src/python/xls_xml.cpp new file mode 100644 index 0000000..5caf747 --- /dev/null +++ b/src/python/xls_xml.cpp @@ -0,0 +1,58 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "xls_xml.hpp" +#include "global.hpp" + +#ifdef __ORCUS_PYTHON_XLS_XML +#include "document.hpp" +#include "orcus/orcus_xls_xml.hpp" +#include "orcus/spreadsheet/document.hpp" +#include "orcus/spreadsheet/factory.hpp" +#endif + +namespace orcus { namespace python { + +#ifdef __ORCUS_PYTHON_XLS_XML + +PyObject* xls_xml_read(PyObject* /*module*/, PyObject* args, PyObject* kwargs) +{ + stream_with_formulas data = read_stream_and_formula_params_from_args(args, kwargs); + if (!data.stream) + return nullptr; + + try + { + spreadsheet::range_size_t ss{1048576, 16384}; + std::unique_ptr<spreadsheet::document> doc = std::make_unique<spreadsheet::document>(ss); + spreadsheet::import_factory fact(*doc); + fact.set_recalc_formula_cells(data.recalc_formula_cells); + fact.set_formula_error_policy(data.error_policy); + orcus_xls_xml app(&fact); + + return import_from_stream_into_document(data.stream.get(), app, std::move(doc)); + } + catch (const std::exception& e) + { + set_python_exception(PyExc_RuntimeError, e); + return nullptr; + } +} + +#else + +PyObject* xls_xml_read(PyObject*, PyObject*, PyObject*) +{ + PyErr_SetString(PyExc_RuntimeError, "The xls-xml module is not enabled."); + return nullptr; +} + +#endif + +}} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/src/python/xls_xml.hpp b/src/python/xls_xml.hpp new file mode 100644 index 0000000..5639521 --- /dev/null +++ b/src/python/xls_xml.hpp @@ -0,0 +1,21 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef INCLUDED_ORCUS_PYTHON_XLS_XML_HPP +#define INCLUDED_ORCUS_PYTHON_XLS_XML_HPP + +#include <Python.h> + +namespace orcus { namespace python { + +PyObject* xls_xml_read(PyObject* module, PyObject* args, PyObject* kwargs); + +}} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/src/python/xlsx.cpp b/src/python/xlsx.cpp new file mode 100644 index 0000000..56cf47d --- /dev/null +++ b/src/python/xlsx.cpp @@ -0,0 +1,58 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "xlsx.hpp" +#include "global.hpp" + +#ifdef __ORCUS_PYTHON_XLSX +#include "document.hpp" +#include "orcus/orcus_xlsx.hpp" +#include "orcus/spreadsheet/document.hpp" +#include "orcus/spreadsheet/factory.hpp" +#endif + +namespace orcus { namespace python { + +#ifdef __ORCUS_PYTHON_XLSX + +PyObject* xlsx_read(PyObject* /*module*/, PyObject* args, PyObject* kwargs) +{ + stream_with_formulas data = read_stream_and_formula_params_from_args(args, kwargs); + if (!data.stream) + return nullptr; + + try + { + spreadsheet::range_size_t ss{1048576, 16384}; + std::unique_ptr<spreadsheet::document> doc = std::make_unique<spreadsheet::document>(ss); + spreadsheet::import_factory fact(*doc); + fact.set_recalc_formula_cells(data.recalc_formula_cells); + fact.set_formula_error_policy(data.error_policy); + orcus_xlsx app(&fact); + + return import_from_stream_into_document(data.stream.get(), app, std::move(doc)); + } + catch (const std::exception& e) + { + set_python_exception(PyExc_RuntimeError, e); + return nullptr; + } +} + +#else + +PyObject* xlsx_read(PyObject*, PyObject*, PyObject*) +{ + PyErr_SetString(PyExc_RuntimeError, "The xlsx module is not enabled."); + return nullptr; +} + +#endif + +}} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/src/python/xlsx.hpp b/src/python/xlsx.hpp new file mode 100644 index 0000000..b3d97e9 --- /dev/null +++ b/src/python/xlsx.hpp @@ -0,0 +1,21 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef INCLUDED_ORCUS_PYTHON_XLSX_HPP +#define INCLUDED_ORCUS_PYTHON_XLSX_HPP + +#include <Python.h> + +namespace orcus { namespace python { + +PyObject* xlsx_read(PyObject* module, PyObject* args, PyObject* kwargs); + +}} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |