diff options
Diffstat (limited to 'packages')
| -rw-r--r-- | packages/meta/task-python-everything_20060425.bb | 3 | ||||
| -rw-r--r-- | packages/meta/task-python-sharprom_20060425.bb | 3 | ||||
| -rw-r--r-- | packages/python/python-2.4.3-manifest.inc | 4 | ||||
| -rw-r--r-- | packages/python/python-webpy/.mtn2git_empty | 0 | ||||
| -rw-r--r-- | packages/python/python-webpy/web.py | 2349 | ||||
| -rw-r--r-- | packages/python/python-webpy_0.138.bb | 19 | ||||
| -rw-r--r-- | packages/python/python_2.4.3.bb | 2 |
7 files changed, 2376 insertions, 4 deletions
diff --git a/packages/meta/task-python-everything_20060425.bb b/packages/meta/task-python-everything_20060425.bb index 738b823d7b..213ed0f825 100644 --- a/packages/meta/task-python-everything_20060425.bb +++ b/packages/meta/task-python-everything_20060425.bb @@ -2,7 +2,7 @@ DESCRIPTION= "Everything Python" MAINTAINER = "Michael 'Mickey' Lauer <mickey@Vanille.de>" HOMEPAGE = "http://www.vanille.de/projects/python.spy" LICENSE = "MIT" -PR = "ml7" +PR = "ml8" BROKEN_BECAUSE_GCC4 = "\ python-egenix-mx-base" @@ -68,6 +68,7 @@ RDEPENDS = "\ python-urwid \ python-vmaps \ python-vorbis \ + python-webpy \ moin \ plone \ twisted \ diff --git a/packages/meta/task-python-sharprom_20060425.bb b/packages/meta/task-python-sharprom_20060425.bb index 8ce0dcf855..49841274ff 100644 --- a/packages/meta/task-python-sharprom_20060425.bb +++ b/packages/meta/task-python-sharprom_20060425.bb @@ -2,7 +2,7 @@ DESCRIPTION = "Everything Python for SharpROM" MAINTAINER = "Michael 'Mickey' Lauer <mickey@Vanille.de>" HOMEPAGE = "http://www.vanille.de/projects/python.spy" LICENSE = "MIT" -PR = "ml4" +PR = "ml5" NONWORKING = "\ python-codes \ @@ -63,6 +63,7 @@ RDEPENDS = "\ python-tlslite \ python-urwid \ python-vmaps \ + python-webpy \ moin \ plone \ twisted \ diff --git a/packages/python/python-2.4.3-manifest.inc b/packages/python/python-2.4.3-manifest.inc index abc6278e04..43d646dae4 100644 --- a/packages/python/python-2.4.3-manifest.inc +++ b/packages/python/python-2.4.3-manifest.inc @@ -1,5 +1,5 @@ ######################################################################################################################## -### AUTO-GENERATED by './generate-oe.py' [(C) 2002-2005 Michael 'Mickey' Lauer <mickey@Vanille.de>] on Wed May 10 13:57:16 2006 +### AUTO-GENERATED by './generate-oe.py' [(C) 2002-2005 Michael 'Mickey' Lauer <mickey@Vanille.de>] on Sat Jul 8 12:48:43 2006 ### ### Visit THE Python for Embedded Systems Site => http://www.Vanille.de/projects/python.spy ### @@ -8,6 +8,8 @@ ######################################################################################################################## +PROVIDES+="python-threading python-distutils python-textutils python-codecs python-pickle python-datetime python-core python-io python-compiler python-compression python-re python-xmlrpc python-terminal python-email python-image python-resource python-devel python-math python-hotshot python-unixadmin python-tkinter python-gdbm python-fcntl python-netclient python-pprint python-netserver python-curses python-syslog python-html python-readline python-pydoc python-logging python-mailbox python-xml python-mime python-tests python-unittest python-stringold python-lib-old-and-deprecated python-compile python-shell python-bsddb python-mmap python-zlib python-db python-crypt python-idle python-lang python-audio " + PACKAGES="python-threading python-distutils python-textutils python-codecs python-pickle python-datetime python-core python-io python-compiler python-compression python-re python-xmlrpc python-terminal python-email python-image python-resource python-devel python-math python-hotshot python-unixadmin python-tkinter python-gdbm python-fcntl python-netclient python-pprint python-netserver python-curses python-syslog python-html python-readline python-pydoc python-logging python-mailbox python-xml python-mime python-tests python-unittest python-stringold python-lib-old-and-deprecated python-compile python-shell python-bsddb python-mmap python-zlib python-db python-crypt python-idle python-lang python-audio " DESCRIPTION_python-threading="Python Threading & Synchronization Support" diff --git a/packages/python/python-webpy/.mtn2git_empty b/packages/python/python-webpy/.mtn2git_empty new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/packages/python/python-webpy/.mtn2git_empty diff --git a/packages/python/python-webpy/web.py b/packages/python/python-webpy/web.py new file mode 100644 index 0000000000..2761fa30a0 --- /dev/null +++ b/packages/python/python-webpy/web.py @@ -0,0 +1,2349 @@ +#!/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 + |
