From 55f569fc752074c5f47539d8be4b19727908e13b Mon Sep 17 00:00:00 2001 From: Ferry Boender Date: Wed, 22 Apr 2015 18:01:54 +0200 Subject: [PATCH] Deprecate support for calling native python callbacks. --- examples/native/README.md | 5 --- examples/native/native.json | 64 ----------------------------------- examples/native/native.py | 47 -------------------------- src/scriptform.py | 66 +++++++++---------------------------- 4 files changed, 15 insertions(+), 167 deletions(-) delete mode 100644 examples/native/README.md delete mode 100644 examples/native/native.json delete mode 100755 examples/native/native.py diff --git a/examples/native/README.md b/examples/native/README.md deleted file mode 100644 index e0a3a74..0000000 --- a/examples/native/README.md +++ /dev/null @@ -1,5 +0,0 @@ -ScriptForm native example -========================= - -This example shows how to create two simple forms with python functions as -backends. diff --git a/examples/native/native.json b/examples/native/native.json deleted file mode 100644 index e2d1faa..0000000 --- a/examples/native/native.json +++ /dev/null @@ -1,64 +0,0 @@ -{ - "title": "Test server", - "forms": { - "import": { - "title": "Import data", - "description": "Import CSV data into a database", - "submit_title": "Import", - "fields": [ - { - "name": "target_db", - "title": "Database to import to", - "type": "select", - "options": [ - ["devtest", "Dev Test db"], - ["prodtest", "Prod Test db"] - ] - }, - { - "name": "csv_file", - "title": "CSV file", - "type": "file" - } - ] - }, - "export": { - "title": "Export data", - "description": "Export a dump of the database", - "submit_title": "Export", - "fields": [ - { - "name": "source_db", - "title": "Database to export", - "type": "select", - "options": [ - ["devtest", "Dev Test db"], - ["prodtest", "Prod Test db"] - ] - } - ] - }, - "add_user": { - "title": "Add user", - "description": "Add a user to the htaccess file or change their password", - "submit_title": "Add user", - "fields": [ - { - "name": "username", - "title": "Username", - "type": "string" - }, - { - "name": "password1", - "title": "Password", - "type": "password" - }, - { - "name": "password2", - "title": "Password (Repear)", - "type": "password" - } - ] - } - } -} diff --git a/examples/native/native.py b/examples/native/native.py deleted file mode 100755 index 647a03e..0000000 --- a/examples/native/native.py +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/python - -import scriptform -import sys - -def job_import(values, request): - return "Importing into database '{}'".format(values['target_db']) - -def job_export(values, request): - size = 4096 * 10000 - request.wfile.write('HTTP/1.0 200 Ok\n') - request.wfile.write('Content-Type: application/octet-stream\n') - request.wfile.write('Content-Disposition: attachment; filename="large_file.dat"\n') - request.wfile.write('Content-Length: {0}\n\n'.format(size)) - - f = file('/dev/urandom', 'r') - sent_size = 0 - while True: - buf = f.read(4096) - if sent_size >= size: - break - request.wfile.write(buf) - sent_size += 4096 - -def job_add_user(values, request): - username = values['username'] - password1 = values['password1'] - password2 = values['password2'] - - if not password1: - raise Exception("Empty password specified") - - if password1 != password2: - raise Exception("Passwords do not match.") - - # We do some stuff here. - - return "User created" - -if __name__ == "__main__": - callbacks = { - 'import': job_import, - 'export': job_export, - 'add_user': job_add_user - } - sf = scriptform.ScriptForm('native.json', callbacks) - sf.run(listen_port=8000) diff --git a/src/scriptform.py b/src/scriptform.py index af897b4..a93bc48 100755 --- a/src/scriptform.py +++ b/src/scriptform.py @@ -163,15 +163,11 @@ class DaemonError(Exception): class ScriptForm: """ - 'Main' class that orchestrates parsing the Form configurations, hooking up - callbacks and running the webserver. + 'Main' class that orchestrates parsing the Form configurations and running + the webserver. """ - def __init__(self, config_file, callbacks=None): + def __init__(self, config_file): self.config_file = config_file - if callbacks: - self.callbacks = callbacks - else: - self.callbacks = {} self.basepath = os.path.realpath(os.path.dirname(config_file)) self.log = logging.getLogger('SCRIPTFORM') self.get_form_config() # Init form config so it can raise errors about problems. @@ -190,7 +186,6 @@ class ScriptForm: config = json.load(file(path, 'r')) forms = [] - callbacks = self.callbacks users = None if 'users' in config: @@ -214,7 +209,6 @@ class ScriptForm: form_config = FormConfig( config['title'], forms, - callbacks, users ) self.form_config_singleton = form_config @@ -247,20 +241,15 @@ class FormConfig: file. It holds information (title, users, the form definitions) on the form configuration being served by this instance of ScriptForm. """ - def __init__(self, title, forms, callbacks={}, users={}): + def __init__(self, title, forms, users={}): self.title = title self.users = users self.forms = forms - self.callbacks = callbacks # Validate scripts for form_def in self.forms: - if form_def.script: - if not stat.S_IXUSR & os.stat(form_def.script)[stat.ST_MODE]: - raise ScriptFormError("{0} is not executable".format(form_def.script)) - else: - if not form_def.name in self.callbacks: - raise ScriptFormError("No script or callback registered for '{0}'".format(form_def.name)) + if not stat.S_IXUSR & os.stat(form_def.script)[stat.ST_MODE]: + raise ScriptFormError("{0} is not executable".format(form_def.script)) def get_form_def(self, form_name): """ @@ -276,30 +265,26 @@ class FormConfig: def callback(self, form_name, form_values, request): """ - Perform a callback for the form `form_name`. This either calls a script - or a Python callable, depending on how the callback is registered. + Perform a callback for the form `form_name`. This calls a script. `form_values` is a dictionary of validated values as returned by FormDefinition.validate(). `request` is the request handler context - (ScriptFormWebApp). Scripts and Python callables can use it to send - their responses. + (ScriptFormWebApp). The output of the script is hooked up to the + output, depending on the output type. """ form = self.get_form_def(form_name) - if form.script: - return self.callback_script(form, form_values, request.wfile) - else: - return self.callback_python(form, form_values, request) - def callback_script(self, form, form_values, output_fh=None): - # Pass form values to the script through the environment as strings. os.chdir(os.path.dirname(form.script)) + # Pass form values to the script through the environment as strings. env = os.environ.copy() for k, v in form_values.items(): env[k] = str(v) if form.output == 'raw': - p = subprocess.Popen(form.script, shell=True, stdout=output_fh, - stderr=output_fh, env=env) + p = subprocess.Popen(form.script, shell=True, + stdout=request.wfile, + stderr=request.wfile, + env=env) stdout, stderr = p.communicate(input) return None else: @@ -313,27 +298,6 @@ class FormConfig: 'exitcode': p.returncode } - def callback_python(self, form, form_values, request): - callback = self.callbacks[form.name] - - try: - result = callback(form_values, request) - if result: - return { - 'stdout': result, - 'stderr': '', - 'exitcode': 0 - } - else: - # Raw output - pass - except Exception, e: - return { - 'stdout': '', - 'stderr': str(e), - 'exitcode': 1 - } - class FormDefinition: """ @@ -772,7 +736,7 @@ class ScriptFormWebApp(WebAppHandler): def h_submit(self, form_values): """ Handle the submitting of a form by validating the values and then doing - a callback to either a script or a Python function. How the output is + a callback to a script. How the output is handled depends on settings in the form definition. """ form_config = self.scriptform.get_form_config()