diff options
Diffstat (limited to 'packages/python/python-webpy')
| -rw-r--r-- | packages/python/python-webpy/web.py | 2349 |
1 files changed, 0 insertions, 2349 deletions
diff --git a/packages/python/python-webpy/web.py b/packages/python/python-webpy/web.py deleted file mode 100644 index 2761fa30a0..0000000000 --- a/packages/python/python-webpy/web.py +++ /dev/null @@ -1,2349 +0,0 @@ -#!/usr/bin/env python -"""web.py: makes web apps (http://webpy.org)""" -__version__ = "0.1381" -__revision__ = "$Rev: 72 $" -__license__ = "public domain" -__author__ = "Aaron Swartz <me@aaronsw.com>" -__contributors__ = "see http://webpy.org/changes" - -from __future__ import generators - -# long term todo: -# - new form system -# - new templating system -# - unit tests? - -# todo: -# - get rid of upvars -# - break up into separate files -# - provide an option to use .write() -# - allow people to do $self.id from inside a reparam -# - add sqlite support -# - convert datetimes, floats in WebSafe -# - locks around memoize -# - fix memoize to use cacheify style techniques -# - merge curval query with the insert -# - figure out how to handle squid, etc. for web.ctx.ip - -import os, os.path, sys, time, types, traceback, threading -import cgi, re, urllib, urlparse, Cookie, pprint -from threading import currentThread -from tokenize import tokenprog -iters = (list, tuple) -if hasattr(__builtins__, 'set') or ( - hasattr(__builtins__, 'has_key') and __builtins__.has_key('set')): - iters += (set,) -try: - from sets import Set - iters += (Set,) -except ImportError: - pass -try: - import datetime, itertools -except ImportError: - pass -try: - from Cheetah.Compiler import Compiler - from Cheetah.Filters import Filter - _hasTemplating = True -except ImportError: - _hasTemplating = False - -try: - from DBUtils.PooledDB import PooledDB - _hasPooling = True -except ImportError: - _hasPooling = False - -# hack for compatibility with Python 2.3: -if not hasattr(traceback, 'format_exc'): - from cStringIO import StringIO - def format_exc(limit=None): - strbuf = StringIO() - traceback.print_exc(limit, strbuf) - return strbuf.getvalue() - traceback.format_exc = format_exc - -## General Utilities - -def _strips(direction, text, remove): - if direction == 'l': - if text.startswith(remove): - return text[len(remove):] - elif direction == 'r': - if text.endswith(remove): - return text[:-len(remove)] - else: - raise ValueError, "Direction needs to be r or l." - return text - -def rstrips(text, remove): - """removes the string `remove` from the right of `text`""" - return _strips('r', text, remove) - -def lstrips(text, remove): - """removes the string `remove` from the left of `text`""" - return _strips('l', text, remove) - -def strips(text, remove): - """removes the string `remove` from the both sides of `text`""" - return rstrips(lstrips(text, remove), remove) - -def autoassign(self, locals): - """ - Automatically assigns local variables to `self`. - Generally used in `__init__` methods, as in: - - def __init__(self, foo, bar, baz=1): autoassign(self, locals()) - """ - #locals = sys._getframe(1).f_locals - #self = locals['self'] - for (key, value) in locals.iteritems(): - if key == 'self': - continue - setattr(self, key, value) - -class Storage(dict): - """ - A Storage object is like a dictionary except `obj.foo` can be used - instead of `obj['foo']`. Create one by doing `storage({'a':1})`. - """ - def __getattr__(self, key): - if self.has_key(key): - return self[key] - raise AttributeError, repr(key) - def __setattr__(self, key, value): - self[key] = value - def __repr__(self): - return '<Storage ' + dict.__repr__(self) + '>' - -storage = Storage - -def storify(mapping, *requireds, **defaults): - """ - Creates a `storage` object from dictionary `mapping`, raising `KeyError` if - d doesn't have all of the keys in `requireds` and using the default - values for keys found in `defaults`. - - For example, `storify({'a':1, 'c':3}, b=2, c=0)` will return the equivalent of - `storage({'a':1, 'b':2, 'c':3})`. - - If a `storify` value is a list (e.g. multiple values in a form submission), - `storify` returns the last element of the list, unless the key appears in - `defaults` as a list. Thus: - - >>> storify({'a':[1, 2]}).a - 2 - >>> storify({'a':[1, 2]}, a=[]).a - [1, 2] - >>> storify({'a':1}, a=[]).a - [1] - >>> storify({}, a=[]).a - [] - - Similarly, if the value has a `value` attribute, `storify will return _its_ - value, unless the key appears in `defaults` as a dictionary. - - >>> storify({'a':storage(value=1)}).a - 1 - >>> storify({'a':storage(value=1)}, a={}).a - <Storage {'value': 1}> - >>> storify({}, a={}).a - {} - - """ - def getvalue(x): - if hasattr(x, 'value'): - return x.value - else: - return x - - stor = Storage() - for key in requireds + tuple(mapping.keys()): - value = mapping[key] - if isinstance(value, list): - if isinstance(defaults.get(key), list): - value = [getvalue(x) for x in value] - else: - value = value[-1] - if not isinstance(defaults.get(key), dict): - value = getvalue(value) - if isinstance(defaults.get(key), list) and not isinstance(value, list): - value = [value] - setattr(stor, key, value) - - for (key, value) in defaults.iteritems(): - result = value - if hasattr(stor, key): - result = stor[key] - if value == () and not isinstance(result, tuple): - result = (result,) - setattr(stor, key, result) - - return stor - -class Memoize: - """ - 'Memoizes' a function, caching its return values for each input. - """ - def __init__(self, func): - self.func = func - self.cache = {} - def __call__(self, *args, **keywords): - key = (args, tuple(keywords.items())) - if key not in self.cache: - self.cache[key] = self.func(*args, **keywords) - return self.cache[key] -memoize = Memoize - -re_compile = memoize(re.compile) #@@ threadsafe? -re_compile.__doc__ = """ -A memoized version of re.compile. -""" - -class _re_subm_proxy: - def __init__(self): - self.match = None - def __call__(self, match): - self.match = match - return '' - -def re_subm(pat, repl, string): - """Like re.sub, but returns the replacement _and_ the match object.""" - compiled_pat = re_compile(pat) - proxy = _re_subm_proxy() - compiled_pat.sub(proxy.__call__, string) - return compiled_pat.sub(repl, string), proxy.match - -def group(seq, size): - """ - Returns an iterator over a series of lists of length size from iterable. - - For example, `list(group([1,2,3,4], 2))` returns `[[1,2],[3,4]]`. - """ - if not hasattr(seq, 'next'): - seq = iter(seq) - while True: - yield [seq.next() for i in xrange(size)] - -class IterBetter: - """ - Returns an object that can be used as an iterator - but can also be used via __getitem__ (although it - cannot go backwards -- that is, you cannot request - `iterbetter[0]` after requesting `iterbetter[1]`). - """ - def __init__(self, iterator): - self.i, self.c = iterator, 0 - def __iter__(self): - while 1: - yield self.i.next() - self.c += 1 - def __getitem__(self, i): - #todo: slices - if i > self.c: - raise IndexError, "already passed "+str(i) - try: - while i < self.c: - self.i.next() - self.c += 1 - # now self.c == i - self.c += 1 - return self.i.next() - except StopIteration: - raise IndexError, str(i) -iterbetter = IterBetter - -def dictreverse(mapping): - """Takes a dictionary like `{1:2, 3:4}` and returns `{2:1, 4:3}`.""" - return dict([(value, key) for (key, value) in mapping.iteritems()]) - -def dictfind(dictionary, element): - """ - Returns a key whose value in `dictionary` is `element` - or, if none exists, None. - """ - for (key, value) in dictionary.iteritems(): - if element is value: - return key - -def dictfindall(dictionary, element): - """ - Returns the keys whose values in `dictionary` are `element` - or, if none exists, []. - """ - res = [] - for (key, value) in dictionary.iteritems(): - if element is value: - res.append(key) - return res - -def dictincr(dictionary, element): - """ - Increments `element` in `dictionary`, - setting it to one if it doesn't exist. - """ - dictionary.setdefault(element, 0) - dictionary[element] += 1 - return dictionary[element] - -def dictadd(dict_a, dict_b): - """ - Returns a dictionary consisting of the keys in `a` and `b`. - If they share a key, the value from b is used. - """ - result = {} - result.update(dict_a) - result.update(dict_b) - return result - -sumdicts = dictadd # deprecated - -def listget(lst, ind, default=None): - """Returns `lst[ind]` if it exists, `default` otherwise.""" - if len(lst)-1 < ind: - return default - return lst[ind] - -def intget(integer, default=None): - """Returns `integer` as an int or `default` if it can't.""" - try: - return int(integer) - except (TypeError, ValueError): - return default - -def datestr(then, now=None): - """Converts a (UTC) datetime object to a nice string representation.""" - def agohence(n, what, divisor=None): - if divisor: n = n // divisor - - out = str(abs(n)) + ' ' + what # '2 day' - if abs(n) != 1: out += 's' # '2 days' - out += ' ' # '2 days ' - if n < 0: - out += 'from now' - else: - out += 'ago' - return out # '2 days ago' - - oneday = 24 * 60 * 60 - - if not now: now = datetime.datetime.utcnow() - delta = now - then - deltaseconds = int(delta.days * oneday + delta.seconds + delta.microseconds * 1e-06) - deltadays = abs(deltaseconds) // oneday - if deltaseconds < 0: deltadays *= -1 # fix for oddity of floor - - if deltadays: - if abs(deltadays) < 4: - return agohence(deltadays, 'day') - - out = then.strftime('%B %e') # e.g. 'June 13' - if then.year != now.year or deltadays < 0: - out += ', %s' % then.year - return out - - if int(deltaseconds): - if abs(deltaseconds) > (60 * 60): - return agohence(deltaseconds, 'hour', 60 * 60) - elif abs(deltaseconds) > 60: - return agohence(deltaseconds, 'minute', 60) - else: - return agohence(deltaseconds, 'second') - - deltamicroseconds = delta.microseconds - if delta.days: deltamicroseconds = int(delta.microseconds - 1e6) # datetime oddity - if abs(deltamicroseconds) > 1000: - return agohence(deltamicroseconds, 'millisecond', 1000) - - return agohence(deltamicroseconds, 'microsecond') - -def upvars(level=2): - """Guido van Rossum doesn't want you to use this function.""" - return dictadd( - sys._getframe(level).f_globals, - sys._getframe(level).f_locals) - -class CaptureStdout: - """ - Captures everything func prints to stdout and returns it instead. - - **WARNING:** Not threadsafe! - """ - def __init__(self, func): - self.func = func - def __call__(self, *args, **keywords): - from cStringIO import StringIO - # Not threadsafe! - out = StringIO() - oldstdout = sys.stdout - sys.stdout = out - try: - self.func(*args, **keywords) - finally: - sys.stdout = oldstdout - return out.getvalue() -capturestdout = CaptureStdout - -class Profile: - """ - Profiles `func` and returns a tuple containing its output - and a string with human-readable profiling information. - """ - def __init__(self, func): - self.func = func - def __call__(self, *args): ##, **kw): kw unused - import hotshot, hotshot.stats, tempfile ##, time already imported - temp = tempfile.NamedTemporaryFile() - prof = hotshot.Profile(temp.name) - - stime = time.time() - result = prof.runcall(self.func, *args) - stime = time.time() - stime - - prof.close() - stats = hotshot.stats.load(temp.name) - stats.strip_dirs() - stats.sort_stats('time', 'calls') - x = '\n\ntook '+ str(stime) + ' seconds\n' - x += capturestdout(stats.print_stats)(40) - x += capturestdout(stats.print_callers)() - return result, x -profile = Profile - -def tryall(context, prefix=None): - """ - Tries a series of functions and prints their results. - `context` is a dictionary mapping names to values; - the value will only be tried if it's callable. - - For example, you might have a file `test/stuff.py` - with a series of functions testing various things in it. - At the bottom, have a line: - - if __name__ == "__main__": tryall(globals()) - - Then you can run `python test/stuff.py` and get the results of - all the tests. - """ - context = context.copy() # vars() would update - results = {} - for (key, value) in context.iteritems(): - if not hasattr(value, '__call__'): - continue - if prefix and not key.startswith(prefix): - continue - print key + ':', - try: - r = value() - dictincr(results, r) - print r - except: - print 'ERROR' - dictincr(results, 'ERROR') - print ' ' + '\n '.join(traceback.format_exc().split('\n')) - - print '-'*40 - print 'results:' - for (key, value) in results.iteritems(): - print ' '*2, str(key)+':', value - -class ThreadedDict: - """ - Takes a dictionary that maps threads to objects. - When a thread tries to get or set an attribute or item - of the threadeddict, it passes it on to the object - for that thread in dictionary. - """ - def __init__(self, dictionary): - self.__dict__['_ThreadedDict__d'] = dictionary - def __getattr__(self, attr): - return getattr(self.__d[currentThread()], attr) - def __getitem__(self, item): - return self.__d[currentThread()][item] - def __setattr__(self, attr, value): - if attr == '__doc__': - self.__dict__[attr] = value - else: - return setattr(self.__d[currentThread()], attr, value) - def __setitem__(self, item, value): - self.__d[currentThread()][item] = value - def __hash__(self): - return hash(self.__d[currentThread()]) -threadeddict = ThreadedDict - -## IP Utilities - -def validipaddr(address): - """returns True if `address` is a valid IPv4 address""" - try: - octets = address.split('.') - assert len(octets) == 4 - for x in octets: - assert 0 <= int(x) <= 255 - except (AssertionError, ValueError): - return False - return True - -def validipport(port): - """returns True if `port` is a valid IPv4 port""" - try: - assert 0 <= int(port) <= 65535 - except (AssertionError, ValueError): - return False - return True - -def validip(ip, defaultaddr="0.0.0.0", defaultport=8080): - """returns `(ip_address, port)` from string `ip_addr_port`""" - addr = defaultaddr - port = defaultport - - ip = ip.split(":", 1) - if len(ip) == 1: - if not ip[0]: - pass - elif validipaddr(ip[0]): - addr = ip[0] - elif validipport(ip[0]): - port = int(ip[0]) - else: - raise ValueError, ':'.join(ip) + ' is not a valid IP address/port' - elif len(ip) == 2: - addr, port = ip - if not validipaddr(addr) and validipport(port): - raise ValueError, ':'.join(ip) + ' is not a valid IP address/port' - port = int(port) - else: - raise ValueError, ':'.join(ip) + ' is not a valid IP address/port' - return (addr, port) - -def validaddr(string_): - """returns either (ip_address, port) or "/path/to/socket" from string_""" - if '/' in string_: - return string_ - else: - return validip(string_) - -## URL Utilities - -def prefixurl(base=''): - """ - Sorry, this function is really difficult to explain. - Maybe some other time. - """ - url = ctx.path.lstrip('/') - for i in xrange(url.count('/')): - base += '../' - if not base: - base = './' - return base - -def urlquote(x): return urllib.quote(websafe(x).encode('utf-8')) - -## Formatting - -try: - from markdown import markdown # http://webpy.org/markdown.py -except ImportError: - pass - -r_url = re_compile('(?<!\()(http://(\S+))') -def safemarkdown(text): - """ - Converts text to HTML following the rules of Markdown, but blocking any - outside HTML input, so that only the things supported by Markdown - can be used. Also converts raw URLs to links. - - (requires [markdown.py](http://webpy.org/markdown.py)) - """ - if text: - text = text.replace('<', '<') - # TODO: automatically get page title? - text = r_url.sub(r'<\1>', text) - text = markdown(text) - return text - -## Databases - -class _ItplError(ValueError): - """String Interpolation Error - from <http://lfw.org/python/Itpl.py> - (cf. below for license) - """ - def __init__(self, text, pos): - ValueError.__init__(self) - self.text = text - self.pos = pos - def __str__(self): - return "unfinished expression in %s at char %d" % ( - repr(self.text), self.pos) - -def _interpolate(format): - """ - Takes a format string and returns a list of 2-tuples of the form - (boolean, string) where boolean says whether string should be evaled - or not. - - from <http://lfw.org/python/Itpl.py> (public domain, Ka-Ping Yee) - """ - def matchorfail(text, pos): - match = tokenprog.match(text, pos) - if match is None: - raise _ItplError(text, pos) - return match, match.end() - - namechars = "abcdefghijklmnopqrstuvwxyz" \ - "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"; - chunks = [] - pos = 0 - - while 1: - dollar = format.find("$", pos) - if dollar < 0: - break - nextchar = format[dollar + 1] - - if nextchar == "{": - chunks.append((0, format[pos:dollar])) - pos, level = dollar + 2, 1 - while level: - match, pos = matchorfail(format, pos) - tstart, tend = match.regs[3] - token = format[tstart:tend] - if token == "{": - level = level + 1 - elif token == "}": - level = level - 1 - chunks.append((1, format[dollar + 2:pos - 1])) - - elif nextchar in namechars: - chunks.append((0, format[pos:dollar])) - match, pos = matchorfail(format, dollar + 1) - while pos < len(format): - if format[pos] == "." and \ - pos + 1 < len(format) and format[pos + 1] in namechars: - match, pos = matchorfail(format, pos + 1) - elif format[pos] in "([": - pos, level = pos + 1, 1 - while level: - match, pos = matchorfail(format, pos) - tstart, tend = match.regs[3] - token = format[tstart:tend] - if token[0] in "([": - level = level + 1 - elif token[0] in ")]": - level = level - 1 - else: - break - chunks.append((1, format[dollar + 1:pos])) - - else: - chunks.append((0, format[pos:dollar + 1])) - pos = dollar + 1 + (nextchar == "$") - - if pos < len(format): - chunks.append((0, format[pos:])) - return chunks - -def sqlors(left, lst): - """ - `left is a SQL clause like `tablename.arg = ` - and `lst` is a list of values. Returns a reparam-style - pair featuring the SQL that ORs together the clause - for each item in the lst. - - For example: - - web.sqlors('foo =', [1,2,3]) - - would result in: - - foo = 1 OR foo = 2 OR foo = 3 - """ - if isinstance(lst, iters): - lst = list(lst) - ln = len(lst) - if ln == 0: - return ("2+2=5", []) - if ln == 1: - lst = lst[0] - - if isinstance(lst, iters): - return '(' + left + \ - (' OR ' + left).join([aparam() for param in lst]) + ")", lst - else: - return left + aparam(), [lst] - -class UnknownParamstyle(Exception): - """raised for unsupported db paramstyles - - Currently supported: qmark,numeric, format, pyformat - """ - pass - -def aparam(): - """Use in a SQL string to make a spot for a db value.""" - style = ctx.db_module.paramstyle - if style == 'qmark': - return '?' - elif style == 'numeric': - return ':1' - elif style in ['format', 'pyformat']: - return '%s' - raise UnknownParamstyle, style - -def reparam(string_, dictionary): - """ - Takes a string and a dictionary and interpolates the string - using values from the dictionary. Returns a 2-tuple containing - the a string with `aparam()`s in it and a list of the matching values. - - You can pass this sort of thing as a clause in any db function. - Otherwise, you can pass a dictionary to the keyword argument `vars` - and the function will call reparam for you. - """ - vals = [] - result = [] - for live, chunk in _interpolate(string_): - if live: - result.append(aparam()) - vals.append(eval(chunk, dictionary)) - else: result.append(chunk) - return ''.join(result), vals - -class UnknownDB(Exception): - """raised for unsupported dbms""" - pass -def connect(dbn, **keywords): - """ - Connects to the specified database. - db currently must be "postgres" or "mysql". - If DBUtils is installed, connection pooling will be used. - """ - if dbn == "postgres": - try: - import psycopg2 as db - except ImportError: - try: - import psycopg as db - except ImportError: - import pgdb as db - keywords['password'] = keywords['pw'] - del keywords['pw'] - keywords['database'] = keywords['db'] - del keywords['db'] - elif dbn == "mysql": - import MySQLdb as db - keywords['passwd'] = keywords['pw'] - del keywords['pw'] - db.paramstyle = 'pyformat' # it's both, like psycopg - elif dbn == "sqlite": - try: ## try first sqlite3 version - from pysqlite2 import dbapi2 as db - db.paramstyle = 'qmark' - except ImportError: ## else try sqlite2 - import sqlite as db - keywords['database'] = keywords['db'] - del keywords['db'] - else: - raise UnknownDB, dbn - ctx.db_name = dbn - ctx.db_module = db - ctx.db_transaction = False - if _hasPooling: - if 'db' not in globals(): - globals()['db'] = PooledDB(dbapi=db, **keywords) - ctx.db = globals()['db'].connection() - else: - ctx.db = db.connect(**keywords) - ctx.dbq_count = 0 - if globals().get('db_printing'): - def db_execute(cur, sql_query, d=None): - """executes an sql query""" - - def sqlquote(obj): - """converts `obj` to its proper SQL version""" - - # because `1 == True and hash(1) == hash(True)` - # we have to do this the hard way... - - if obj is None: - return 'NULL' - elif obj is True: - return "'t'" - elif obj is False: - return "'f'" - elif isinstance(obj, datetime.datetime): - return repr(obj.isoformat()) - else: - return repr(obj) - - ctx.dbq_count += 1 - try: - outq = sql_query % tuple(map(sqlquote, d)) - except TypeError: - outq = sql_query - print >> debug, str(ctx.dbq_count)+':', outq - a = time.time() - out = cur.execute(sql_query, d) - b = time.time() - print >> debug, '(%s)' % round(b - a, 2) - return out - ctx.db_execute = db_execute - else: - ctx.db_execute = lambda cur, sql_query, d=None: \ - cur.execute(sql_query, d) - return ctx.db - -def transact(): - """Start a transaction.""" - # commit everything up to now, so we don't rollback it later - ctx.db.commit() - ctx.db_transaction = True - -def commit(): - """Commits a transaction.""" - ctx.db.commit() - ctx.db_transaction = False - -def rollback(): - """Rolls back a transaction.""" - ctx.db.rollback() - ctx.db_transaction = False - -def query(sql_query, vars=None, processed=False): - """ - Execute SQL query `sql_query` using dictionary `vars` to interpolate it. - If `processed=True`, `vars` is a `reparam`-style list to use - instead of interpolating. - """ - if vars is None: - vars = {} - db_cursor = ctx.db.cursor() - - if not processed: - sql_query, vars = reparam(sql_query, vars) - ctx.db_execute(db_cursor, sql_query, vars) - if db_cursor.description: - names = [x[0] for x in db_cursor.description] - def iterwrapper(): - row = db_cursor.fetchone() - while row: - yield Storage(dict(zip(names, row))) - row = db_cursor.fetchone() - out = iterbetter(iterwrapper()) - out.__len__ = lambda: int(db_cursor.rowcount) - out.list = lambda: [Storage(dict(zip(names, x))) \ - for x in db_cursor.fetchall()] - else: - out = db_cursor.rowcount - - if not ctx.db_transaction: - ctx.db.commit() - return out - -def sqllist(lst): - """ - If a list, converts it to a comma-separated string. - Otherwise, returns the string. - """ - if isinstance(lst, str): - return lst - else: return ', '.join(lst) - -def sqlwhere(dictionary): - """ - Converts a `dictionary` to an SQL WHERE clause in - `reparam` format. Thus, - - {'cust_id': 2, 'order_id':3} - - would result in the equivalent of: - - 'cust_id = 2 AND order_id = 3' - - but properly quoted. - """ - - return ' AND '.join([ - '%s = %s' % (k, aparam()) for k in dictionary.keys() - ]), dictionary.values() - -def select(tables, vars=None, what='*', where=None, order=None, group=None, - limit=None, offset=None): - """ - Selects `what` from `tables` with clauses `where`, `order`, - `group`, `limit`, and `offset. Uses vars to interpolate. - Otherwise, each clause can take a reparam-style list. - """ - if vars is None: - vars = {} - values = [] - qout = "" - - for (sql, val) in ( - ('SELECT', what), - ('FROM', sqllist(tables)), - ('WHERE', where), - ('GROUP BY', group), - ('ORDER BY', order), - ('LIMIT', limit), - ('OFFSET', offset)): - if isinstance(val, (int, long)): - if sql == 'WHERE': - nquery, nvalue = 'id = '+aparam(), [val] - else: - nquery, nvalue = str(val), () - elif isinstance(val, (list, tuple)) and len(val) == 2: - nquery, nvalue = val - elif val: - nquery, nvalue = reparam(val, vars) - else: - continue - qout += " " + sql + " " + nquery - values.extend(nvalue) - return query(qout, values, processed=True) - -def insert(tablename, seqname=None, **values): - """ - Inserts `values` into `tablename`. Returns current sequence ID. - Set `seqname` to the ID if it's not the default, or to `False` - if there isn't one. - """ - db_cursor = ctx.db.cursor() - - if values: - sql_query, v = "INSERT INTO %s (%s) VALUES (%s)" % ( - tablename, - ", ".join(values.keys()), - ', '.join([aparam() for x in values]) - ), values.values() - else: - sql_query, v = "INSERT INTO %s DEFAULT VALUES" % tablename, None - - if seqname is False: - pass - elif ctx.db_name == "postgres": - if seqname is None: - seqname = tablename + "_id_seq" - sql_query += "; SELECT currval('%s')" % seqname - elif ctx.db_name == "mysql": - ctx.db_execute(db_cursor, sql_query, v) - sql_query = "SELECT last_insert_id()" - v = () - elif ctx.db_name == "sqlite": - ctx.db_execute(db_cursor, sql_query, v) - # not really the same... - sql_query = "SELECT last_insert_rowid()" - v = () - - ctx.db_execute(db_cursor, sql_query, v) - try: - out = db_cursor.fetchone()[0] - except Exception: - out = None - - if not ctx.db_transaction: - ctx.db.commit() - - return out - -def update(tables, where, vars=None, **values): - """ - Update `tables` with clause `where` (interpolated using `vars`) - and setting `values`. - """ - if vars is None: - vars = {} - if isinstance(where, (int, long)): - vars = [where] - where = "id = " + aparam() - elif isinstance(where, (list, tuple)) and len(where) == 2: - where, vars = where - else: - where, vars = reparam(where, vars) - - db_cursor = ctx.db.cursor() - ctx.db_execute(db_cursor, "UPDATE %s SET %s WHERE %s" % ( - sqllist(tables), - ', '.join([k + '=' + aparam() for k in values.keys()]), - where), - values.values() + vars) - - if not ctx.db_transaction: - ctx.db.commit() - return db_cursor.rowcount - -def delete(table, where, using=None, vars=None): - """ - Deletes from `table` with clauses `where` and `using`. - """ - if vars is None: - vars = {} - db_cursor = ctx.db.cursor() - - if isinstance(where, (int, long)): - vars = [where] - where = "id = " + aparam() - elif isinstance(where, (list, tuple)) and len(where) == 2: - where, vars = where - else: - where, vars = reparam(where, vars) - q = 'DELETE FROM %s WHERE %s' % (table, where) - if using: - q += ' USING ' + sqllist(using) - ctx.db_execute(db_cursor, q, vars) - - if not ctx.db_transaction: - ctx.db.commit() - return db_cursor.rowcount - -## Request Handlers - -def handle(mapping, fvars=None): - """ - Call the appropriate function based on the url to function mapping in `mapping`. - If no module for the function is specified, look up the function in `fvars`. If - `fvars` is empty, using the caller's context. - - `mapping` should be a tuple of paired regular expressions with function name - substitutions. `handle` will import modules as necessary. - """ - for url, ofno in group(mapping, 2): - if isinstance(ofno, tuple): - ofn, fna = ofno[0], list(ofno[1:]) - else: - ofn, fna = ofno, [] - fn, result = re_subm('^' + url + '$', ofn, ctx.path) - if result: # it's a match - if fn.split(' ', 1)[0] == "redirect": - url = fn.split(' ', 1)[1] - if ctx.method == "GET": - x = ctx.env.get('QUERY_STRING', '') - if x: - url += '?' + x - return redirect(url) - elif '.' in fn: - x = fn.split('.') - mod, cls = '.'.join(x[:-1]), x[-1] - mod = __import__(mod, globals(), locals(), [""]) - cls = getattr(mod, cls) - else: - cls = fn - mod = fvars or upvars() - if isinstance(mod, types.ModuleType): - mod = vars(mod) - try: - cls = mod[cls] - except KeyError: - return notfound() - - meth = ctx.method - if meth == "HEAD": - if not hasattr(cls, meth): - meth = "GET" - if not hasattr(cls, meth): - return nomethod(cls) - tocall = getattr(cls(), meth) - args = list(result.groups()) - for d in re.findall(r'\\(\d+)', ofn): - args.pop(int(d) - 1) - return tocall(*([urllib.unquote(x) for x in args] + fna)) - - return notfound() - -def autodelegate(prefix=''): - """ - Returns a method that takes one argument and calls the method named prefix+arg, - calling `notfound()` if there isn't one. Example: - - urls = ('/prefs/(.*)', 'prefs') - - class prefs: - GET = autodelegate('GET_') - def GET_password(self): pass - def GET_privacy(self): pass - - `GET_password` would get called for `/prefs/password` while `GET_privacy` for - `GET_privacy` gets called for `/prefs/privacy`. - - If a user visits `/prefs/password/change` then `GET_password(self, '/change')` - is called. - """ - def internal(self, arg): - if '/' in arg: - first, rest = arg.split('/', 1) - func = prefix + first - args = ['/' + rest] - else: - func = prefix + arg - args = [] - - if hasattr(self, func): - try: - return getattr(self, func)(*args) - except TypeError: - return notfound() - else: - return notfound() - return internal - -def background(func): - """A function decorator to run a long-running function as a background thread.""" - def internal(*a, **kw): - data() # cache it - ctx = _context[currentThread()] - _context[currentThread()] = storage(ctx.copy()) - - def newfunc(): - _context[currentThread()] = ctx - func(*a, **kw) - - t = threading.Thread(target=newfunc) - background.threaddb[id(t)] = t - t.start() - ctx.headers = [] - return seeother(changequery(_t=id(t))) - return internal -backg |
