Fix native Python callbacks after refactoring.

pull/7/head
Ferry Boender 10 years ago
parent b0e635b768
commit e511678c07
  1. 10
      README.md
  2. 16
      examples/native/native.json
  3. 28
      examples/native/native.py
  4. 35
      src/scriptform.py

@ -9,8 +9,8 @@ as frontends to scripts.
ScriptForm takes a JSON file which contains form definitions. It then ScriptForm takes a JSON file which contains form definitions. It then
constructs web forms from this JSON and serves these to users. The user can constructs web forms from this JSON and serves these to users. The user can
select a form and fill it out. When the user submits the form, it is validated select a form and fill it out. When the user submits the form, it is validated
and the associated script is called. Data entered in the form is passed to the and the associated script or Python callback is called. Data entered in the
script through the environment. form is passed to the script through the environment.
### Features ### Features
@ -22,6 +22,8 @@ script through the environment.
- Uploaded files are automatically saved to temporary files, which are passed - Uploaded files are automatically saved to temporary files, which are passed
on to the callback. on to the callback.
- Multiple forms in a single JSON definition file. - Multiple forms in a single JSON definition file.
- Handles script / exception errors, HTML output or lets scripts and Python
callbacks stream their own HTTP response to the browser.
### Use-cases ### Use-cases
@ -106,9 +108,7 @@ ScriptForm requires:
## Usage ## Usage
### Authentication FIXME
Passwords are stored in plain text.
## License ## License

@ -22,6 +22,22 @@
} }
] ]
}, },
"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": { "add_user": {
"title": "Add user", "title": "Add user",
"description": "Add a user to the htaccess file or change their password", "description": "Add a user to the htaccess file or change their password",

@ -1,20 +1,37 @@
#!/usr/bin/python #!/usr/bin/python
import scriptform import scriptform
import sys
def job_import(values): def job_import(values, request):
return "Importing into database '{}'".format(values['target_db']) return "Importing into database '{}'".format(values['target_db'])
def job_add_user(values): 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'] username = values['username']
password1 = values['password1'] password1 = values['password1']
password2 = values['password2'] password2 = values['password2']
if not password1: if not password1:
return "Empty password specified." raise Exception("Empty password specified")
if password1 != password2: if password1 != password2:
return "Passwords do not match." raise Exception("Passwords do not match.")
# We do some stuff here. # We do some stuff here.
@ -23,7 +40,8 @@ def job_add_user(values):
if __name__ == "__main__": if __name__ == "__main__":
callbacks = { callbacks = {
'import': job_import, 'import': job_import,
'export': job_export,
'add_user': job_add_user 'add_user': job_add_user
} }
sf = scriptform.ScriptForm('native.json', callbacks) sf = scriptform.ScriptForm('native.json', callbacks)
sf.run(listen_port=8080) sf.run(listen_port=8000)

@ -6,6 +6,7 @@
# - Default values for input fields. # - Default values for input fields.
# - If there are errors in the form, its values are empties. # - If there are errors in the form, its values are empties.
# - Send responses using self.send_ if possible # - Send responses using self.send_ if possible
# - Complain about no registered callback on startup instead of serving.
import sys import sys
import optparse import optparse
@ -214,8 +215,8 @@ class FormConfig:
if not stat.S_IXUSR & os.stat(form_def.script)[stat.ST_MODE]: if not stat.S_IXUSR & os.stat(form_def.script)[stat.ST_MODE]:
raise ScriptFormError("{0} is not executable".format(form_def.script)) raise ScriptFormError("{0} is not executable".format(form_def.script))
else: else:
if not form_name in self.callbacks: if not form_def.name in self.callbacks:
raise ScriptFormError("No script or callback registered for '{0}'".format(form_name)) raise ScriptFormError("No script or callback registered for '{0}'".format(form_def.name))
def get_form(self, form_name): def get_form(self, form_name):
for form_def in self.forms: for form_def in self.forms:
@ -224,12 +225,12 @@ class FormConfig:
else: else:
raise ValueError("No such form: {0}".format(form_name)) raise ValueError("No such form: {0}".format(form_name))
def callback(self, form_name, form_values, output_fh=None): def callback(self, form_name, form_values, request):
form = self.get_form(form_name) form = self.get_form(form_name)
if form.script: if form.script:
return self.callback_script(form, form_values, output_fh) return self.callback_script(form, form_values, request.wfile)
else: else:
return self.callback_python(form, form_values, output_fh) return self.callback_python(form, form_values, request)
def callback_script(self, form, form_values, output_fh=None): def callback_script(self, form, form_values, output_fh=None):
# Pass form values to the script through the environment as strings. # Pass form values to the script through the environment as strings.
@ -253,8 +254,26 @@ class FormConfig:
'exitcode': p.returncode 'exitcode': p.returncode
} }
def callback_python(self, form, form_values, output_fh=None): def callback_python(self, form, form_values, request):
pass 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: class FormDefinition:
@ -744,7 +763,7 @@ class ScriptFormWebApp(WebAppHandler):
# in some nice HTML. If no result is returned, the output was raw # in some nice HTML. If no result is returned, the output was raw
# and the callback should have written its own response to the # and the callback should have written its own response to the
# self.wfile filehandle. # self.wfile filehandle.
result = form_config.callback(form_name, form_values, self.wfile) result = form_config.callback(form_name, form_values, self)
if result: if result:
if result['exitcode'] != 0: if result['exitcode'] != 0:
msg = '<span class="error">{0}</span>'.format(cgi.escape(result['stderr'])) msg = '<span class="error">{0}</span>'.format(cgi.escape(result['stderr']))

Loading…
Cancel
Save