diff options
-rw-r--r-- | .gitignore | 13 | ||||
-rw-r--r-- | .style.yapf | 5 | ||||
-rw-r--r-- | Makefile | 40 | ||||
-rw-r--r-- | Makefile.in | 56 | ||||
-rw-r--r-- | README | 63 | ||||
-rwxr-xr-x[-rw-r--r--] | bin/taler-merchant-survey (renamed from taler-merchant-survey.in) | 58 | ||||
-rwxr-xr-x | bootstrap | 12 | ||||
-rwxr-xr-x | configure | 87 | ||||
-rw-r--r-- | configure.ac | 79 | ||||
-rw-r--r-- | contrib/Doxyfile (renamed from Doxyfile) | 0 | ||||
-rw-r--r-- | frontend-survey.wsgi.in | 21 | ||||
-rw-r--r-- | m4/ax_compare_version.m4 | 177 | ||||
-rw-r--r-- | m4/python.m4 | 646 | ||||
-rwxr-xr-x | setup.py | 58 | ||||
-rw-r--r-- | talersurvey/survey/survey.py | 127 | ||||
-rw-r--r-- | talersurvey/survey/templates/index.html | 6 | ||||
-rw-r--r-- | talersurvey/survey/templates/show_tip.html | 35 | ||||
-rw-r--r-- | talersurvey/tests.py | 1 |
18 files changed, 380 insertions, 1104 deletions
@@ -1,16 +1,5 @@ .eggs/ *.pyc *.swp -Makefile -__pycache__/ -aclocal.m4 -autom4te.cache/ -config.log -config.status -configure -frontend-survey.wsgi -taler-merchant-survey +__pycache__ *.egg-info -compile -install-sh -missing diff --git a/.style.yapf b/.style.yapf new file mode 100644 index 0000000..3b39780 --- /dev/null +++ b/.style.yapf @@ -0,0 +1,5 @@ +[style] +based_on_style = pep8 +coalesce_brackets=True +column_limit=80 +dedent_closing_brackets=True diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..19de878 --- /dev/null +++ b/Makefile @@ -0,0 +1,40 @@ +install_global=false +-include config.mk + +.PHONY: all +all: + @echo "This is a python project, no compilation required" + +.PHONY: install + +ifeq ($(install_global), true) +install: install-global +else +install: install-local +endif + +.PHONY: install-global +install-global: + pip3 install . + +.PHONY: install-local +install-local: + pip3 install . --user + +# run testcases +.PHONY: check +check: + @export TALER_CONFIG_FILE=talersurvey/tests.conf; \ + python3 setup.py test + +.PHONY: clean +clean: + @echo nothing to do + +.PHONY: dist +dist: + git archive --format=tar.gz HEAD -o taler-merchant-survey.tar.gz + +.PHONY: pretty +pretty: + yapf -r -i talersurvey/ diff --git a/Makefile.in b/Makefile.in deleted file mode 100644 index 8f5ae5b..0000000 --- a/Makefile.in +++ /dev/null @@ -1,56 +0,0 @@ -INSTALL = install -INSTALL_PROGRAM = $(INSTALL) -INSTALL_DATA = $(INSTALL) -m 644 -prefix = @prefix@ -srcdir = @srcdir@ - -script_templates = taler-merchant-survey frontend-survey.wsgi -templates = Makefile $(script_templates) - -edit = sed -e 's|@prefix[@]|$(prefix)|g' - -.PHONY: all -all: $(templates) - cd talersurvey/survey/static/web-common && make && cd - - -Makefile: Makefile.in - ./config.status $@ - -$(script_templates): %: Makefile %.in - rm -f $@ $@.tmp - $(edit) '$(srcdir)/$@.in' >$@.tmp - mv $@.tmp $@ - - -.PHONY: install-data -install-data: $(templates) - @$(INSTALL_DATA) -Dt $(prefix)/share/taler/ frontend-survey.wsgi - -# @test -n "$$(ls -A talerbank/app/static/web-common/)" || \ -# (echo "please check out git submodules"; exit 1) - - - -# link package under prefix to source tree -.PHONY: devinstall -devinstall: $(templates) install-data - @pip3 install -e . --install-option="--prefix=@prefix@" - - -# install into prefix -.PHONY: install -install: $(templates) install-data - @pip3 install . @DEBIAN_PIP3_SYSTEM@ --install-option="--prefix=@prefix@" - @# force update when sources changed - @pip3 install . @DEBIAN_PIP3_SYSTEM@ --install-option="--prefix=@prefix@" --upgrade --no-deps - cd talersurvey/survey/static/web-common && make install && cd - - -# run testcases -.PHONY: check -check: - @export TALER_CONFIG_FILE=@abs_srcdir@/talersurvey/tests.conf; \ - export TALER_PREFIX=@prefix@; \ - python3 setup.py test - -pylint: - @pylint talersurvey/ @@ -0,0 +1,63 @@ +Installation instructions +------------------------- + +First, initialize the configure script. + +$ ./bootstrap + +Then, choose the installation prefix + +$ ./configure --prefix=<prefix> + +Last, install + +$ make install + +Optionally, to run tests: + +$ make check + +Configuration options. +---------------------- + +The following configuration excerpt shows all the options +accepted by the survey shop. + +$ cat ~/.config/taler.conf + +[frontends] + +# Merchant backend to use. +backend = http://backend.test.taler.net/ + +# This value will fill: "Authorization: ApiKey <fill here>" +backend_apikey = sandbox + +[survey] + +# The following three options configure the way the blog +# will serve UWSGI requests via unix domain sockets. +uwsgi_serve = unix +uwsgi_unixpath = /path/to/survey.uwsgi +uwsgi_unixpath_mode = 660 + +# The following option picks the merchant instance that +# will be represented by the running blog. +instance = default + +Running instructions +-------------------- + +The following command launches the blog to serve HTTP requests +on the port <port>. + +$ taler-merchant-survey serve-http -p <port> + +The following command launches the blog to serve UWSGI requests +via a unix domain socket. + +$ taler-merchant-survey serve-uwsgi + +Finally, get a help message with the following command + +$ taler-merchant-survey --help diff --git a/taler-merchant-survey.in b/bin/taler-merchant-survey index 254ff7f..3462ca5 100644..100755 --- a/taler-merchant-survey.in +++ b/bin/taler-merchant-survey @@ -28,19 +28,9 @@ import site import logging from taler.util.talerconfig import TalerConfig -os.environ.setdefault("TALER_PREFIX", "@prefix@") -site.addsitedir("%s/lib/python%d.%d/site-packages" % ( - "@prefix@", - sys.version_info.major, - sys.version_info.minor)) - - -## @cond -TC = TalerConfig.from_file(os.environ.get("TALER_CONFIG_FILE")) LOGGER = logging.getLogger(__name__) # No perfect match to our logging format, but good enough ... UWSGI_LOGFMT = "%(ltime) %(proto) %(method) %(uri) %(proto) => %(status)" -## @endcond ## @@ -52,13 +42,11 @@ def handle_serve_http(args): port = args.port if port is None: port = TC["survey"]["http_port"].value_int(required=True) - spec = ":%d" % (port,) - os.execlp("uwsgi", "uwsgi", - "--master", - "--die-on-term", - "--log-format", UWSGI_LOGFMT, - "--http", spec, - "--wsgi-file", "@prefix@/share/taler/frontend-survey.wsgi") + spec = ":%d" % (port, ) + os.execlp( + "uwsgi", "uwsgi", "--master", "--die-on-term", "--log-format", + UWSGI_LOGFMT, "--http", spec, "--module", "talersurvey.survey:app" + ) ## @@ -69,22 +57,22 @@ def handle_serve_http(args): # # @param command line options. def handle_serve_uwsgi(args): - del args # pacify PEP checkers - serve_uwsgi = TC["survey"]["uwsgi_serve"].value_string(required=True).lower() - params = ["uwsgi", "uwsgi", - "--master", - "--die-on-term", - "--log-format", UWSGI_LOGFMT, - "--wsgi-file", "@prefix@/share/taler/frontend-survey.wsgi"] + del args # pacify PEP checkers + serve_uwsgi = TC["survey"]["uwsgi_serve"].value_string(required=True + ).lower() + params = [ + "uwsgi", "uwsgi", "--master", "--die-on-term", "--log-format", + UWSGI_LOGFMT, "--module", "talersurvey.survey:app" + ] if serve_uwsgi == "tcp": port = TC["survey"]["uwsgi_port"].value_int(required=True) - spec = ":%d" % (port,) + spec = ":%d" % (port, ) params.extend(["--socket", spec]) elif serve_uwsgi == "unix": spec = TC["survey"]["uwsgi_unixpath"].value_filename(required=True) mode = TC["survey"]["uwsgi_unixpath_mode"].value_filename(required=True) params.extend(["--socket", spec]) - params.extend(["--chmod-socket="+mode]) + params.extend(["--chmod-socket=" + mode]) os.makedirs(os.path.dirname(spec), exist_ok=True) logging.info("launching uwsgi with argv %s", params[1:]) os.execlp(*params) @@ -93,14 +81,21 @@ def handle_serve_uwsgi(args): ## @cond PARSER = argparse.ArgumentParser() PARSER.set_defaults(func=None) -PARSER.add_argument('--config', '-c', - help="configuration file to use", - metavar="CONFIG", type=str, - dest="config", default=None) +PARSER.add_argument( + '--config', + '-c', + help="configuration file to use", + metavar="CONFIG", + type=str, + dest="config", + default=None +) SUB = PARSER.add_subparsers() P = SUB.add_parser('serve-http', help="Serve over HTTP") -P.add_argument("--port", "-p", dest="port", type=int, default=None, metavar="PORT") +P.add_argument( + "--port", "-p", dest="port", type=int, default=None, metavar="PORT" +) P.set_defaults(func=handle_serve_http) P = SUB.add_parser('serve-uwsgi', help="Serve over UWSGI") @@ -115,5 +110,6 @@ if getattr(ARGS, 'func', None) is None: if ARGS.config is not None: os.environ["TALER_CONFIG_FILE"] = ARGS.config +TC = TalerConfig.from_file(os.environ.get("TALER_CONFIG_FILE")) ARGS.func(ARGS) diff --git a/bootstrap b/bootstrap deleted file mode 100755 index 373e5f5..0000000 --- a/bootstrap +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/sh - -if ! git --version >/dev/null; then - echo "git not installed" - exit 1 -fi - -echo "$0: Updating submodules" -echo | git submodule update --init - -echo "$0: Running autoreconf" -autoreconf -if diff --git a/configure b/configure new file mode 100755 index 0000000..469bad7 --- /dev/null +++ b/configure @@ -0,0 +1,87 @@ +#!/usr/bin/env bash + +set -eu + +usage() { + echo "Usage: ./configure [OPTION]" + echo + echo "Configuration:" + echo " -h, --help display this help and exit" + echo + echo "Installation directories:" + echo " --destination=[local|global] install Python package locally or globally" +} + + +# -allow a command to fail with !’s side effect on errexit +# -use return value from ${PIPESTATUS[0]}, because ! hosed $? +! getopt --test > /dev/null +if [[ ${PIPESTATUS[0]} -ne 4 ]]; then + echo 'getopt not available' + exit 1 +fi + +LONGOPTS=destination:,help +OPTIONS=h + +! PARSED=$(getopt --options=$OPTIONS --longoptions=$LONGOPTS --name "$0" -- "$@") +if [[ ${PIPESTATUS[0]} -ne 0 ]]; then + # e.g. return value is 1 + # then getopt has complained about wrong arguments to stdout + exit 2 +fi + +# read getopt’s output this way to handle the quoting right: +eval set -- "$PARSED" + +destination="local" + +while true; do + case "$1" in + --destination) + destination="$2" + shift 2 + ;; + -h|--help) + usage + exit 1 + ;; + --) + shift + break + ;; + *) + echo "Programming error" + exit 3 + ;; + esac +done + +case "$destination" in + local) + install_global=false + ;; + global) + install_global=true + ;; + *) + echo "Destination (--destination) must be 'local' or 'global', paths are not allowed." + exit 3 + ;; +esac + +cat << EOF > config.mk +# this file is autogenerated by ./configure +install_global=$install_global +EOF + + +if ! python3 --version &>/dev/null; then + echo 'Error: python3 missing' + exit 1 +fi + +if ! pip3 --version &>/dev/null; then + echo 'Error: pip3 missing' + exit 1 +fi diff --git a/configure.ac b/configure.ac deleted file mode 100644 index 5e349d2..0000000 --- a/configure.ac +++ /dev/null @@ -1,79 +0,0 @@ -AC_INIT([talerfrontends], [0.1.0], [taler-bug@gnunet.org]) - -AC_CONFIG_MACRO_DIR([m4]) -AM_INIT_AUTOMAKE -AC_PROG_AWK -AC_PROG_SED - -# -# Check for Python -# - -PC_INIT([3.4]) -pyheaders=0 -PC_PYTHON_CHECK_HEADERS([pyheaders=1]) -PC_PYTHON_CHECK_VERSION() - -# -# Check for pip3 -# - -AC_MSG_CHECKING([pip3]) -pip3 --version >/dev/null -if test $? -ne 0; - then - AC_MSG_ERROR([Please install pip3>=6.0]) -fi - -# On Debian systems, we may need to pass "--system" to pip3 to get -# to the desired installation target directory -pip3 install --help | grep '\-\-system' >> /dev/null -if test $? -ne 0; -then - DEBIAN_PIP3_SYSTEM="" -else - DEBIAN_PIP3_SYSTEM="--system" -fi -AC_SUBST(DEBIAN_PIP3_SYSTEM) - - - -VERSION=$(pip3 --version | $AWK '{ print $2 }') - -AC_MSG_RESULT([$VERSION]) - -AX_COMPARE_VERSION([$VERSION],[lt],[6.0], [AC_MSG_ERROR([Please install pip3>=6.0])]) - - -# -# Check for tsc -# -AC_CHECK_PROG([tsc],[tsc],[yes],[no]) -AM_CONDITIONAL([HAVE_TSC], [test "x$tsc" = xyes]) - -# -# Check for minifier -# -AC_MSG_CHECKING([Checking for jsmin]) -python3 -m jsmin &> /dev/null -if test $? -ne 0; - then - AC_MSG_ERROR([Please install Python3 module 'jsmin']) -fi - -# -# Report -# -if test x$pyheaders != x1; then - AC_MSG_WARN([Python headers not installed, might be required to build uwsgi]) -fi - - -# -# Finish -# - -AC_CONFIG_FILES([Makefile - talersurvey/survey/static/web-common/Makefile]) - -AC_OUTPUT diff --git a/Doxyfile b/contrib/Doxyfile index 518c651..518c651 100644 --- a/Doxyfile +++ b/contrib/Doxyfile diff --git a/frontend-survey.wsgi.in b/frontend-survey.wsgi.in deleted file mode 100644 index 75b3ffe..0000000 --- a/frontend-survey.wsgi.in +++ /dev/null @@ -1,21 +0,0 @@ -import sys - -if sys.version_info.major < 3: - print("The taler bank needs to run with Python>=3.4") - sys.exit(1) - -import site -import os -import logging - -logging.basicConfig(level=logging.INFO) - -os.environ.setdefault("TALER_PREFIX", "@prefix@") -site.addsitedir("%s/lib/python%d.%d/site-packages" % ( - "@prefix@", - sys.version_info.major, - sys.version_info.minor)) - -import talersurvey.survey - -application = talersurvey.survey.app diff --git a/m4/ax_compare_version.m4 b/m4/ax_compare_version.m4 deleted file mode 100644 index 74dc0fd..0000000 --- a/m4/ax_compare_version.m4 +++ /dev/null @@ -1,177 +0,0 @@ -# =========================================================================== -# http://www.gnu.org/software/autoconf-archive/ax_compare_version.html -# =========================================================================== -# -# SYNOPSIS -# -# AX_COMPARE_VERSION(VERSION_A, OP, VERSION_B, [ACTION-IF-TRUE], [ACTION-IF-FALSE]) -# -# DESCRIPTION -# -# This macro compares two version strings. Due to the various number of -# minor-version numbers that can exist, and the fact that string -# comparisons are not compatible with numeric comparisons, this is not -# necessarily trivial to do in a autoconf script. This macro makes doing -# these comparisons easy. -# -# The six basic comparisons are available, as well as checking equality -# limited to a certain number of minor-version levels. -# -# The operator OP determines what type of comparison to do, and can be one -# of: -# -# eq - equal (test A == B) -# ne - not equal (test A != B) -# le - less than or equal (test A <= B) -# ge - greater than or equal (test A >= B) -# lt - less than (test A < B) -# gt - greater than (test A > B) -# -# Additionally, the eq and ne operator can have a number after it to limit -# the test to that number of minor versions. -# -# eq0 - equal up to the length of the shorter version -# ne0 - not equal up to the length of the shorter version -# eqN - equal up to N sub-version levels -# neN - not equal up to N sub-version levels -# -# When the condition is true, shell commands ACTION-IF-TRUE are run, -# otherwise shell commands ACTION-IF-FALSE are run. The environment -# variable 'ax_compare_version' is always set to either 'true' or 'false' -# as well. -# -# Examples: -# -# AX_COMPARE_VERSION([3.15.7],[lt],[3.15.8]) -# AX_COMPARE_VERSION([3.15],[lt],[3.15.8]) -# -# would both be true. -# -# AX_COMPARE_VERSION([3.15.7],[eq],[3.15.8]) -# AX_COMPARE_VERSION([3.15],[gt],[3.15.8]) -# -# would both be false. -# -# AX_COMPARE_VERSION([3.15.7],[eq2],[3.15.8]) -# -# would be true because it is only comparing two minor versions. -# -# AX_COMPARE_VERSION([3.15.7],[eq0],[3.15]) -# -# would be true because it is only comparing the lesser number of minor -# versions of the two values. -# -# Note: The characters that separate the version numbers do not matter. An -# empty string is the same as version 0. OP is evaluated by autoconf, not -# configure, so must be a string, not a variable. -# -# The author would like to acknowledge Guido Draheim whose advice about -# the m4_case and m4_ifvaln functions make this macro only include the -# portions necessary to perform the specific comparison specified by the -# OP argument in the final configure script. -# -# LICENSE -# -# Copyright (c) 2008 Tim Toolan <toolan@ele.uri.edu> -# -# Copying and distribution of this file, with or without modification, are -# permitted in any medium without royalty provided the copyright notice -# and this notice are preserved. This file is offered as-is, without any -# warranty. - -#serial 11 - -dnl ######################################################################### -AC_DEFUN([AX_COMPARE_VERSION], [ - AC_REQUIRE([AC_PROG_AWK]) - - # Used to indicate true or false condition - ax_compare_version=false - - # Convert the two version strings to be compared into a format that - # allows a simple string comparison. The end result is that a version - # string of the form 1.12.5-r617 will be converted to the form - # 0001001200050617. In other words, each number is zero padded to four - # digits, and non digits are removed. - AS_VAR_PUSHDEF([A],[ax_compare_version_A]) - A=`echo "$1" | sed -e 's/\([[0-9]]*\)/Z\1Z/g' \ - -e 's/Z\([[0-9]]\)Z/Z0\1Z/g' \ - -e 's/Z\([[0-9]][[0-9]]\)Z/Z0\1Z/g' \ - -e 's/Z\([[0-9]][[0-9]][[0-9]]\)Z/Z0\1Z/g' \ - -e 's/[[^0-9]]//g'` - - AS_VAR_PUSHDEF([B],[ax_compare_version_B]) - B=`echo "$3" | sed -e 's/\([[0-9]]*\)/Z\1Z/g' \ - -e 's/Z\([[0-9]]\)Z/Z0\1Z/g' \ - -e 's/Z\([[0-9]][[0-9]]\)Z/Z0\1Z/g' \ - -e 's/Z\([[0-9]][[0-9]][[0-9]]\)Z/Z0\1Z/g' \ - -e 's/[[^0-9]]//g'` - - dnl # In the case of le, ge, lt, and gt, the strings are sorted as necessary - dnl # then the first line is used to determine if the condition is true. - dnl # The sed right after the echo is to remove any indented white space. - m4_case(m4_tolower($2), - [lt],[ - ax_compare_version=`echo "x$A -x$B" | sed 's/^ *//' | sort -r | sed "s/x${A}/false/;s/x${B}/true/;1q"` - ], - [gt],[ - ax_compare_version=`echo "x$A -x$B" | sed 's/^ *//' | sort | sed "s/x${A}/false/;s/x${B}/true/;1q"` - ], - [le],[ - ax_compare_version=`echo "x$A -x$B" | sed 's/^ *//' | sort | sed "s/x${A}/true/;s/x${B}/false/;1q"` - ], - [ge],[ - ax_compare_version=`echo "x$A -x$B" | sed 's/^ *//' | sort -r | sed "s/x${A}/true/;s/x${B}/false/;1q"` - ],[ - dnl Split the operator from the subversion count if present. - m4_bmatch(m4_substr($2,2), - [0],[ - # A count of zero means use the length of the shorter version. - # Determine the number of characters in A and B. - ax_compare_version_len_A=`echo "$A" | $AWK '{print(length)}'` - ax_compare_version_len_B=`echo "$B" | $AWK '{print(length)}'` - - # Set A to no more than B's length and B to no more than A's length. - A=`echo "$A" | sed "s/\(.\{$ax_compare_version_len_B\}\).*/\1/"` - B=`echo "$B" | sed "s/\(.\{$ax_compare_version_len_A\}\).*/\1/"` - ], - [[0-9]+],[ - # A count greater than zero means use only that many subversions - A=`echo "$A" | sed "s/\(\([[0-9]]\{4\}\)\{m4_substr($2,2)\}\).*/\1/"` - B=`echo "$B" | sed "s/\(\([[0-9]]\{4\}\)\{m4_substr($2,2)\}\).*/\1/"` - ], - [.+],[ - AC_WARNING( - [illegal OP numeric parameter: $2]) - ],[]) - - # Pad zeros at end of numbers to make same length. - ax_compare_version_tmp_A="$A`echo $B | sed 's/./0/g'`" - B="$B`echo $A | sed 's/./0/g'`" - A="$ax_compare_version_tmp_A" - - # Check for equality or inequality as necessary. - m4_case(m4_tolower(m4_substr($2,0,2)), - [eq],[ - test "x$A" = "x$B" && ax_compare_version=true - ], - [ne],[ - test "x$A" != "x$B" && ax_compare_version=true - ],[ - AC_WARNING([illegal OP parameter: $2]) - ]) - ]) - - AS_VAR_POPDEF([A])dnl - AS_VAR_POPDEF([B])dnl - - dnl # Execute ACTION-IF-TRUE / ACTION-IF-FALSE. - if test "$ax_compare_version" = "true" ; then - m4_ifvaln([$4],[$4],[:])dnl - m4_ifvaln([$5],[else $5])dnl - fi -]) dnl AX_COMPARE_VERSION diff --git a/m4/python.m4 b/m4/python.m4 deleted file mode 100644 index 98a68b8..0000000 --- a/m4/python.m4 +++ /dev/null @@ -1,646 +0,0 @@ -# Copyright 2012, 2013, 2014 Brandon Invergo <brandon@invergo.net> -# -# This file is part of pyconfigure. This program is free -# software; you can redistribute it and/or modify it under the -# terms of the GNU General Public License as published by the -# Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# Under Section 7 of GPL version 3, you are granted additional -# permissions described in the Autoconf Configure Script Exception, -# version 3.0, as published by the Free Software Foundation. -# -# You should have received a copy of the GNU General Public License -# and a copy of the Autoconf Configure Script Exception along with -# this program; see the files COPYINGv3 and COPYING.EXCEPTION -# respectively. If not, see <http://www.gnu.org/licenses/>. - - -# Many of these macros were adapted from ones written by Andrew Dalke -# and James Henstridge and are included with the Automake utility -# under the following copyright terms: -# -# Copyright (C) 1999-2012 Free Software Foundation, Inc. -# -# This file 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. - -# Table of Contents: -# -# 1. Language selection -# and routines to produce programs in a given language. -# -# 2. Producing programs in a given language. -# -# 3. Looking for a compiler -# And possibly the associated preprocessor. -# -# 4. Looking for specific libs & functionality - - -## ----------------------- ## -## 1. Language selection. ## -## ----------------------- ## - - -# AC_LANG(Python) -# --------------- -AC_LANG_DEFINE([Python], [py], [PY], [PYTHON], [], -[ac_ext=py -ac_compile='chmod +x conftest.$ac_ext >&AS_MESSAGE_LOG_FD' -ac_link='chmod +x conftest.$ac_ext && cp conftest.$ac_ext conftest >&AS_MESSAGE_LOG_FD' -]) - - -# AC_LANG_PYTHON -# -------------- -AU_DEFUN([AC_LANG_PYTHON], [AC_LANG(Python)]) - - -## ----------------------- ## -## 2. Producing programs. ## -## ----------------------- ## - - -# AC_LANG_PROGRAM(Python)([PROLOGUE], [BODY]) -# ------------------------------------------- -m4_define([AC_LANG_PROGRAM(Python)], [dnl -@%:@!$PYTHON -$1 -m4_if([$2], [], [], [dnl -if __name__ == '__main__': -$2])]) - - -# _AC_LANG_IO_PROGRAM(Python) -# --------------------------- -# Produce source that performs I/O. -m4_define([_AC_LANG_IO_PROGRAM(Python)], -[AC_LANG_PROGRAM([dnl -import sys -try: - h = open('conftest.out') -except: - sys.exit(1) -else: - close(h) - sys.exit(0) -], [])]) - - -# _AC_LANG_CALL(Python)([PROLOGUE], [FUNCTION]) -# --------------------- -# Produce source that calls FUNCTION -m4_define([_AC_LANG_CALL(Python)], -[AC_LANG_PROGRAM([$1], [$2])]) - - -## -------------------------------------------- ## -## 3. Looking for Compilers and Interpreters. ## -## -------------------------------------------- ## - - -AC_DEFUN([AC_LANG_COMPILER(Python)], -[AC_REQUIRE([PC_PROG_PYTHON])]) - - -# PC_INIT([MIN-VERSION], [MAX-VERSION]) -# ----------------------------- -# Initialize pyconfigure, finding a Python interpreter with a given -# minimum and/or maximum version. -AC_DEFUN([PC_INIT], -[PC_PROG_PYTHON([], [$1], [$2]) -dnl If we found something, do a sanity check that the interpreter really -dnl has the version its name would suggest. -m4_ifval([PYTHON], - [PC_PYTHON_VERIFY_VERSION([>=], [pc_min_ver], [], - [AC_MSG_FAILURE([No compatible Python interpreter found. If you're sure that you have one, try setting the PYTHON environment variable to the location of the interpreter.])])]) -m4_ifval([PYTHON], - [PC_PYTHON_VERIFY_VERSION([<=], [pc_max_ver], [], - [AC_MSG_FAILURE([No compatible Python interpreter found. If you're sure that you have one, try setting the PYTHON environment variable to the location of the interpreter.])])]) -])# PC_INIT - -# PC_PROG_PYTHON([PROG-TO-CHECK-FOR], [MIN-VERSION], [MAX-VERSION]) -# --------------------------------- -# Find a Python interpreter. Python versions prior to 2.0 are not -# supported. (2.0 was released on October 16, 2000). -AC_DEFUN_ONCE([PC_PROG_PYTHON], -[AC_ARG_VAR([PYTHON], [the Python interpreter]) -dnl The default minimum version is 2.0 -m4_define_default([pc_min_ver], m4_ifval([$2], [$2], [2.0])) -dnl The default maximum version is 3.3 -m4_define_default([pc_max_ver], m4_ifval([$3], [$3], [4.0])) -dnl Build up a list of possible interpreter names. -m4_define_default([_PC_PYTHON_INTERPRETER_LIST], - [dnl If we want some Python 3 versions (max version >= 3.0), -dnl also search for "python3" - m4_if(m4_version_compare(pc_max_ver, [2.9]), [1], [python3], []) \ -dnl If we want some Python 2 versions (min version <= 2.7), -dnl also search for "python2". - m4_if(m4_version_compare(pc_min_ver, [2.8]), [-1], [python2], []) \ -dnl Construct a comma-separated list of interpreter names (python2.6, -dnl python2.7, etc). We only care about the first 3 characters of the -dnl version strings (major-dot-minor; not -dnl major-dot-minor-dot-bugfix[-dot-whatever]) - m4_foreach([pc_ver], - m4_esyscmd_s(seq -s[[", "]] -f["[[%.1f]]"] m4_substr(pc_max_ver, [0], [3]) -0.1 m4_substr(pc_min_ver, [0], [3])), -dnl Remove python2.8 and python2.9 since they will never exist - [m4_bmatch(pc_ver, [2.[89]], [], [python]pc_ver)]) \ - [python]]) -dnl Do the actual search at last. -m4_ifval([$1], - [AC_PATH_PROGS(PYTHON, [$1 _PC_PYTHON_INTERPRETER_LIST])], - [AC_PATH_PROGS(PYTHON, [_PC_PYTHON_INTERPRETER_LIST])]) -])# PC_PROG_PYTHON - - -# PC_PYTHON_PROG_PYTHON_CONFIG(PROG-TO-CHECK-FOR) -# ---------------------------------------------- -# Find the python-config program -AC_DEFUN([PC_PYTHON_PROG_PYTHON_CONFIG], -[AC_REQUIRE([PC_PROG_PYTHON])[]dnl -AC_ARG_VAR([PYTHON_CONFIG], [the Python-config program]) -dnl python-config's binary name is normally based on the Python interpreter's -dnl binary name (i.e. python2.7 -> python2.7-config) -m4_define([_PYTHON_BASENAME], [`basename $PYTHON`]) -m4_ifval([$1], - [AC_PATH_PROGS(PYTHON_CONFIG, [$1 _PYTHON_BASENAME-config])], - [AC_PATH_PROG(PYTHON_CONFIG, _PYTHON_BASENAME-config)]) -]) # PC_PYTHON_PROG_PYTHON_CONFIG - - -# PC_PYTHON_VERIFY_VERSION([RELATION], [VERSION], [ACTION-IF-TRUE], [ACTION-IF-FALSE]) -# --------------------------------------------------------------------------- -# Run ACTION-IF-TRUE if the Python interpreter PROG has version [RELATION] VERSION. -# i.e if RELATION is "<", check if PROG has a version number less than VERSION. -# Run ACTION-IF-FALSE otherwise. -# Specify RELATION as any mathematical comparison "<", ">", "<=", ">=", "==" or "!=" -# This test uses sys.hexversion instead of the string equivalent (first -# word of sys.version), in order to cope with versions such as 2.2c1. -# This supports Python 2.0 or higher. (2.0 was released on October 16, 2000). -AC_DEFUN([PC_PYTHON_VERIFY_VERSION], -[m4_define([pc_python_safe_ver], m4_bpatsubsts($2, [\.], [_])) -AC_CACHE_CHECK([if Python $1 '$2'], - [[pc_cv_python_req_version_]pc_python_safe_ver], - [AC_LANG_PUSH(Python)[]dnl - AC_RUN_IFELSE( - [AC_LANG_PROGRAM([dnl -import sys -], [dnl - # split strings by '.' and convert to numeric. Append some zeros - # because we need at least 4 digits for the hex conversion. - # map returns an iterator in Python 3.0 and a list in 2.x - reqver = list(map(int, '$2'.split('.'))) + [[0, 0, 0]] - reqverhex = 0 - # xrange is not present in Python 3.0 and range returns an iterator - for i in list(range(4)): - reqverhex = (reqverhex << 8) + reqver[[i]] - # the final 8 bits are "0xf0" for final versions, which are all - # we'll test against, since it's doubtful that a released software - # will depend on an alpha- or beta-state Python. - reqverhex += 0xf0 - if sys.hexversion $1 reqverhex: - sys.exit() - else: - sys.exit(1) -])], - [[pc_cv_python_req_version_]pc_python_safe_ver=yes], - [[pc_cv_python_req_version_]pc_python_safe_ver=no]) - AC_LANG_POP(Python)[]dnl - ]) -AS_IF([test "$[pc_cv_python_req_version_]pc_python_safe_ver" = "no"], [$4], [$3]) -])# PC_PYTHON_VERIFY_VERSION - - -# PC_PYTHON_CHECK_VERSION -# ----------------------- -# Query Python for its version number. Getting [:3] seems to be -# the best way to do this; it's what "site.py" does in the standard -# library. -AC_DEFUN([PC_PYTHON_CHECK_VERSION], -[AC_REQUIRE([PC_PROG_PYTHON])[]dnl -AC_CACHE_CHECK([for $1 version], - [pc_cv_python_version], - [AC_LANG_PUSH(Python)[]dnl - AC_LINK_IFELSE([AC_LANG_PROGRAM([dnl -import sys -], [dnl - sys.stdout.write(sys.version[[:3]]) -])], - [pc_cv_python_version=`./conftest`], - [AC_MSG_FAILURE([failed to run Python program])]) - AC_LANG_POP(Python)[]dnl - ]) -AC_SUBST([PYTHON_VERSION], [$pc_cv_python_version]) -])# PC_PYTHON_CHECK_VERSION - - -# PC_PYTHON_CHECK_PREFIX -# ---------------------- -# Use the value of $prefix for the corresponding value of -# PYTHON_PREFIX. This is made a distinct variable so it can be -# overridden if need be. However, general consensus is that you -# shouldn't need this ability. -AC_DEFUN([PC_PYTHON_CHECK_PREFIX], -[AC_REQUIRE([PC_PYTHON_PROG_PYTHON_CONFIG])[]dnl -dnl Try to get it with python-config otherwise do it from within Python -AC_CACHE_CHECK([for Python prefix], [pc_cv_python_prefix], -[if test -x "$PYTHON_CONFIG"; then - pc_cv_python_prefix=`$PYTHON_CONFIG --prefix 2>&AS_MESSAGE_LOG_FD` -else - AC_LANG_PUSH(Python)[]dnl - AC_LINK_IFELSE([AC_LANG_PROGRAM([dnl -import sys -], [dnl - sys.stdout.write(sys.prefix) -])], [pc_cv_python_prefix=`./conftest`; - if test $? != 0; then - AC_MSG_FAILURE([could not determine Python prefix]) - fi], - [AC_MSG_FAILURE([failed to run Python program])]) - AC_LANG_POP(Python)[]dnl -fi]) -AC_SUBST([PYTHON_PREFIX], [$pc_cv_python_prefix])]) - - -# PC_PYTHON_CHECK_EXEC_PREFIX -# -------------------------- -# Like above, but for $exec_prefix -AC_DEFUN([PC_PYTHON_CHECK_EXEC_PREFIX], -[AC_REQUIRE([PC_PYTHON_PROG_PYTHON_CONFIG])[]dnl -dnl Try to get it with python-config otherwise do it from within Python -AC_CACHE_CHECK([for Python exec-prefix], [pc_cv_python_exec_prefix], -[if test -x "$PYTHON_CONFIG"; then - pc_cv_python_exec_prefix=`$PYTHON_CONFIG --exec-prefix 2>&AS_MESSAGE_LOG_FD` -else - AC_LANG_PUSH(Python)[]dnl - AC_LINK_IFELSE([AC_LANG_PROGRAM([dnl -import sys -], [dnl - sys.stdout.write(sys.exec_prefix) -])], - [pc_cv_python_exec_prefix=`./conftest`; - if test $? != 0; then - AC_MSG_FAILURE([could not determine Python exec_prefix]) - fi], - [AC_MSG_FAILURE([failed to run Python program])]) - AC_LANG_POP(Python)[]dnl -fi -]) -AC_SUBST([PYTHON_EXEC_PREFIX], [$pc_cv_python_exec_prefix])]) - - -# PC_PYTHON_CHECK_INCLUDES -# ------------------------ -# Find the Python header file include flags (ie -# '-I/usr/include/python') -AC_DEFUN([PC_PYTHON_CHECK_INCLUDES], -[AC_REQUIRE([PC_PYTHON_PROG_PYTHON_CONFIG])[]dnl -dnl Try to find the headers location with python-config otherwise guess -AC_CACHE_CHECK([for Python includes], [pc_cv_python_includes], -[if test -x "$PYTHON_CONFIG"; then - pc_cv_python_includes=`$PYTHON_CONFIG --includes 2>&AS_MESSAGE_LOG_FD` -else - pc_cv_python_includes="[-I$includedir/$_PYTHON_BASENAME]m4_ifdef(PYTHON_ABI_FLAGS, - PYTHON_ABI_FLAGS,)" -fi -]) -AC_SUBST([PYTHON_INCLUDES], [$pc_cv_python_includes])]) - - -# PC_PYTHON_CHECK_HEADERS([ACTION-IF-PRESENT], [ACTION-IF-ABSENT]) -# ----------------------- -# Check for the presence and usability of Python.h -AC_DEFUN([PC_PYTHON_CHECK_HEADERS], -[AC_REQUIRE([PC_PYTHON_CHECK_INCLUDES])[]dnl -pc_cflags_store=$CPPFLAGS -CPPFLAGS="$CFLAGS $PYTHON_INCLUDES" -AC_CHECK_HEADER([Python.h], [$1], [$2]) -CPPFLAGS=$pc_cflags_store -]) - - -# PC_PYTHON_CHECK_LIBS -# -------------------- -# Find the Python lib flags (ie '-lpython') -AC_DEFUN([PC_PYTHON_CHECK_LIBS], -[AC_REQUIRE([PC_PYTHON_PROG_PYTHON_CONFIG])[]dnl -dnl Try to find the lib flags with python-config otherwise guess -AC_CACHE_CHECK([for Python libs], [pc_cv_python_libs], -[if test -x "$PYTHON_CONFIG"; then - pc_cv_python_libs=`$PYTHON_CONFIG --libs 2>&AS_MESSAGE_LOG_FD` -else - pc_cv_python_libs="[-l$_PYTHON_BASENAME]m4_ifdef(PYTHON_ABI_FLAGS, PYTHON_ABI_FLAGS,)" -fi -]) -AC_SUBST([PYTHON_LIBS], [$pc_cv_python_libs])]) - - -# PC_PYTHON_TEST_LIBS(LIBRARY-FUNCTION, [ACTION-IF-PRESENT], [ACTION-IF-ABSENT]) -# ------------------- -# Verify that the Python libs can be loaded -AC_DEFUN([PC_PYTHON_TEST_LIBS], -[AC_REQUIRE([PC_PYTHON_CHECK_LIBS])[]dnl -pc_libflags_store=$LIBS -for lflag in $PYTHON_LIBS; do - case $lflag in - -lpython*@:}@ - LIBS="$LIBS $lflag" - pc_libpython=`echo $lflag | sed -e 's/^-l//'` - ;; - *@:}@;; - esac -done -AC_CHECK_LIB([$pc_libpython], [$1], [$2], [$3])]) - - -# PC_PYTHON_CHECK_CFLAGS -# ---------------------- -# Find the Python CFLAGS -AC_DEFUN([PC_PYTHON_CHECK_CFLAGS], -[AC_REQUIRE([PC_PYTHON_PROG_PYTHON_CONFIG])[]dnl -dnl Try to find the CFLAGS with python-config otherwise give up -AC_CACHE_CHECK([for Python CFLAGS], [pc_cv_python_cflags], -[if test -x "$PYTHON_CONFIG"; then - pc_cv_python_cflags=`$PYTHON_CONFIG --cflags 2>&AS_MESSAGE_LOG_FD` -else - pc_cv_python_cflags= -fi -]) -AC_SUBST([PYTHON_CFLAGS], [$pc_cv_python_cflags])]) - - -# PC_PYTHON_CHECK_LDFLAGS -# ----------------------- -# Find the Python LDFLAGS -AC_DEFUN([PC_PYTHON_CHECK_LDFLAGS], -[AC_REQUIRE([PC_PYTHON_PROG_PYTHON_CONFIG])[]dnl -dnl Try to find the LDFLAGS with python-config otherwise give up -AC_CACHE_CHECK([for Python LDFLAGS], [pc_cv_python_ldflags], -[if test -x "$PYTHON_CONFIG"; then - pc_cv_python_ldflags=`$PYTHON_CONFIG --ldflags 2>&AS_MESSAGE_LOG_FD` -else - pc_cv_python_ldflags= -fi -]) -AC_SUBST([PYTHON_LDFLAGS], [$pc_cv_python_ldflags])]) - - -# PC_PYTHON_CHECK_EXTENSION_SUFFIX -# -------------------------------- -# Find the Python extension suffix (i.e. '.cpython-32.so') -AC_DEFUN([PC_PYTHON_CHECK_EXTENSION_SUFFIX], -[AC_REQUIRE([PC_PYTHON_PROG_PYTHON_CONFIG])[]dnl -dnl Try to find the suffix with python-config otherwise give up -AC_CACHE_CHECK([for Python extension suffix], [pc_cv_python_extension_suffix], -[if test -x "$PYTHON_CONFIG"; then - pc_cv_python_extension_suffix=`$PYTHON_CONFIG --extension-suffix 2>&AS_MESSAGE_LOG_FD` -else - pc_cv_python_extension_suffix= -fi -]) -AC_SUBST([PYTHON_EXTENSION_SUFFIX], [$pc_cv_python_extension_suffix])]) - - -# PC_PYTHON_CHECK_ABI_FLAGS -# ------------------------- -# Find the Python ABI flags -AC_DEFUN([PC_PYTHON_CHECK_ABI_FLAGS], -[AC_REQUIRE([PC_PYTHON_PROG_PYTHON_CONFIG])[]dnl -dnl Try to find the ABI flags with python-config otherwise give up -AC_CACHE_CHECK([for Python ABI flags], [pc_cv_python_abi_flags], -[if test -x "$PYTHON_CONFIG"; then - pc_cv_python_abi_flags=`$PYTHON_CONFIG --abiflags 2>&AS_MESSAGE_LOG_FD` -else - pc_cv_python_abi_flags= -fi -]) -AC_SUBST([PYTHON_ABI_FLAGS], [$pc_cv_python_abi_flags])]) - - -# PC_PYTHON_CHECK_PLATFORM -# ------------------------ -# At times (like when building shared libraries) you may want -# to know which OS platform Python thinks this is. -AC_DEFUN([PC_PYTHON_CHECK_PLATFORM], -[AC_REQUIRE([PC_PROG_PYTHON])[]dnl -dnl Get the platform from within Python (sys.platform) -AC_CACHE_CHECK([for Python platform], [pc_cv_python_platform], - [AC_LANG_PUSH(Python)[]dnl - AC_LINK_IFELSE([AC_LANG_PROGRAM([dnl -import sys -], [dnl - sys.stdout.write(sys.platform) -])], [pc_cv_python_platform=`./conftest`; - if test $? != 0; then - AC_MSG_FAILURE([could not determine Python platform]) - fi], - [AC_MSG_FAILURE([failed to run Python program])]) - AC_LANG_POP(Python)[]dnl - ]) -AC_SUBST([PYTHON_PLATFORM], [$pc_cv_python_platform]) -]) - - -# PC_PYTHON_CHECK_SITE_DIR -# --------------------- -# The directory to which new libraries are installed (i.e. the -# "site-packages" directory. -AC_DEFUN([PC_PYTHON_CHECK_SITE_DIR], -[AC_REQUIRE([PC_PROG_PYTHON])AC_REQUIRE([PC_PYTHON_CHECK_PREFIX])[]dnl -AC_CACHE_CHECK([for Python site-packages directory], - [pc_cv_python_site_dir], - [AC_LANG_PUSH(Python)[]dnl - if test "x$prefix" = xNONE - then - pc_py_prefix=$ac_default_prefix - else - pc_py_prefix=$prefix - fi - AC_LINK_IFELSE([AC_LANG_PROGRAM([dnl -import sys -from platform import python_implementation -# sysconfig in CPython 2.7 doesn't work in virtualenv -# <https://github.com/pypa/virtualenv/issues/118> -try: - import sysconfig -except: - can_use_sysconfig = False -else: - can_use_sysconfig = True -if can_use_sysconfig: - if python_implementation() == "CPython" and sys.version[[:3]] == '2.7': - can_use_sysconfig = False -if not can_use_sysconfig: - from distutils import sysconfig - sitedir = sysconfig.get_python_lib(False, False, prefix='$pc_py_prefix') -else: - sitedir = sysconfig.get_path('purelib', vars={'base':'$pc_py_prefix'}) -], [dnl - sys.stdout.write(sitedir) -])], [pc_cv_python_site_dir=`./conftest`], - [AC_MSG_FAILURE([failed to run Python program])]) - AC_LANG_POP(Python)[]dnl - case $pc_cv_python_site_dir in - $pc_py_prefix*) - pc__strip_prefix=`echo "$pc_py_prefix" | sed 's|.|.|g'` - pc_cv_python_site_dir=`echo "$pc_cv_python_site_dir" | sed "s,^$pc__strip_prefix/,,"` - ;; - *) - case $pc_py_prefix in - /usr|/System*) ;; - *) - pc_cv_python_site_dir=lib/python$PYTHON_VERSION/site-packages - ;; - esac - ;; - esac - ]) -AC_SUBST([pythondir], [\${prefix}/$pc_cv_python_site_dir])])# PC_PYTHON_CHECK_SITE_DIR - -# PC_PYTHON_SITE_PACKAGE_DIR -# -------------------------- -# $PACKAGE directory under PYTHON_SITE_DIR -AC_DEFUN([PC_PYTHON_SITE_PACKAGE_DIR], -[AC_REQUIRE([PC_PYTHON_CHECK_SITE_DIR])[]dnl -AC_SUBST([pkgpythondir], [\${pythondir}/$PACKAGE_NAME])]) - - -# PC_PYTHON_CHECK_EXEC_DIR -# ------------------------ -# directory for installing python extension modules (shared libraries) -AC_DEFUN([PC_PYTHON_CHECK_EXEC_DIR], -[AC_REQUIRE([PC_PROG_PYTHON])AC_REQUIRE([PC_PYTHON_CHECK_EXEC_PREFIX])[]dnl - AC_CACHE_CHECK([for Python extension module directory], - [pc_cv_python_exec_dir], - [AC_LANG_PUSH(Python)[]dnl - if test "x$pc_cv_python_exec_prefix" = xNONE - then - pc_py_exec_prefix=$pc_cv_python_prefix - else - pc_py_exec_prefix=$pc_cv_python_exec_prefix - fi - AC_LINK_IFELSE([AC_LANG_PROGRAM([dnl -import sys -from platform import python_implementation -# sysconfig in CPython 2.7 doesn't work in virtualenv -# <https://github.com/pypa/virtualenv/issues/118> -try: - import sysconfig -except: - can_use_sysconfig = False -else: - can_use_sysconfig = True -if can_use_sysconfig: - if python_implementation() == "CPython" and sys.version[[:3]] == '2.7': - can_use_sysconfig = False -if not can_use_sysconfig: - from distutils import sysconfig - sitedir = sysconfig.get_python_lib(False, False, prefix='$pc_py__exec_prefix') -else: - sitedir = sysconfig.get_path('purelib', vars={'platbase':'$pc_py_exec_prefix'}) -], [dnl - sys.stdout.write(sitedir) -])], [pc_cv_python_exec_dir=`./conftest`], - [AC_MSG_FAILURE([failed to run Python program])]) - AC_LANG_POP(Python)[]dnl - case $pc_cv_python_exec_dir in - $pc_py_exec_prefix*) - pc__strip_prefix=`echo "$pc_py_exec_prefix" | sed 's|.|.|g'` - pc_cv_python_exec_dir=`echo "$pc_cv_python_exec_dir" | sed "s,^$pc__strip_prefix/,,"` - ;; - *) - case $pc_py_exec_prefix in - /usr|/System*) ;; - *) - pc_cv_python_exec_dir=lib/python$PYTHON_VERSION/site-packages - ;; - esac - ;; - esac - ]) -AC_SUBST([pyexecdir], [\${exec_prefix}/$pc_cv_python_pyexecdir])]) #PY_PYTHON_CHECK_EXEC_LIB_DIR - - -# PC_PYTHON_EXEC_PACKAGE_DIR -# -------------------------- -# $PACKAGE directory under PYTHON_SITE_DIR -AC_DEFUN([PC_PYTHON_EXEC_PACKAGE_DIR], -[AC_REQUIRE([PC_PYTHON_CHECK_EXEC_DIR])[]dnl -AC_SUBST([pkgpyexecdir], [\${pyexecdir}/$PACKAGE_NAME])]) - - -## -------------------------------------------- ## -## 4. Looking for specific libs & functionality ## -## -------------------------------------------- ## - - -# PC_PYTHON_CHECK_MODULE(LIBRARY, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) -# ---------------------------------------------------------------------- -# Macro for checking if a Python library is installed -AC_DEFUN([PC_PYTHON_CHECK_MODULE], -[AC_REQUIRE([PC_PROG_PYTHON])[]dnl -m4_define([pc_python_safe_mod], m4_bpatsubsts($1, [\.], [_])) -AC_CACHE_CHECK([for Python '$1' library], - [[pc_cv_python_module_]pc_python_safe_mod], - [AC_LANG_PUSH(Python)[]dnl - AC_RUN_IFELSE( - [AC_LANG_PROGRAM([dnl -import sys -try: - import $1 -except: - sys.exit(1) -else: - sys.exit(0) -], [])], - [[pc_cv_python_module_]pc_python_safe_mod="yes"], - [[pc_cv_python_module_]pc_python_safe_mod="no"]) - AC_LANG_POP(Python)[]dnl - ]) -AS_IF([test "$[pc_cv_python_module_]pc_python_safe_mod" = "no"], [$3], [$2]) -])# PC_PYTHON_CHECK_MODULE - - -# PC_PYTHON_CHECK_FUNC([LIBRARY], FUNCTION, ARGS, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) -# --------------------------------------------------------------------------------------- -# Check to see if a given function call, optionally from a module, can -# be successfully called -AC_DEFUN([PC_PYTHON_CHECK_FUNC], -[AC_REQUIRE([PC_PROG_PYTHON])[]dnl -m4_define([pc_python_safe_mod], m4_bpatsubsts($1, [\.], [_])) -AC_CACHE_CHECK([for Python m4_ifnblank($1, '$1.$2()', '$2()') function], - [[pc_cv_python_func_]pc_python_safe_mod[_$2]], - [AC_LANG_PUSH(Python)[]dnl - AC_RUN_IFELSE( - [AC_LANG_PROGRAM([dnl -import sys -m4_ifnblank([$1], [dnl -try: - import $1 -except: - sys.exit(1) -], [])], -[ -m4_ifnblank([$1], [ - try: - $1.$2($3)], [ - try: - $2($3)]) - except: - sys.exit(1) - else: - sys.exit(0) -])], - [[pc_cv_python_func_]pc_python_safe_mod[_$2]="yes"], - [[pc_cv_python_func_]pc_python_safe_mod[_$2]="no"]) - AC_LANG_POP(Python)[]dnl - ]) -AS_IF([test "$[pc_cv_python_func_]pc_python_safe_mod[_$2]" = "no"], [$5], [$4]) -])# PC_PYTHON_CHECK_FUNC @@ -1,30 +1,32 @@ from setuptools import setup, find_packages -setup(name='talersurvey', - version='0.0', - description='Example survey site for GNU Taler', - url='git://taler.net/survey', - author='Marcello Stanisci', - author_email='stanisci.m@gmail.com', - license='GPL', - packages=find_packages(), - install_requires=["Flask>=0.10", "taler-util"], - test_suite="nose.collector", - tests_require=["mock", "nose"], - package_data={ - '':[ - "survey/templates/*.html", - "survey/static/favicon.ico", - "survey/static/*.svg", - "survey/static/*.css", - "survey/static/*.js", - "survey/static/*.js.tar.gz", - "survey/static/web-common/*.png", - "survey/static/web-common/*.css", - "survey/static/web-common/*.js", - "survey/static/web-common/*.js.tar.gz", - "survey/static/web-common/*.html", - ] - }, - scripts=['taler-merchant-survey'], - zip_safe=False) +setup( + name='talersurvey', + version='0.6.0pre1', + description='Example survey site for GNU Taler', + url='git://taler.net/survey', + author='Marcello Stanisci', + author_email='stanisci.m@gmail.com', + license='GPL', + packages=find_packages(), + install_requires=["Flask>=0.10", "jsmin", "taler-util"], + test_suite="nose.collector", + tests_require=["mock", "nose", "requests", "uwsgi", "qrcode", "lxml"], + package_data={ + '': [ + "survey/templates/*.html", + "survey/static/favicon.ico", + "survey/static/*.svg", + "survey/static/*.css", + "survey/static/*.js", + "survey/static/*.js.tar.gz", + "survey/static/web-common/*.png", + "survey/static/web-common/*.css", + "survey/static/web-common/*.js", + "survey/static/web-common/*.js.tar.gz", + "survey/static/web-common/*.html", + ] + }, + scripts=['./bin/taler-merchant-survey'], + zip_safe=False +) diff --git a/talersurvey/survey/survey.py b/talersurvey/survey/survey.py index 897c4bf..a016a9f 100644 --- a/talersurvey/survey/survey.py +++ b/talersurvey/survey/survey.py @@ -26,6 +26,9 @@ from urllib.parse import urljoin import flask import requests import traceback +import qrcode +import qrcode.image.svg +import lxml.etree from taler.util.talerconfig import TalerConfig BASE_DIR = os.path.dirname(os.path.abspath(__file__)) @@ -48,14 +51,21 @@ LOGGER = logging.getLogger(__name__) # the merchant backend. # @return a flask-native response object. def backend_error(requests_response): - LOGGER.error("Backend error: status code: " - + str(requests_response.status_code)) + LOGGER.error( + "Backend error: status code: " + str(requests_response.status_code) + ) try: - return flask.jsonify(requests_response.json()), requests_response.status_code + return flask.jsonify( + requests_response.json() + ), requests_response.status_code except json.decoder.JSONDecodeError: - LOGGER.error("Backend error (NO JSON returned): status code: " - + str(requests_response.status_code)) - return flask.jsonify(dict(error="Backend died, no JSON got from it")), 502 + LOGGER.error( + "Backend error (NO JSON returned): status code: " + + str(requests_response.status_code) + ) + return flask.jsonify( + dict(error="Backend died, no JSON got from it") + ), 502 ## @@ -66,14 +76,15 @@ def backend_error(requests_response): def utility_processor(): def env(name, default=None): return os.environ.get(name, default) + def prettydate(talerdate): - parsed_time = re.search(r"/Date\(([0-9]+)\)/", talerdate) - if not parsed_time: - return "malformed date given" - parsed_time = int(parsed_time.group(1)) - timestamp = datetime.datetime.fromtimestamp(parsed_time) - # returns the YYYY-MM-DD date format. - return timestamp.strftime("%Y-%b-%d") + parsed_time = re.search(r"/Date\(([0-9]+)\)/", talerdate) + if not parsed_time: + return "malformed date given" + parsed_time = int(parsed_time.group(1)) + timestamp = datetime.datetime.fromtimestamp(parsed_time) + # returns the YYYY-MM-DD date format. + return timestamp.strftime("%Y-%b-%d") return dict(env=env, prettydate=prettydate) @@ -99,17 +110,26 @@ def err_abort(abort_status_code, **params): def backend_post(endpoint, json): headers = {"Authorization": "ApiKey " + APIKEY} try: - resp = requests.post(urljoin(BACKEND_URL, endpoint), json=json, headers=headers) + resp = requests.post( + urljoin(BACKEND_URL, endpoint), json=json, headers=headers + ) except requests.ConnectionError: err_abort(500, message="Could not establish connection to backend") try: response_json = resp.json() except ValueError: - err_abort(500, message="Could not parse response from backend", - status_code=resp.status_code) + err_abort( + 500, + message="Could not parse response from backend", + status_code=resp.status_code + ) if resp.status_code != 200: - err_abort(500, message="Backend returned error status", - json=response_json, status_code=resp.status_code) + err_abort( + 500, + message="Backend returned error status", + json=response_json, + status_code=resp.status_code + ) return response_json @@ -123,7 +143,9 @@ def backend_post(endpoint, json): def backend_get(endpoint, params): headers = {"Authorization": "ApiKey " + APIKEY} try: - resp = requests.get(urljoin(BACKEND_URL, endpoint), params=params, headers=headers) + resp = requests.get( + urljoin(BACKEND_URL, endpoint), params=params, headers=headers + ) except requests.ConnectionError: err_abort(500, message="Could not establish connection to backend") try: @@ -131,8 +153,12 @@ def backend_get(endpoint, params): except ValueError: err_abort(500, message="Could not parse response from backend") if resp.status_code != 200: - err_abort(500, message="Backend returned error status", - json=response_json, status_code=resp.status_code) + err_abort( + 500, + message="Backend returned error status", + json=response_json, + status_code=resp.status_code + ) return response_json @@ -144,9 +170,11 @@ def backend_get(endpoint, params): # (and execution stack!). @app.errorhandler(Exception) def internal_error(e): - return flask.render_template("templates/error.html", - message="Internal error", - stack=traceback.format_exc()) + return flask.render_template( + "templates/error.html", + message="Internal error", + stack=traceback.format_exc() + ) ## @@ -156,8 +184,12 @@ def internal_error(e): @app.route("/favicon.ico") def favicon(): print("will look into: " + os.path.join(app.root_path, 'static')) - return flask.send_from_directory(os.path.join(app.root_path, 'static'), - "favicon.ico", mimetype="image/vnd.microsoft.ico") + return flask.send_from_directory( + os.path.join(app.root_path, 'static'), + "favicon.ico", + mimetype="image/vnd.microsoft.ico" + ) + ## # Give information about the tip reserve status. @@ -169,6 +201,13 @@ def survey_stats(): stats = backend_get("tip-query", dict(instance="default")) return flask.render_template("templates/survey_stats.html", stats=stats) + +def get_qrcode_svg(data): + factory = qrcode.image.svg.SvgImage + img = qrcode.make(data, image_factory=factory) + return lxml.etree.tostring(img.get_image()).decode("utf-8") + + ## # Tell the backend to 'authorize' a tip; this means that # the backend will allocate a certain amount to be later @@ -179,17 +218,29 @@ def survey_stats(): # otherwise. @app.route("/submit-survey", methods=["POST"]) def submit_survey(): - tip_spec = dict(amount=CURRENCY + ":1.0", - next_url=os.environ.get("TALER_ENV_URL_INTRO", "https://taler.net/"), - instance="default", - justification="Payment methods survey") - resp = backend_post("tip-authorize", tip_spec) - - if resp.get("tip_redirect_url"): - return flask.redirect(resp["tip_redirect_url"]) + tip_spec = dict( + amount=CURRENCY + ":1.0", + next_url=os.environ.get("TALER_ENV_URL_INTRO", "https://taler.net/"), + instance="default", + justification="Payment methods survey" + ) + backend_resp = backend_post("tip-authorize", tip_spec) + + taler_tip_uri = backend_resp.get("taler_tip_uri") + if taler_tip_uri: + qrcode_svg = get_qrcode_svg(taler_tip_uri) + content = flask.render_template( + "templates/show_tip.html", + qrcode_svg=qrcode_svg, + taler_tip_uri=taler_tip_uri, + ) + headers = {"Taler": taler_tip_uri} + return flask.Response(content, status=402, headers=headers) + + err_abort( + 500, message="Tipping failed, unexpected backend response", json=resp + ) - err_abort(500, message="Tipping failed, unexpected backend response", - json=resp) ## # Serve the main index page. @@ -197,4 +248,6 @@ def submit_survey(): # @return response object of the index page. @app.route("/", methods=["GET"]) def index(): - return flask.render_template("templates/index.html", merchant_currency=CURRENCY) + return flask.render_template( + "templates/index.html", merchant_currency=CURRENCY + ) diff --git a/talersurvey/survey/templates/index.html b/talersurvey/survey/templates/index.html index ffd8f61..e58e669 100644 --- a/talersurvey/survey/templates/index.html +++ b/talersurvey/survey/templates/index.html @@ -7,11 +7,7 @@ and get a nice tip - via your Taler wallet - from this shop! <a href={{ url_for('survey_stats') }}>(survey stats)</a> </p> </div> - <div class="taler-installed-hide"> - <p>The survey is not possible without the wallet; - please <a href="https://taler.net/wallet-installation.html">install one!</a></p> - </div> - <div class="taler-installed-show"> + <div> <form action="{{ url_for('submit_survey') }}" method="post" class="pure-form pure-form-stacked"> <legend>What do you prefer?</legend> <fieldset> diff --git a/talersurvey/survey/templates/show_tip.html b/talersurvey/survey/templates/show_tip.html new file mode 100644 index 0000000..1c6317a --- /dev/null +++ b/talersurvey/survey/templates/show_tip.html @@ -0,0 +1,35 @@ +{% extends "templates/base.html" %} + + +{% block meta %} +<noscript> + <meta http-equiv="refresh" content="1"> +</noscript> +{% endblock meta %} + +{% block main %} + +<h1>Tip Offered</h1> + +<div class="taler-installed-hide"> + <p> + Looks like your browser doesn't support GNU Taler payments. You can try + installing a <a href="https://taler.net/en/wallet.html">wallet browser extension</a>. + </p> +</div> + +<div> + + <p> + You can use this QR code to receive a tip with your mobile wallet: + </p> + + {{ qrcode_svg | safe }} + + <p> + Click <a href="{{ taler_tip_uri }}">this link</a> to open your system's Taler wallet if it exists. + </p> + +</div> + +{% endblock main %} diff --git a/talersurvey/tests.py b/talersurvey/tests.py index 512038e..360270c 100644 --- a/talersurvey/tests.py +++ b/talersurvey/tests.py @@ -35,5 +35,6 @@ class SurveyTestCase(unittest.TestCase): survey.app.testing = True self.app = survey.app.test_client() + if __name__ == "__main__": unittest.main() |