summaryrefslogtreecommitdiff
path: root/talerdonations
diff options
context:
space:
mode:
Diffstat (limited to 'talerdonations')
-rw-r--r--talerdonations/donations/donations.py113
-rw-r--r--talerdonations/helpers.py59
-rw-r--r--talerdonations/talerconfig.py277
-rw-r--r--talerdonations/tests.py67
4 files changed, 268 insertions, 248 deletions
diff --git a/talerdonations/donations/donations.py b/talerdonations/donations/donations.py
index 9bf5afc..56a8c1e 100644
--- a/talerdonations/donations/donations.py
+++ b/talerdonations/donations/donations.py
@@ -15,33 +15,31 @@
# @author Florian Dold
# @author Marcello Stanisci
-import flask
-from urllib.parse import urljoin, urlencode, quote, parse_qsl
-import requests
+from urllib.parse import urljoin, parse_qsl
import logging
import os
import base64
import random
-import time
from datetime import datetime
-import jinja2
+import requests
+import flask
from talerdonations.talerconfig import TalerConfig
-from talerdonations.helpers import (make_url,
-expect_parameter, amount_from_float, amount_to_float,
-join_urlparts, get_query_string, MissingParameterException,
-backend_error)
+from talerdonations.helpers import (make_url, \
+ expect_parameter, amount_from_float, \
+ amount_to_float, join_urlparts, get_query_string, \
+ MissingParameterException, backend_error)
-logger = logging.getLogger(__name__)
+LOGGER = logging.getLogger(__name__)
-base_dir = os.path.dirname(os.path.abspath(__file__))
+BASE_DIR = os.path.dirname(os.path.abspath(__file__))
-app = flask.Flask(__name__, template_folder=base_dir)
+app = flask.Flask(__name__, template_folder=BASE_DIR)
app.debug = True
app.secret_key = base64.b64encode(os.urandom(64)).decode('utf-8')
-tc = TalerConfig.from_env()
-BACKEND_URL = tc["frontends"]["backend"].value_string(required=True)
-CURRENCY = tc["taler"]["currency"].value_string(required=True)
+TC = TalerConfig.from_env()
+BACKEND_URL = TC["frontends"]["backend"].value_string(required=True)
+CURRENCY = TC["taler"]["currency"].value_string(required=True)
MAX_FEE = dict(value=3, fraction=0, currency=CURRENCY)
app.config.from_object(__name__)
@@ -69,17 +67,19 @@ def checkout():
amount_str = expect_parameter("donation_amount")
donation_receiver = expect_parameter("donation_receiver")
try:
- amount = float(amount_str)
+ float(amount_str)
except ValueError:
- logger.warn("Invalid amount ('%s')", amount_str)
- e = flask.jsonify(error="invalid amount")
- return flask.make_response(e, 400)
+ LOGGER.warning("Invalid amount ('%s')", amount_str)
+ return flask.make_response(
+ flask.jsonify(error="invalid amount"),
+ 400)
display_alert = flask.request.args.get("display_alert", None)
- return flask.render_template("templates/checkout.html",
- donation_amount=amount_str,
- donation_receiver=donation_receiver,
- merchant_currency=CURRENCY,
- display_alert=display_alert)
+ return flask.render_template(
+ "templates/checkout.html",
+ donation_amount=amount_str,
+ donation_receiver=donation_receiver,
+ merchant_currency=CURRENCY,
+ display_alert=display_alert)
@app.route("/generate-contract", methods=["GET"])
@@ -87,13 +87,14 @@ def generate_contract():
try:
donation_receiver = expect_parameter("donation_receiver")
donation_amount = expect_parameter("donation_amount")
- except MissingParameterException as e:
- return flask.jsonify(dict(error="Missing parameter '%s'" % e)), 400
+ except MissingParameterException as exc:
+ return flask.jsonify(
+ dict(error="Missing parameter '%s'" % exc)), 400
amount = amount_from_float(float(donation_amount))
order_id = "donation-%s-%X-%s" % \
(donation_receiver,
- random.randint(0, 0xFFFFFFFF),
- datetime.today().strftime('%H_%M_%S'))
+ random.randint(0, 0xFFFFFFFF),
+ datetime.today().strftime('%H_%M_%S'))
order = dict(
summary="Donation!",
nonce=flask.request.args.get("nonce"),
@@ -117,21 +118,22 @@ def generate_contract():
jurisdiction="none",
),
)
- r = requests.post(urljoin(BACKEND_URL, 'proposal'), json=dict(order=order))
- if 200 != r.status_code:
+ resp = requests.post(urljoin(BACKEND_URL, 'proposal'),
+ json=dict(order=order))
+ if resp.status_code != 200:
# It is important to use 'backend_error()', as it handles
# the case where the backend gives NO JSON as response.
# For example, if it dies, or nginx hijacks somehow the
# response.
- return backend_error(r)
- return flask.jsonify(r.json()), r.status_code
+ return backend_error(resp)
+ return flask.jsonify(resp.json()), resp.status_code
@app.route("/donate")
def donate():
donation_receiver = expect_parameter("donation_receiver")
donation_amount = expect_parameter("donation_amount")
payment_system = expect_parameter("payment_system")
- if "taler" != payment_system:
+ if payment_system != "taler":
return flask.redirect(make_url("checkout",
("donation_receiver", donation_receiver),
("donation_amount", donation_amount),
@@ -153,12 +155,12 @@ def fulfillment():
if order_id in payed_order_ids:
data = payed_order_ids[order_id]
return flask.render_template(
- "templates/fulfillment.html",
- donation_receiver=data["donation_receiver"],
- donation_amount=data["donation_amount"],
- order_id=order_id,
- currency=CURRENCY)
-
+ "templates/fulfillment.html",
+ donation_receiver=data["donation_receiver"],
+ donation_amount=data["donation_amount"],
+ order_id=order_id,
+ currency=CURRENCY)
+
response = flask.make_response(flask.render_template("templates/fallback.html"), 402)
response.headers["X-Taler-Contract-Query"] = "fulfillment_url"
response.headers["X-Taler-Offer-Url"] = make_url("/")
@@ -169,20 +171,21 @@ def fulfillment():
def pay():
deposit_permission = flask.request.get_json()
if deposit_permission is None:
- e = flask.jsonify(error="no json in body")
- return e, 400
- r = requests.post(urljoin(BACKEND_URL, "pay"), json=deposit_permission)
- if 200 != r.status_code:
- return backend_error(r)
- proposal_data = r.json()["contract_terms"]
+ return flask.jsonify(error="no json in body"), 400
+ resp = requests.post(urljoin(BACKEND_URL, "pay"),
+ json=deposit_permission)
+ if resp.status_code != 200:
+ return backend_error(resp)
+ proposal_data = resp.json()["contract_terms"]
order_id = proposal_data["order_id"]
- payed_order_ids = flask.session["payed_order_ids"] = flask.session.get("payed_order_ids", {})
+ payed_order_ids = flask.session["payed_order_ids"] \
+ = flask.session.get("payed_order_ids", {})
payed_order_ids[order_id] = dict(
donation_receiver=proposal_data["merchant"]["instance"],
donation_amount=amount_to_float(proposal_data["amount"])
)
print("received payment for", order_id)
- return flask.jsonify(r.json()), r.status_code
+ return flask.jsonify(resp.json()), resp.status_code
@app.route("/backoffice")
def track():
@@ -194,10 +197,10 @@ def track():
def history():
qs = get_query_string().decode("utf-8")
url = urljoin(BACKEND_URL, "history")
- r = requests.get(url, params=dict(parse_qsl(qs)))
- if 200 != r.status_code:
- return backend_error(r)
- return flask.jsonify(r.json()), r.status_code
+ resp = requests.get(url, params=dict(parse_qsl(qs)))
+ if resp.status_code != 200:
+ return backend_error(resp)
+ return flask.jsonify(resp.json()), resp.status_code
@app.route("/track/order")
@@ -205,7 +208,7 @@ def track_order():
instance = expect_parameter("instance")
order_id = expect_parameter("order_id")
url = urljoin(BACKEND_URL, "track/transaction")
- r = requests.get(url, params=dict(order_id=order_id, instance=instance))
- if 200 != r.status_code:
- return backend_error(r)
- return flask.jsonify(r.json()), r.status_code
+ resp = requests.get(url, params=dict(order_id=order_id, instance=instance))
+ if resp.status_code != 200:
+ return backend_error(resp)
+ return flask.jsonify(resp.json()), resp.status_code
diff --git a/talerdonations/helpers.py b/talerdonations/helpers.py
index 257192d..614e463 100644
--- a/talerdonations/helpers.py
+++ b/talerdonations/helpers.py
@@ -15,22 +15,18 @@
# @author Florian Dold
# @author Marcello Stanisci
-from flask import request, jsonify, make_response, current_app, render_template
-import flask
from urllib.parse import urljoin, urlencode
import logging
-import requests
-import re
-import datetime
import json
+import flask
from .talerconfig import TalerConfig
-logger = logging.getLogger(__name__)
+LOGGER = logging.getLogger(__name__)
-tc = TalerConfig.from_env()
-BACKEND_URL = tc["frontends"]["backend"].value_string(required=True)
-NDIGITS = tc["frontends"]["NDIGITS"].value_int()
-CURRENCY = tc["taler"]["CURRENCY"].value_string()
+TC = TalerConfig.from_env()
+BACKEND_URL = TC["frontends"]["backend"].value_string(required=True)
+NDIGITS = TC["frontends"]["NDIGITS"].value_int()
+CURRENCY = TC["taler"]["CURRENCY"].value_string()
FRACTION_BASE = 1e8
@@ -40,29 +36,30 @@ if not NDIGITS:
class MissingParameterException(Exception):
def __init__(self, param):
self.param = param
+ super().__init__()
def amount_to_float(amount):
return amount['value'] + (float(amount['fraction']) / float(FRACTION_BASE))
-def amount_from_float(x):
- value = int(x)
- fraction = int((x - value) * FRACTION_BASE)
+def amount_from_float(floatx):
+ value = int(floatx)
+ fraction = int((floatx - value) * FRACTION_BASE)
return dict(currency=CURRENCY, value=value, fraction=fraction)
def join_urlparts(*parts):
- s = ""
- i = 0
- while i < len(parts):
- n = parts[i]
- i += 1
- if s.endswith("/"):
- n = n.lstrip("/")
- elif s and not n.startswith("/"):
- n = "/" + n
- s += n
- return s
+ ret = ""
+ part = 0
+ while part < len(parts):
+ buf = parts[part]
+ part += 1
+ if ret.endswith("/"):
+ buf = buf.lstrip("/")
+ elif ret and not buf.startswith("/"):
+ buf = "/" + buf
+ ret += buf
+ return ret
def make_url(page, *query_params):
@@ -72,10 +69,10 @@ def make_url(page, *query_params):
"""
query = urlencode(query_params)
if page.startswith("/"):
- root = request.url_root
+ root = flask.request.url_root
page = page.lstrip("/")
else:
- root = request.base_url
+ root = flask.request.base_url
url = urljoin(root, "%s?%s" % (page, query))
# urlencode is overly eager with quoting, the wallet right now
# needs some characters unquoted.
@@ -83,22 +80,22 @@ def make_url(page, *query_params):
def expect_parameter(name, alt=None):
- value = request.args.get(name, None)
+ value = flask.request.args.get(name, None)
if value is None and alt is None:
- logger.error("Missing parameter '%s' from '%s'." % (name, request.args))
+ LOGGER.error("Missing parameter '%s' from '%s'." % (name, flask.request.args))
raise MissingParameterException(name)
return value if value else alt
def get_query_string():
- return request.query_string
+ return flask.request.query_string
def backend_error(requests_response):
- logger.error("Backend error: status code: "
+ LOGGER.error("Backend error: status code: "
+ str(requests_response.status_code))
try:
return flask.jsonify(requests_response.json()), requests_response.status_code
except json.decoder.JSONDecodeError:
- logger.error("Backend error (NO JSON returned): status code: "
+ 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
diff --git a/talerdonations/talerconfig.py b/talerdonations/talerconfig.py
index ba4dfbb..a7ca065 100644
--- a/talerdonations/talerconfig.py
+++ b/talerdonations/talerconfig.py
@@ -18,23 +18,22 @@
Parse GNUnet-style configurations in pure Python
"""
-# FIXME: make sure that autovivification of config entries
-# does not leave garbage behind (use weakrefs!)
-
import logging
import collections
import os
import weakref
+import sys
+import re
-logger = logging.getLogger(__name__)
+LOGGER = logging.getLogger(__name__)
__all__ = ["TalerConfig"]
-taler_datadir = None
+TALER_DATADIR = None
try:
# not clear if this is a good idea ...
- from talerpaths import taler_datadir as t
- taler_datadir = t
+ from talerpaths import TALER_DATADIR as t
+ TALER_DATADIR = t
except ImportError:
pass
@@ -45,7 +44,7 @@ class ExpansionSyntaxError(Exception):
pass
-def expand(s, getter):
+def expand(var, getter):
"""
Do shell-style parameter expansion.
Supported syntax:
@@ -56,18 +55,18 @@ def expand(s, getter):
pos = 0
result = ""
while pos != -1:
- start = s.find("$", pos)
+ start = var.find("$", pos)
if start == -1:
break
- if s[start:].startswith("${"):
+ if var[start:].startswith("${"):
balance = 1
end = start + 2
- while balance > 0 and end < len(s):
- balance += {"{": 1, "}": -1}.get(s[end], 0)
+ while balance > 0 and end < len(var):
+ balance += {"{": 1, "}": -1}.get(var[end], 0)
end += 1
if balance != 0:
raise ExpansionSyntaxError("unbalanced parentheses")
- piece = s[start+2:end-1]
+ piece = var[start+2:end-1]
if piece.find(":-") > 0:
varname, alt = piece.split(":-", 1)
replace = getter(varname)
@@ -77,20 +76,20 @@ def expand(s, getter):
varname = piece
replace = getter(varname)
if replace is None:
- replace = s[start:end]
+ replace = var[start:end]
else:
end = start + 2
- while end < len(s) and s[start+1:end+1].isalnum():
+ while end < len(var) and var[start+1:end+1].isalnum():
end += 1
- varname = s[start+1:end]
+ varname = var[start+1:end]
replace = getter(varname)
if replace is None:
- replace = s[start:end]
+ replace = var[start:end]
result = result + replace
pos = end
- return result + s[pos:]
+ return result + var[pos:]
class OptionDict(collections.defaultdict):
@@ -99,79 +98,81 @@ class OptionDict(collections.defaultdict):
self.section_name = section_name
super().__init__()
def __missing__(self, key):
- e = Entry(self.config(), self.section_name, key)
- self[key] = e
- return e
- def __getitem__(self, slice):
- return super().__getitem__(slice.lower())
- def __setitem__(self, slice, value):
- super().__setitem__(slice.lower(), value)
+ entry = Entry(self.config(), self.section_name, key)
+ self[key] = entry
+ return entry
+ def __getitem__(self, chunk):
+ return super().__getitem__(chunk.lower())
+ def __setitem__(self, chunk, value):
+ super().__setitem__(chunk.lower(), value)
class SectionDict(collections.defaultdict):
- def __init__(self):
- super().__init__()
def __missing__(self, key):
- v = OptionDict(self, key)
- self[key] = v
- return v
- def __getitem__(self, slice):
- return super().__getitem__(slice.lower())
- def __setitem__(self, slice, value):
- super().__setitem__(slice.lower(), value)
+ value = OptionDict(self, key)
+ self[key] = value
+ return value
+ def __getitem__(self, chunk):
+ return super().__getitem__(chunk.lower())
+ def __setitem__(self, chunk, value):
+ super().__setitem__(chunk.lower(), value)
class Entry:
- def __init__(self, config, section, option, value=None, filename=None, lineno=None):
- self.value = value
- self.filename = filename
- self.lineno = lineno
+ def __init__(self, config, section, option, **kwargs):
+ self.value = kwargs.get("value")
+ self.filename = kwargs.get("filename")
+ self.lineno = kwargs.get("lineno")
self.section = section
self.option = option
self.config = weakref.ref(config)
def __repr__(self):
- return "<Entry section=%s, option=%s, value=%s>" % (self.section, self.option, repr(self.value),)
+ return "<Entry section=%s, option=%s, value=%s>" \
+ % (self.section, self.option, repr(self.value),)
def __str__(self):
return self.value
- def value_string(self, default=None, warn=False, required=False):
+ def value_string(self, default=None, required=False, warn=False):
if required and self.value is None:
- raise ConfigurationError("Missing required option '%s' in section '%s'" % (self.option.upper(), self.section.upper()))
+ raise ConfigurationError("Missing required option '%s' in section '%s'" \
+ % (self.option.upper(), self.section.upper()))
if self.value is None:
if warn:
if default is not None:
- logger.warn("Configuration is missing option '%s' in section '%s', falling back to '%s'",
- self.option, self.section, default)
+ LOGGER.warning("Configuration is missing option '%s' in section '%s',\
+ falling back to '%s'", self.option, self.section, default)
else:
- logger.warn("Configuration is missing option '%s' in section '%s'", self.option.upper(), self.section.upper())
+ LOGGER.warning("Configuration ** is missing option '%s' in section '%s'",
+ self.option.upper(), self.section.upper())
return default
return self.value
- def value_int(self, default=None, warn=False, required=False):
- v = self.value_string(default, warn, required)
- if v is None:
+ def value_int(self, default=None, required=False, warn=False):
+ value = self.value_string(default, warn, required)
+ if value is None:
return None
try:
- return int(v)
+ return int(value)
except ValueError:
- raise ConfigurationError("Expected number for option '%s' in section '%s'" % (self.option.upper(), self.section.upper()))
+ raise ConfigurationError("Expected number for option '%s' in section '%s'" \
+ % (self.option.upper(), self.section.upper()))
def _getsubst(self, key):
- x = self.config()["paths"][key].value
- if x is not None:
- return x
- x = os.environ.get(key)
- if x is not None:
- return x
+ value = self.config()["paths"][key].value
+ if value is not None:
+ return value
+ value = os.environ.get(key)
+ if value is not None:
+ return value
return None
- def value_filename(self, default=None, warn=False, required=False):
- v = self.value_string(default, warn, required)
- if v is None:
+ def value_filename(self, default=None, required=False, warn=False):
+ value = self.value_string(default, required, warn)
+ if value is None:
return None
- return expand(v, lambda x: self._getsubst(x))
+ return expand(value, self._getsubst)
def location(self):
if self.filename is None or self.lineno is None:
@@ -190,6 +191,8 @@ class TalerConfig:
"""
self.sections = SectionDict()
+ # defaults != config file: the first is the 'base'
+ # whereas the second overrides things from the first.
@staticmethod
def from_file(filename=None, load_defaults=True):
cfg = TalerConfig()
@@ -204,14 +207,17 @@ class TalerConfig:
cfg.load_file(filename)
return cfg
- def value_string(self, section, option, default=None, required=None, warn=False):
- return self.sections[section][option].value_string(default, required, warn)
+ def value_string(self, section, option, **kwargs):
+ return self.sections[section][option].value_string(
+ kwargs.get("default"), kwargs.get("required"), kwargs.get("warn"))
- def value_filename(self, section, option, default=None, required=None, warn=False):
- return self.sections[section][option].value_filename(default, required, warn)
+ def value_filename(self, section, option, **kwargs):
+ return self.sections[section][option].value_filename(
+ kwargs.get("default"), kwargs.get("required"), kwargs.get("warn"))
- def value_int(self, section, option, default=None, required=None, warn=False):
- return self.sections[section][option].value_int(default, required, warn)
+ def value_int(self, section, option, **kwargs):
+ return self.sections[section][option].value_int(
+ kwargs.get("default"), kwargs.get("required"), kwargs.get("warn"))
def load_defaults(self):
base_dir = os.environ.get("TALER_BASE_CONFIG")
@@ -220,12 +226,15 @@ class TalerConfig:
return
prefix = os.environ.get("TALER_PREFIX")
if prefix:
+ tmp = os.path.split(os.path.normpath(prefix))
+ if re.match("lib", tmp[1]):
+ prefix = tmp[0]
self.load_dir(os.path.join(prefix, "share/taler/config.d"))
return
- if taler_datadir:
- self.load_dir(os.path.join(taler_datadir, "share/taler/config.d"))
+ if TALER_DATADIR:
+ self.load_dir(os.path.join(TALER_DATADIR, "share/taler/config.d"))
return
- logger.warn("no base directory found")
+ LOGGER.warning("no base directory found")
@staticmethod
def from_env(*args, **kwargs):
@@ -240,7 +249,7 @@ class TalerConfig:
try:
files = os.listdir(dirname)
except FileNotFoundError:
- logger.warn("can't read config directory '%s'", dirname)
+ LOGGER.warning("can't read config directory '%s'", dirname)
return
for file in files:
if not file.endswith(".conf"):
@@ -249,73 +258,85 @@ class TalerConfig:
def load_file(self, filename):
sections = self.sections
- with open(filename, "r") as file:
- lineno = 0
- current_section = None
- for line in file:
- lineno += 1
- line = line.strip()
- if len(line) == 0:
- # empty line
- continue
- if line.startswith("#"):
- # comment
- continue
- if line.startswith("["):
- if not line.endswith("]"):
- logger.error("invalid section header in line %s: %s", lineno, repr(line))
- section_name = line.strip("[]").strip().strip('"')
- current_section = section_name
- continue
- if current_section is None:
- logger.error("option outside of section in line %s: %s", lineno, repr(line))
- continue
- kv = line.split("=", 1)
- if len(kv) != 2:
- logger.error("invalid option in line %s: %s", lineno, repr(line))
- key = kv[0].strip()
- value = kv[1].strip()
- if value.startswith('"'):
- value = value[1:]
- if not value.endswith('"'):
- logger.error("mismatched quotes in line %s: %s", lineno, repr(line))
- else:
- value = value[:-1]
- e = Entry(self.sections, current_section, key, value, filename, lineno)
- sections[current_section][key] = e
+ try:
+ with open(filename, "r") as file:
+ lineno = 0
+ current_section = None
+ for line in file:
+ lineno += 1
+ line = line.strip()
+ if line == "":
+ # empty line
+ continue
+ if line.startswith("#"):
+ # comment
+ continue
+ if line.startswith("["):
+ if not line.endswith("]"):
+ LOGGER.error("invalid section header in line %s: %s",
+ lineno, repr(line))
+ section_name = line.strip("[]").strip().strip('"')
+ current_section = section_name
+ continue
+ if current_section is None:
+ LOGGER.error("option outside of section in line %s: %s", lineno, repr(line))
+ continue
+ pair = line.split("=", 1)
+ if len(pair) != 2:
+ LOGGER.error("invalid option in line %s: %s", lineno, repr(line))
+ key = pair[0].strip()
+ value = pair[1].strip()
+ if value.startswith('"'):
+ value = value[1:]
+ if not value.endswith('"'):
+ LOGGER.error("mismatched quotes in line %s: %s", lineno, repr(line))
+ else:
+ value = value[:-1]
+ entry = Entry(self.sections, current_section, key,
+ value=value, filename=filename, lineno=lineno)
+ sections[current_section][key] = entry
+ except FileNotFoundError:
+ LOGGER.error("Configuration file (%s) not found", filename)
+ sys.exit(3)
def dump(self):
- for section_name, section in self.sections.items():
- print("[%s]" % (section.section_name,))
- for option_name, e in section.items():
- print("%s = %s # %s" % (e.option, e.value, e.location()))
-
- def __getitem__(self, slice):
- if isinstance(slice, str):
- return self.sections[slice]
+ for kv_section in self.sections.items():
+ print("[%s]" % (kv_section[1].section_name,))
+ for kv_option in kv_section[1].items():
+ print("%s = %s # %s" % \
+ (kv_option[1].option,
+ kv_option[1].value,
+ kv_option[1].location()))
+
+ def __getitem__(self, chunk):
+ if isinstance(chunk, str):
+ return self.sections[chunk]
raise TypeError("index must be string")
if __name__ == "__main__":
- import sys
import argparse
- parser = argparse.ArgumentParser()
- parser.add_argument("--section", "-s", dest="section", default=None, metavar="SECTION")
- parser.add_argument("--option", "-o", dest="option", default=None, metavar="OPTION")
- parser.add_argument("--config", "-c", dest="config", default=None, metavar="FILE")
- parser.add_argument("--filename", "-f", dest="expand_filename", default=False, action='store_true')
- args = parser.parse_args()
-
- tc = TalerConfig.from_file(args.config)
-
- if args.section is not None and args.option is not None:
- if args.expand_filename:
- x = tc.value_filename(args.section, args.option)
+ PARSER = argparse.ArgumentParser()
+ PARSER.add_argument("--section", "-s", dest="section",
+ default=None, metavar="SECTION")
+ PARSER.add_argument("--option", "-o", dest="option",
+ default=None, metavar="OPTION")
+ PARSER.add_argument("--config", "-c", dest="config",
+ default=None, metavar="FILE")
+ PARSER.add_argument("--filename", "-f", dest="expand_filename",
+ default=False, action='store_true')
+ ARGS = PARSER.parse_args()
+
+ TC = TalerConfig.from_file(ARGS.config)
+
+ if ARGS.section is not None and ARGS.option is not None:
+ if ARGS.expand_filename:
+ X = TC.value_filename(ARGS.section, ARGS.option)
else:
- x = tc.value_string(args.section, args.option)
- if x is not None:
- print(x)
+ X = TC.value_string(ARGS.section, ARGS.option)
+ if X is not None:
+ print(X)
else:
- tc.dump()
+ TC.dump()
diff --git a/talerdonations/tests.py b/talerdonations/tests.py
index 48ef764..9a5d374 100644
--- a/talerdonations/tests.py
+++ b/talerdonations/tests.py
@@ -1,13 +1,13 @@
#!/usr/bin/env python3
import unittest
+from datetime import datetime
from mock import patch, MagicMock
from talerdonations.donations import donations
from talerdonations.talerconfig import TalerConfig
-from datetime import datetime
-tc = TalerConfig.from_env()
-CURRENCY = tc["taler"]["currency"].value_string(required=True)
+TC = TalerConfig.from_env()
+CURRENCY = TC["taler"]["currency"].value_string(required=True)
class DonationsTestCase(unittest.TestCase):
def setUp(self):
@@ -22,7 +22,7 @@ class DonationsTestCase(unittest.TestCase):
mocked_datetime.today.return_value = datetime.today()
mocked_random.return_value = 333
order_id = "donation-%s-%X-%s" % \
- ("Tor", mocked_random(), mocked_datetime.today().strftime("%H_%M_%S"))
+ ("Tor", mocked_random(), mocked_datetime.today().strftime("%H_%M_%S"))
ret_post = MagicMock()
ret_post.status_code = 200
ret_post.json.return_value = {}
@@ -30,37 +30,36 @@ class DonationsTestCase(unittest.TestCase):
self.app.get(
"/generate-contract?nonce=44&donation_receiver=Tor&donation_amount=1.0")
mocked_post.assert_called_with(
- "http://backend.test.taler.net/proposal", json={
- "order": {
- "summary": "Donation!",
- "order_id": order_id,
- "nonce": "44",
- "amount": {
- "value": 1,
- "fraction": 0,
- "currency": CURRENCY},
- "max_fee": {
- "value": 1,
- "fraction": 0,
- "currency": CURRENCY},
- "order_id": order_id,
- "products": [{
- "description": "Donation to Tor",
- "quantity": 1,
- "product_id": 0,
- "price": {
+ "http://backend.test.taler.net/proposal",
+ json={
+ "order": {
+ "summary": "Donation!",
+ "nonce": "44",
+ "amount": {
+ "value": 1,
+ "fraction": 0,
+ "currency": CURRENCY},
+ "max_fee": {
"value": 1,
"fraction": 0,
- "currency": CURRENCY}}],
- "fulfillment_url":
- "http://localhost/fulfillment?order_id=%s" % order_id,
- "pay_url": "http://localhost/pay",
- "merchant": {
- "instance": "Tor",
- "address": "nowhere",
- "name": "Kudos Inc.",
- "jurisdiction": "none"}
- }})
+ "currency": CURRENCY},
+ "order_id": order_id,
+ "products": [{
+ "description": "Donation to Tor",
+ "quantity": 1,
+ "product_id": 0,
+ "price": {
+ "value": 1,
+ "fraction": 0,
+ "currency": CURRENCY}}],
+ "fulfillment_url":
+ "http://localhost/fulfillment?order_id=%s" % order_id,
+ "pay_url": "http://localhost/pay",
+ "merchant": {
+ "instance": "Tor",
+ "address": "nowhere",
+ "name": "Kudos Inc.",
+ "jurisdiction": "none"}}})
-if "__main__" == __name__:
+if __name__ == "__main__":
unittest.main()