diff --git a/src/daemon.py b/src/daemon.py
index 4e92ff8..6a42b19 100644
--- a/src/daemon.py
+++ b/src/daemon.py
@@ -1,3 +1,7 @@
+"""
+Provide daemon capabilities via the Daemon class.
+"""
+
import logging
import os
import sys
@@ -8,10 +12,13 @@ import atexit
class DaemonError(Exception):
+ """
+ Default error for Daemon class.
+ """
pass
-class Daemon: # pragma: no cover
+class Daemon(object): # pragma: no cover
"""
Daemonize the current process (detach it from the console).
"""
@@ -27,17 +34,24 @@ class Daemon: # pragma: no cover
self.log_file = log_file
self.foreground = foreground
+ log_fmt = '%(asctime)s:%(name)s:%(levelname)s:%(message)s'
logging.basicConfig(level=log_level,
- format='%(asctime)s:%(name)s:%(levelname)s:%(message)s',
+ format=log_fmt,
filename=self.log_file,
filemode='a')
self.log = logging.getLogger('DAEMON')
- self.shutdown_cb = None
+ self.shutdown_callback = None
- def register_shutdown_cb(self, cb):
- self.shutdown_cb = cb
+ def register_shutdown_callback(self, callback):
+ """
+ Register a callback to be executed when the daemon is stopped.
+ """
+ self.shutdown_callback = callback
def start(self):
+ """
+ Start the daemon. Raises a DaemonError if it's already running.
+ """
self.log.info("Starting")
if self.is_running():
self.log.error('Already running')
@@ -46,6 +60,10 @@ class Daemon: # pragma: no cover
self._fork()
def stop(self):
+ """
+ Stop the daemon. Raises a DaemonError if the daemon is ot running,
+ which is determined by examaning the PID file.
+ """
if not self.is_running():
raise DaemonError("Not running")
@@ -101,6 +119,11 @@ class Daemon: # pragma: no cover
return True
def _fork(self):
+ """
+ Fork the current process daemon-style. Forks twice, closes file
+ descriptors, etc. A signal handler is also registered to be called if
+ the daemon received a SIGTERM signal.
+ """
# Fork a child and end the parent (detach from parent)
pid = os.fork()
if pid > 0:
@@ -114,9 +137,9 @@ class Daemon: # pragma: no cover
pid = os.fork()
if pid > 0:
self.log.info("PID = {0}".format(pid))
- f = file(self.pid_file, 'w')
- f.write(str(pid))
- f.close()
+ pidfile = file(self.pid_file, 'w')
+ pidfile.write(str(pid))
+ pidfile.close()
sys.exit(0) # End parent
atexit.register(self._cleanup)
@@ -124,9 +147,9 @@ class Daemon: # pragma: no cover
# Close STDIN, STDOUT and STDERR so we don't tie up the controlling
# terminal
- for fd in (0, 1, 2):
+ for fdescriptor in (0, 1, 2):
try:
- os.close(fd)
+ os.close(fdescriptor)
except OSError:
pass
@@ -139,7 +162,10 @@ class Daemon: # pragma: no cover
return pid
def _cleanup(self, sig=None):
+ """
+ Remvoe pid files and call registered shutodnw callbacks.
+ """
self.log.info("Received signal {0}".format(sig))
if os.path.exists(self.pid_file):
os.unlink(self.pid_file)
- self.shutdown_cb()
+ self.shutdown_callback()
diff --git a/src/formconfig.py b/src/formconfig.py
index 251b3d8..4eadea7 100644
--- a/src/formconfig.py
+++ b/src/formconfig.py
@@ -1,3 +1,9 @@
+"""
+FormConfig is the in-memory representation of a form configuration JSON file.
+It holds information (title, users, the form definitions) on the form
+configuration being served by this instance of ScriptForm.
+"""
+
import logging
import stat
import os
@@ -5,16 +11,20 @@ import subprocess
class FormConfigError(Exception):
+ """
+ Default error for FormConfig errors
+ """
pass
-class FormConfig:
+class FormConfig(object):
"""
FormConfig is the in-memory representation of a form configuration JSON
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, users=None, static_dir=None, custom_css=None):
+ def __init__(self, title, forms, users=None, static_dir=None,
+ custom_css=None):
self.title = title
self.users = {}
if users is not None:
@@ -27,7 +37,8 @@ class FormConfig:
# Validate scripts
for form_def in self.forms:
if not stat.S_IXUSR & os.stat(form_def.script)[stat.ST_MODE]:
- raise FormConfigError("{0} is not executable".format(form_def.script))
+ msg = "{0} is not executable".format(form_def.script)
+ raise FormConfigError(msg)
def get_form_def(self, form_name):
"""
@@ -70,29 +81,33 @@ class FormConfig:
# Validate params
if form.output == 'raw' and (stdout is None or stderr is None):
- raise ValueError('stdout and stderr cannot be None if script output is \'raw\'')
+ msg = 'stdout and stderr cannot be none if script output ' \
+ 'is \'raw\''
+ raise ValueError(msg)
# 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)
+ for key, value in form_values.items():
+ env[key] = str(value)
# If the form output type is 'raw', we directly stream the output to
# the browser. Otherwise we store it for later displaying.
if form.output == 'raw':
- p = subprocess.Popen(form.script, shell=True,
- stdout=stdout,
- stderr=stderr,
- env=env)
- stdout, stderr = p.communicate(input)
- return p.returncode
+ proc = subprocess.Popen(form.script, shell=True,
+ stdout=stdout,
+ stderr=stderr,
+ env=env)
+ stdout, stderr = proc.communicate(input)
+ return proc.returncode
else:
- p = subprocess.Popen(form.script, shell=True, stdin=subprocess.PIPE,
- stdout=subprocess.PIPE, stderr=subprocess.PIPE,
- env=env)
- stdout, stderr = p.communicate()
+ proc = subprocess.Popen(form.script, shell=True,
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ env=env)
+ stdout, stderr = proc.communicate()
return {
'stdout': stdout,
'stderr': stderr,
- 'exitcode': p.returncode
+ 'exitcode': proc.returncode
}
diff --git a/src/formdefinition.py b/src/formdefinition.py
index c1ceaed..fe7273d 100644
--- a/src/formdefinition.py
+++ b/src/formdefinition.py
@@ -1,12 +1,18 @@
+"""
+FormDefinition holds information about a single form and provides methods for
+validation of the form values.
+"""
+
import os
import datetime
class ValidationError(Exception):
+ """Default exception for Validation errors"""
pass
-class FormDefinition:
+class FormDefinition(object):
"""
FormDefinition holds information about a single form and provides methods
for validation of the form values.
@@ -25,6 +31,9 @@ class FormDefinition:
self.allowed_users = allowed_users
def get_field_def(self, field_name):
+ """
+ Return the field definition for `field_name`.
+ """
for field in self.fields:
if field['name'] == field_name:
return field
@@ -40,9 +49,11 @@ class FormDefinition:
# First make sure all required fields are there
for field in self.fields:
- if 'required' in field and \
- field['required'] is True and \
- (field['name'] not in form_values or form_values[field['name']] == ''):
+ field_required = ('required' in field and
+ field['required'] is True)
+ field_missing = (field['name'] not in form_values or
+ form_values[field['name']] == '')
+ if field_required and field_missing:
errors.setdefault(field['name'], []).append(
"This field is required"
)
@@ -51,14 +62,15 @@ class FormDefinition:
for field in self.fields:
field_name = field['name']
if field_name in errors:
- # Skip fields that are required but missing, since they can't be validated
+ # Skip fields that are required but missing, since they can't
+ # be validated
continue
try:
- v = self._field_validate(field_name, form_values)
- if v is not None:
- values[field_name] = v
- except ValidationError, e:
- errors.setdefault(field_name, []).append(str(e))
+ value = self._field_validate(field_name, form_values)
+ if value is not None:
+ values[field_name] = value
+ except ValidationError, err:
+ errors.setdefault(field_name, []).append(str(err))
return (errors, values)
@@ -75,6 +87,9 @@ class FormDefinition:
return validate_cb(field_def, form_values)
def validate_string(self, field_def, form_values):
+ """
+ Validate a form field of type 'string'.
+ """
value = form_values[field_def['name']]
maxlen = field_def.get('maxlen', None)
minlen = field_def.get('minlen', None)
@@ -87,6 +102,9 @@ class FormDefinition:
return value
def validate_integer(self, field_def, form_values):
+ """
+ Validate a form field of type 'integer'.
+ """
value = form_values[field_def['name']]
maxval = field_def.get('max', None)
minval = field_def.get('min', None)
@@ -104,6 +122,9 @@ class FormDefinition:
return int(value)
def validate_float(self, field_def, form_values):
+ """
+ Validate a form field of type 'float'.
+ """
value = form_values[field_def['name']]
maxval = field_def.get('max', None)
minval = field_def.get('min', None)
@@ -121,6 +142,9 @@ class FormDefinition:
return float(value)
def validate_date(self, field_def, form_values):
+ """
+ Validate a form field of type 'date'.
+ """
value = form_values[field_def['name']]
maxval = field_def.get('max', None)
minval = field_def.get('min', None)
@@ -140,6 +164,9 @@ class FormDefinition:
return value
def validate_radio(self, field_def, form_values):
+ """
+ Validate a form field of type 'radio'.
+ """
value = form_values[field_def['name']]
if not value in [o[0] for o in field_def['options']]:
raise ValidationError(
@@ -147,6 +174,9 @@ class FormDefinition:
return value
def validate_select(self, field_def, form_values):
+ """
+ Validate a form field of type 'select'.
+ """
value = form_values[field_def['name']]
if not value in [o[0] for o in field_def['options']]:
raise ValidationError(
@@ -154,6 +184,9 @@ class FormDefinition:
return value
def validate_checkbox(self, field_def, form_values):
+ """
+ Validate a form field of type 'checkbox'.
+ """
value = form_values.get(field_def['name'], 'off')
if not value in ['on', 'off']:
raise ValidationError(
@@ -161,6 +194,9 @@ class FormDefinition:
return value
def validate_text(self, field_def, form_values):
+ """
+ Validate a form field of type 'text'.
+ """
value = form_values[field_def['name']]
minlen = field_def.get('minlen', None)
maxlen = field_def.get('maxlen', None)
@@ -174,6 +210,9 @@ class FormDefinition:
return value
def validate_password(self, field_def, form_values):
+ """
+ Validate a form field of type 'password'.
+ """
value = form_values[field_def['name']]
minlen = field_def.get('minlen', None)
@@ -183,6 +222,9 @@ class FormDefinition:
return value
def validate_file(self, field_def, form_values):
+ """
+ Validate a form field of type 'file'.
+ """
value = form_values[field_def['name']]
field_name = field_def['name']
upload_fname = form_values[u'{0}__name'.format(field_name)]
@@ -190,6 +232,7 @@ class FormDefinition:
extensions = field_def.get('extensions', None)
if extensions is not None and upload_fname_ext not in extensions:
- raise ValidationError("Only file types allowed: {0}".format(u','.join(extensions)))
+ msg = "Only file types allowed: {0}".format(u','.join(extensions))
+ raise ValidationError(msg)
return value
diff --git a/src/formrender.py b/src/formrender.py
index 60cc8b9..8b34fb4 100644
--- a/src/formrender.py
+++ b/src/formrender.py
@@ -1,37 +1,74 @@
-html_field = u'''
+"""
+FormRender takes care of the rendering of forms to HTML.
+"""
+
+HTML_FIELD = u'''