fetches named parameters from the WSGI querystring, plus defaults for missing values, and type conversions so you dont accidentally have strings when you want numbers.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | """ EXAMPLE USAGE:
opts = Opts(environ,
opt('q', default=''),
opt('pages', default=2),
opt('split', default=0),
opt('simple', default=0),
opt('max_topics', default=40),
opt('ncol', default=3),
opt('save', default=False),
opt('load', default=False),
opt('smoothing', default='lidstone'),
opt('single_query', default=0),
opt('format', default='dev'),
)
... then opts is a hash.
"""
#### first, from anyall.org/util.py
def safehtml(x):
return cgi.escape(str(x),quote=True)
def unicodify(s, encoding='utf8', *args):
""" because {str,unicode}.{encode,decode} is anti-polymorphic, but sometimes
you can't control which you have. """
if isinstance(s,unicode): return s
if isinstance(s,str): return s.decode(encoding, *args)
return unicode(s)
class Struct(dict):
def __getattr__(self, a):
if a.startswith('__'):
raise AttributeError
return self[a]
def __setattr__(self, a, v):
self[a] = v
#### main code
type_builtin = type
def opt(name, type=None, default=None):
o = Struct(name=name, type=type, default=default)
if type is None:
if default is not None:
o.type = type_builtin(default)
else:
o.type = str #raise Exception("need type for %s" % name)
#if o.type==bool: o.type=int
return o
def type_clean(val,type):
if type==bool:
if val in (False,0,'0','f','false','False','no','n'): return False
if val in (True,1,'1','t','true','True','yes','y'): return True
raise Exception("bad bool value %s" % repr(val))
if type==str or type==unicode:
# nope no strings, you're gonna get unicode instead!
return unicodify(val)
return type(val)
class Opts(Struct):
" modelled on trollop.rubyforge.org and gist.github.com/5682 "
def __init__(self, environ, *optlist):
vars = cgi.parse_qs(environ['QUERY_STRING'])
for opt in optlist:
val = vars.get(opt.name)
val = val[0] if val else None
if val is None and opt.default is not None:
val = copy(opt.default)
elif val is None:
raise Exception("option not given: %s" % opt.name)
val = type_clean(val, opt.type)
self[opt.name] = val
def input(self, name, **kwargs):
val = self[name]
h = '''<input id=%s name=%s value="%s"''' % (name, name, safehtml(val))
more = {}
if type(val)==int:
more['size'] = 2
elif type(val)==float:
more['size'] = 4
more.update(kwargs)
for k,v in more.iteritems():
h += ''' %s="%s"''' % (k,v)
h += ">"
return h
|
Inspired by the commandline parser trollop.rubyforge.org. messing up manual type conversions out of string key/value land is a big source of bugs, at least for me.
bonus TODO: use optfunc's strategy of introspecting function args/types to override. (github.com/simonw/optfunc)
sorry for the UI stuff thrown in here -- it's useful for internal debugging sorts of tools, not so good for user-facing stuff.