summaryrefslogtreecommitdiff
path: root/packages/python/python-webpy
diff options
context:
space:
mode:
Diffstat (limited to 'packages/python/python-webpy')
-rw-r--r--packages/python/python-webpy/web.py2349
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('<', '&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
-
- 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