summaryrefslogtreecommitdiff
path: root/packages
diff options
context:
space:
mode:
Diffstat (limited to 'packages')
-rw-r--r--packages/meta/task-python-everything_20060425.bb3
-rw-r--r--packages/meta/task-python-sharprom_20060425.bb3
-rw-r--r--packages/python/python-2.4.3-manifest.inc4
-rw-r--r--packages/python/python-webpy/.mtn2git_empty0
-rw-r--r--packages/python/python-webpy/web.py2349
-rw-r--r--packages/python/python-webpy_0.138.bb19
-rw-r--r--packages/python/python_2.4.3.bb2
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('<', '&lt;')
+ # 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
+