commit python-web.py for openSUSE:Factory
Hello community, here is the log from the commit of package python-web.py for openSUSE:Factory checked in at 2017-01-25 23:32:38 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-web.py (Old) and /work/SRC/openSUSE:Factory/.python-web.py.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Package is "python-web.py" Changes: -------- --- /work/SRC/openSUSE:Factory/python-web.py/python-web.py.changes 2012-07-02 11:07:48.000000000 +0200 +++ /work/SRC/openSUSE:Factory/.python-web.py.new/python-web.py.changes 2017-01-25 23:32:39.450462416 +0100 @@ -1,0 +2,31 @@ +Mon Jan 23 21:10:49 UTC 2017 - dmueller@suse.com + +- fix source url + +------------------------------------------------------------------- +Wed Nov 30 21:18:23 UTC 2016 - michael@stroeder.com + +- Updated to 0.38 + * Fixed failing tests in test/session.py when postgres is not installed. (tx Michael Diamond) + * Fixed an error with Python 2.3 (tx Michael Diamond) + * web.database now accepts a URL, $DATABASE_URL (fixes #171) (tx Aaron Swartz, we miss you) + * support port use 'port' as keyword for postgres database with used eith pgdb (tx Sandesh Singh) + * Fixes to FirebirdDB database (tx Ben Hanna) + * Added a gaerun method to start application for google app engine (tx Matt Habel) + * Better error message from `db.multiple_insert` when not all rows have the same keys (tx Ben Hoyt) + * Allow custom messages for most errors (tx Shaun Sharples) + * IPv6 support (tx Matthew of Boswell and zamabe) + * Fixed sending email using Amazon SES (tx asldevi) + * Fixed handling of long numbers in sqlify. closes #213. (tx cjrolo) + * Escape HTML characters when emitting API docs. (tx Jeff Zellman) + * Fixed an inconsistency in form.Dropdown when numbers are used for args and value. (tx Noprianto) + * Fixed a potential remote exeution risk in `reparam` (tx Adrián Brav) + * The where clause in db queries can be a dict now + * Added `first` method to iterbetter + * Fix to unexpected session when used with MySQL (tx suhashpatil) + * Change dburl2dict to use urlparse and to support the simple case of just a database name. (tx Jeff Zellman) + * Support '204 No Content' status code (tx Matteo Landi) + * Support `451 Unavailable For Legal Reasons` status code(tx Yannik Robin Kettenbach) + * Updates to documentation (tx goodrone, asldevi) + +------------------------------------------------------------------- Old: ---- web.py-0.37.tar.gz New: ---- web.py-0.38.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-web.py.spec ++++++ --- /var/tmp/diff_new_pack.D6Q5G7/_old 2017-01-25 23:32:39.826405762 +0100 +++ /var/tmp/diff_new_pack.D6Q5G7/_new 2017-01-25 23:32:39.826405762 +0100 @@ -1,7 +1,7 @@ # # spec file for package python-web.py # -# Copyright (c) 2012 SUSE LINUX Products GmbH, Nuernberg, Germany. +# Copyright (c) 2017 SUSE LINUX GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -17,13 +17,13 @@ Name: python-web.py -Version: 0.37 +Version: 0.38 Release: 0 Url: http://webpy.org/ Summary: web.py: makes web apps License: SUSE-Public-Domain and BSD-3-Clause Group: Development/Languages/Python -Source: http://pypi.python.org/packages/source/w/web.py/web.py-%{version}.tar.gz +Source: https://pypi.io/packages/source/w/web.py/web.py-%{version}.tar.gz BuildRoot: %{_tmppath}/%{name}-%{version}-build BuildRequires: python-devel %if 0%{?suse_version} && 0%{?suse_version} <= 1110 ++++++ web.py-0.37.tar.gz -> web.py-0.38.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/web.py-0.37/PKG-INFO new/web.py-0.38/PKG-INFO --- old/web.py-0.37/PKG-INFO 2012-06-26 07:21:31.000000000 +0200 +++ new/web.py-0.38/PKG-INFO 2016-07-08 09:07:25.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 1.0 Name: web.py -Version: 0.37 +Version: 0.38 Summary: web.py: makes web apps Home-page: http://webpy.org/ Author: Anand Chitipothu diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/web.py-0.37/web/__init__.py new/web.py-0.38/web/__init__.py --- old/web.py-0.37/web/__init__.py 2012-06-26 07:19:55.000000000 +0200 +++ new/web.py-0.38/web/__init__.py 2016-07-08 09:03:09.000000000 +0200 @@ -3,7 +3,7 @@ from __future__ import generators -__version__ = "0.37" +__version__ = "0.38" __author__ = [ "Aaron Swartz <me@aaronsw.com>", "Anand Chitipothu <anandology@gmail.com>" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/web.py-0.37/web/application.py new/web.py-0.38/web/application.py --- old/web.py-0.37/web/application.py 2012-06-26 07:19:55.000000000 +0200 +++ new/web.py-0.38/web/application.py 2016-07-08 09:03:09.000000000 +0200 @@ -334,7 +334,49 @@ except ImportError: # we're not running from within Google App Engine return wsgiref.handlers.CGIHandler().run(wsgiapp) - + + def gaerun(self, *middleware): + """ + Starts the program in a way that will work with Google app engine, + no matter which version you are using (2.5 / 2.7) + + If it is 2.5, just normally start it with app.gaerun() + + If it is 2.7, make sure to change the app.yaml handler to point to the + global variable that contains the result of app.gaerun() + + For example: + + in app.yaml (where code.py is where the main code is located) + + handlers: + - url: /.* + script: code.app + + Make sure that the app variable is globally accessible + """ + wsgiapp = self.wsgifunc(*middleware) + try: + # check what version of python is running + version = sys.version_info[:2] + major = version[0] + minor = version[1] + + if major != 2: + raise EnvironmentError("Google App Engine only supports python 2.5 and 2.7") + + # if 2.7, return a function that can be run by gae + if minor == 7: + return wsgiapp + # if 2.5, use run_wsgi_app + elif minor == 5: + from google.appengine.ext.webapp.util import run_wsgi_app + return run_wsgi_app(wsgiapp) + else: + raise EnvironmentError("Not a supported platform, use python 2.5 or 2.7") + except ImportError: + return wsgiref.handlers.CGIHandler().run(wsgiapp) + def load(self, env): """Initializes ctx using env.""" ctx = web.ctx diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/web.py-0.37/web/db.py new/web.py-0.38/web/db.py --- old/web.py-0.37/web/db.py 2012-06-26 07:19:55.000000000 +0200 +++ new/web.py-0.38/web/db.py 2016-07-08 09:03:09.000000000 +0200 @@ -11,7 +11,7 @@ "database", 'DB', ] -import time +import time, os, urllib, urlparse try: import datetime except ImportError: @@ -296,6 +296,8 @@ <sql: 's IN (1, 2)'> """ dictionary = dictionary.copy() # eval mucks with it + # disable builtins to avoid risk for remote code exection. + dictionary['__builtins__'] = object() vals = [] result = [] for live, chunk in _interpolate(string_): @@ -326,6 +328,8 @@ return "'t'" elif obj is False: return "'f'" + elif isinstance(obj, long): + return str(obj) elif datetime and isinstance(obj, datetime.datetime): return repr(obj.isoformat()) else: @@ -613,11 +617,22 @@ #@@@ for backward-compatibility elif isinstance(where, (list, tuple)) and len(where) == 2: where = SQLQuery(where[0], where[1]) + elif isinstance(where, dict): + where = self._where_dict(where) elif isinstance(where, SQLQuery): pass else: where = reparam(where, vars) return where + + def _where_dict(self, where): + where_clauses = [] + for k, v in where.iteritems(): + where_clauses.append(k + ' = ' + sqlquote(v)) + if where_clauses: + return SQLQuery.join(where_clauses, " AND ") + else: + return None def query(self, sql_query, vars=None, processed=False, _test=False): """ @@ -673,6 +688,8 @@ <sql: 'SELECT * FROM foo'> >>> db.select(['foo', 'bar'], where="foo.bar_id = bar.id", limit=5, _test=True) <sql: 'SELECT * FROM foo, bar WHERE foo.bar_id = bar.id LIMIT 5'> + >>> db.select('foo', where={'id': 5}, _test=True) + <sql: 'SELECT * FROM foo WHERE id = 5'> """ if vars is None: vars = {} sql_clauses = self.sql_clauses(what, tables, where, group, order, limit, offset) @@ -694,15 +711,7 @@ >>> db.where('foo', _test=True) <sql: 'SELECT * FROM foo'> """ - where_clauses = [] - for k, v in kwargs.iteritems(): - where_clauses.append(k + ' = ' + sqlquote(v)) - - if where_clauses: - where = SQLQuery.join(where_clauses, " AND ") - else: - where = None - + where = self._where_dict(kwargs) return self.select(table, what=what, order=order, group=group, limit=limit, offset=offset, _test=_test, where=where) @@ -726,6 +735,8 @@ #@@@ elif isinstance(val, (list, tuple)) and len(val) == 2: nout = SQLQuery(val[0], val[1]) # backwards-compatibility + elif sql == 'WHERE' and isinstance(val, dict): + nout = self._where_dict(val) elif isinstance(val, SQLQuery): nout = val else: @@ -815,10 +826,9 @@ keys = values[0].keys() #@@ make sure all keys are valid - # make sure all rows have same keys. for v in values: if v.keys() != keys: - raise ValueError, 'Bad data' + raise ValueError, 'Not all rows have the same keys' sql_query = SQLQuery('INSERT INTO %s (%s) VALUES ' % (tablename, ', '.join(keys))) @@ -926,6 +936,8 @@ if db_module.__name__ == "psycopg2": import psycopg2.extensions psycopg2.extensions.register_type(psycopg2.extensions.UNICODE) + if db_module.__name__ == "pgdb" and 'port' in keywords: + keywords["host"] += ":" + str(keywords.pop('port')) # if db is not provided postgres driver will take it from PGDATABASE environment variable if 'db' in keywords: @@ -1042,10 +1054,11 @@ db = None pass if 'pw' in keywords: - keywords['passwd'] = keywords['pw'] - del keywords['pw'] - keywords['database'] = keywords['db'] - del keywords['db'] + keywords['password'] = keywords.pop('pw') + keywords['database'] = keywords.pop('db') + + self.paramstyle = db.paramstyle + DB.__init__(self, db, keywords) def delete(self, table, where=None, using=None, vars=None, _test=False): @@ -1131,6 +1144,30 @@ else: return query + "; SELECT %s.currval FROM dual" % seqname +def dburl2dict(url): + """ + Takes a URL to a database and parses it into an equivalent dictionary. + + >>> dburl2dict('postgres:///mygreatdb') + {'pw': None, 'dbn': 'postgres', 'db': 'mygreatdb', 'host': None, 'user': None, 'port': None} + >>> dburl2dict('postgres://james:day@serverfarm.example.net:5432/mygreatdb') + {'pw': 'day', 'dbn': 'postgres', 'db': 'mygreatdb', 'host': 'serverfarm.example.net', 'user': 'james', 'port': 5432} + >>> dburl2dict('postgres://james:day@serverfarm.example.net/mygreatdb') + {'pw': 'day', 'dbn': 'postgres', 'db': 'mygreatdb', 'host': 'serverfarm.example.net', 'user': 'james', 'port': None} + >>> dburl2dict('postgres://james:d%40y@serverfarm.example.net/mygreatdb') + {'pw': 'd@y', 'dbn': 'postgres', 'db': 'mygreatdb', 'host': 'serverfarm.example.net', 'user': 'james', 'port': None} + >>> dburl2dict('mysql://james:d%40y@serverfarm.example.net/mygreatdb') + {'pw': 'd@y', 'dbn': 'mysql', 'db': 'mygreatdb', 'host': 'serverfarm.example.net', 'user': 'james', 'port': None} + """ + parts = urlparse.urlparse(urllib.unquote(url)) + + return {'dbn': parts.scheme, + 'user': parts.username, + 'pw': parts.password, + 'db': parts.path[1:], + 'host': parts.hostname, + 'port': parts.port} + _databases = {} def database(dburl=None, **params): """Creates appropriate database using params. @@ -1138,6 +1175,10 @@ Pooling will be enabled if DBUtils module is available. Pooling can be disabled by passing pooling=False in params. """ + if not dburl and not params: + dburl = os.environ['DATABASE_URL'] + if dburl: + params = dburl2dict(dburl) dbn = params.pop('dbn') if dbn in _databases: return _databases[dbn](**params) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/web.py-0.37/web/form.py new/web.py-0.38/web/form.py --- old/web.py-0.37/web/form.py 2012-06-26 07:19:55.000000000 +0200 +++ new/web.py-0.38/web/form.py 2016-07-08 09:03:09.000000000 +0200 @@ -253,7 +253,13 @@ else: value, desc = arg, arg - if self.value == value or (isinstance(self.value, list) and value in self.value): + value = utils.safestr(value) + if isinstance(self.value, (tuple, list)): + s_value = [utils.safestr(x) for x in self.value] + else: + s_value = utils.safestr(self.value) + + if s_value == value or (isinstance(s_value, list) and value in s_value): select_p = ' selected="selected"' else: select_p = '' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/web.py-0.37/web/net.py new/web.py-0.38/web/net.py --- old/web.py-0.37/web/net.py 2012-06-26 07:19:55.000000000 +0200 +++ new/web.py-0.38/web/net.py 2016-07-08 09:03:09.000000000 +0200 @@ -4,7 +4,7 @@ """ __all__ = [ - "validipaddr", "validipport", "validip", "validaddr", + "validipaddr", "validip6addr", "validipport", "validip", "validaddr", "urlquote", "httpdate", "parsehttpdate", "htmlquote", "htmlunquote", "websafe", @@ -13,6 +13,28 @@ import urllib, time try: import datetime except ImportError: pass +import re +import socket + +def validip6addr(address): + """ + Returns True if `address` is a valid IPv6 address. + + >>> validip6addr('::') + True + >>> validip6addr('aaaa:bbbb:cccc:dddd::1') + True + >>> validip6addr('1:2:3:4:5:6:7:8:9:10') + False + >>> validip6addr('12:10') + False + """ + try: + socket.inet_pton(socket.AF_INET6, address) + except (socket.error, AttributeError): + return False + + return True def validipaddr(address): """ @@ -55,10 +77,37 @@ return True def validip(ip, defaultaddr="0.0.0.0", defaultport=8080): - """Returns `(ip_address, port)` from string `ip_addr_port`""" + """ + Returns `(ip_address, port)` from string `ip_addr_port` + >>> validip('1.2.3.4') + ('1.2.3.4', 8080) + >>> validip('80') + ('0.0.0.0', 80) + >>> validip('192.168.0.1:85') + ('192.168.0.1', 85) + >>> validip('::') + ('::', 8080) + >>> validip('[::]:88') + ('::', 88) + >>> validip('[::1]:80') + ('::1', 80) + + """ addr = defaultaddr port = defaultport + #Matt Boswell's code to check for ipv6 first + match = re.search(r'^\[([^]]+)\](?::(\d+))?$',ip) #check for [ipv6]:port + if match: + if validip6addr(match.group(1)): + if match.group(2): + if validipport(match.group(2)): return (match.group(1),int(match.group(2))) + else: + return (match.group(1),port) + else: + if validip6addr(ip): return (ip,port) + #end ipv6 code + ip = ip.split(":", 1) if len(ip) == 1: if not ip[0]: @@ -90,6 +139,8 @@ ('127.0.0.1', 8080) >>> validaddr('127.0.0.1:8000') ('127.0.0.1', 8000) + >>> validip('[::1]:80') + ('::1', 80) >>> validaddr('fff') Traceback (most recent call last): ... diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/web.py-0.37/web/session.py new/web.py-0.38/web/session.py --- old/web.py-0.37/web/session.py 2012-06-26 07:19:55.000000000 +0200 +++ new/web.py-0.38/web/session.py 2016-07-08 09:03:09.000000000 +0200 @@ -306,9 +306,9 @@ pickled = self.encode(value) now = datetime.datetime.now() if key in self: - self.db.update(self.table, where="session_id=$key", data=pickled, vars=locals()) + self.db.update(self.table, where="session_id=$key", data=pickled,atime=now, vars=locals()) else: - self.db.insert(self.table, False, session_id=key, data=pickled ) + self.db.insert(self.table, False, session_id=key, atime=now, data=pickled ) def __delitem__(self, key): self.db.delete(self.table, where="session_id=$key", vars=locals()) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/web.py-0.37/web/utils.py new/web.py-0.38/web/utils.py --- old/web.py-0.37/web/utils.py 2012-06-26 07:19:55.000000000 +0200 +++ new/web.py-0.38/web/utils.py 2016-07-08 09:03:09.000000000 +0200 @@ -645,6 +645,15 @@ ... IndexError: already passed 3 + It is also possible to get the first value of the iterator or None. + + >>> c = iterbetter(iter([3, 4, 5])) + >>> print c.first() + 3 + >>> c = iterbetter(iter([])) + >>> print c.first() + None + For boolean test, IterBetter peeps at first value in the itertor without effecting the iteration. >>> c = iterbetter(iter(range(5))) @@ -661,6 +670,18 @@ def __init__(self, iterator): self.i, self.c = iterator, 0 + def first(self, default=None): + """Returns the first element of the iterator or None when there are no + elements. + + If the optional argument default is specified, that is returned instead + of None when there are no elements. + """ + try: + return iter(self).next() + except StopIteration: + return default + def __iter__(self): if hasattr(self, "_head"): yield self._head @@ -1493,7 +1514,7 @@ c = boto.ses.SESConnection( aws_access_key_id=webapi.config.get('aws_access_key_id'), aws_secret_access_key=web.api.config.get('aws_secret_access_key')) - c.send_raw_email(self.from_address, message_text, self.from_recipients) + c.send_raw_email(self.from_address, message_text, self.recipients) else: sendmail = webapi.config.get('sendmail_path', '/usr/sbin/sendmail') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/web.py-0.37/web/webapi.py new/web.py-0.38/web/webapi.py --- old/web.py-0.37/web/webapi.py 2012-06-26 07:19:55.000000000 +0200 +++ new/web.py-0.38/web/webapi.py 2016-07-08 09:03:09.000000000 +0200 @@ -8,23 +8,23 @@ "header", "debug", "input", "data", "setcookie", "cookies", - "ctx", - "HTTPError", + "ctx", + "HTTPError", + + # 200, 201, 202, 204 + "OK", "Created", "Accepted", "NoContent", + "ok", "created", "accepted", "nocontent", - # 200, 201, 202 - "OK", "Created", "Accepted", - "ok", "created", "accepted", - # 301, 302, 303, 304, 307 - "Redirect", "Found", "SeeOther", "NotModified", "TempRedirect", + "Redirect", "Found", "SeeOther", "NotModified", "TempRedirect", "redirect", "found", "seeother", "notmodified", "tempredirect", - # 400, 401, 403, 404, 405, 406, 409, 410, 412, 415 - "BadRequest", "Unauthorized", "Forbidden", "NotFound", "NoMethod", "NotAcceptable", "Conflict", "Gone", "PreconditionFailed", "UnsupportedMediaType", - "badrequest", "unauthorized", "forbidden", "notfound", "nomethod", "notacceptable", "conflict", "gone", "preconditionfailed", "unsupportedmediatype", + # 400, 401, 403, 404, 405, 406, 409, 410, 412, 415, 451 + "BadRequest", "Unauthorized", "Forbidden", "NotFound", "NoMethod", "NotAcceptable", "Conflict", "Gone", "PreconditionFailed", "UnsupportedMediaType", "UnavailableForLegalReasons", + "badrequest", "unauthorized", "forbidden", "notfound", "nomethod", "notacceptable", "conflict", "gone", "preconditionfailed", "unsupportedmediatype", "unavailableforlegalreasons", # 500 - "InternalError", + "InternalError", "internalerror", ] @@ -46,16 +46,16 @@ header(k, v) self.data = data Exception.__init__(self, status) - + def _status_code(status, data=None, classname=None, docstring=None): if data is None: data = status.split(" ", 1)[1] - classname = status.split(" ", 1)[1].replace(' ', '') # 304 Not Modified -> NotModified + classname = status.split(" ", 1)[1].replace(' ', '') # 304 Not Modified -> NotModified docstring = docstring or '`%s` status' % status def __init__(self, data=data, headers={}): HTTPError.__init__(self, status, headers, data) - + # trick to create class dynamically with dynamic docstring. return type(classname, (HTTPError, object), { '__doc__': docstring, @@ -65,13 +65,14 @@ ok = OK = _status_code("200 OK", data="") created = Created = _status_code("201 Created") accepted = Accepted = _status_code("202 Accepted") +nocontent = NoContent = _status_code("204 No Content") class Redirect(HTTPError): """A `301 Moved Permanently` redirect.""" def __init__(self, url, status='301 Moved Permanently', absolute=False): """ - Returns a `status` redirect to the new URL. - `url` is joined with the base URL so that things like + Returns a `status` redirect to the new URL. + `url` is joined with the base URL so that things like `redirect("about") will work properly. """ newloc = urlparse.urljoin(ctx.path, url) @@ -102,7 +103,7 @@ """A `303 See Other` redirect.""" def __init__(self, url, absolute=False): Redirect.__init__(self, url, '303 See Other', absolute=absolute) - + seeother = SeeOther class NotModified(HTTPError): @@ -132,20 +133,20 @@ class Unauthorized(HTTPError): """`401 Unauthorized` error.""" message = "unauthorized" - def __init__(self): + def __init__(self, message=None): status = "401 Unauthorized" headers = {'Content-Type': 'text/html'} - HTTPError.__init__(self, status, headers, self.message) + HTTPError.__init__(self, status, headers, message or self.message) unauthorized = Unauthorized class Forbidden(HTTPError): """`403 Forbidden` error.""" message = "forbidden" - def __init__(self): + def __init__(self, message=None): status = "403 Forbidden" headers = {'Content-Type': 'text/html'} - HTTPError.__init__(self, status, headers, self.message) + HTTPError.__init__(self, status, headers, message or self.message) forbidden = Forbidden @@ -175,7 +176,7 @@ status = '405 Method Not Allowed' headers = {} headers['Content-Type'] = 'text/html' - + methods = ['GET', 'HEAD', 'POST', 'PUT', 'DELETE'] if cls: methods = [method for method in methods if hasattr(cls, method)] @@ -183,63 +184,83 @@ headers['Allow'] = ', '.join(methods) data = None HTTPError.__init__(self, status, headers, data) - + nomethod = NoMethod class NotAcceptable(HTTPError): """`406 Not Acceptable` error.""" message = "not acceptable" - def __init__(self): + def __init__(self, message=None): status = "406 Not Acceptable" headers = {'Content-Type': 'text/html'} - HTTPError.__init__(self, status, headers, self.message) + HTTPError.__init__(self, status, headers, message or self.message) notacceptable = NotAcceptable class Conflict(HTTPError): """`409 Conflict` error.""" message = "conflict" - def __init__(self): + def __init__(self, message=None): status = "409 Conflict" headers = {'Content-Type': 'text/html'} - HTTPError.__init__(self, status, headers, self.message) + HTTPError.__init__(self, status, headers, message or self.message) conflict = Conflict class Gone(HTTPError): """`410 Gone` error.""" message = "gone" - def __init__(self): + def __init__(self, message=None): status = '410 Gone' headers = {'Content-Type': 'text/html'} - HTTPError.__init__(self, status, headers, self.message) + HTTPError.__init__(self, status, headers, message or self.message) gone = Gone class PreconditionFailed(HTTPError): """`412 Precondition Failed` error.""" message = "precondition failed" - def __init__(self): + def __init__(self, message=None): status = "412 Precondition Failed" headers = {'Content-Type': 'text/html'} - HTTPError.__init__(self, status, headers, self.message) + HTTPError.__init__(self, status, headers, message or self.message) preconditionfailed = PreconditionFailed class UnsupportedMediaType(HTTPError): """`415 Unsupported Media Type` error.""" message = "unsupported media type" - def __init__(self): + def __init__(self, message=None): status = "415 Unsupported Media Type" headers = {'Content-Type': 'text/html'} - HTTPError.__init__(self, status, headers, self.message) + HTTPError.__init__(self, status, headers, message or self.message) unsupportedmediatype = UnsupportedMediaType +class _UnavailableForLegalReasons(HTTPError): + """`451 Unavailable For Legal Reasons` error.""" + message="unavailable for legal reasons" + def __init__(self, message=None): + status = "451 Unavailable For Legal Reasons" + headers = {'Content-Type': 'text/html'} + HTTPError.__init__(self, status, headers, message or self.message) + +def UnavailableForLegalReasons(message=None): + """Returns HTTPError with '415 Unavailable For Legal Reasons' error from the active application. + """ + if message: + return _UnavailableForLegalReasons(message) + elif ctx.get('app_stack'): + return ctx.app_stack[-1].unavailableforlegalreasons() + else: + return _UnavailableForLegalReasons() + +unavailableforlegalreasons = UnavailableForLegalReasons + class _InternalError(HTTPError): """500 Internal Server Error`.""" message = "internal server error" - + def __init__(self, message=None): status = '500 Internal Server Error' headers = {'Content-Type': 'text/html'} @@ -260,41 +281,41 @@ def header(hdr, value, unique=False): """ Adds the header `hdr: value` with the response. - + If `unique` is True and a header with that name already exists, - it doesn't add a new one. + it doesn't add a new one. """ hdr, value = safestr(hdr), safestr(value) # protection against HTTP response splitting attack if '\n' in hdr or '\r' in hdr or '\n' in value or '\r' in value: raise ValueError, 'invalid characters in header' - + if unique is True: for h, v in ctx.headers: if h.lower() == hdr.lower(): return - + ctx.headers.append((hdr, value)) - + def rawinput(method=None): """Returns storage object with GET or POST arguments. """ method = method or "both" from cStringIO import StringIO - def dictify(fs): + def dictify(fs): # hack to make web.input work with enctype='text/plain. if fs.list is None: - fs.list = [] + fs.list = [] return dict([(k, fs[k]) for k in fs.keys()]) - + e = ctx.env.copy() a = b = {} - + if method.lower() in ['both', 'post', 'put']: if e['REQUEST_METHOD'] in ['POST', 'PUT']: if e.get('CONTENT_TYPE', '').lower().startswith('multipart/'): - # since wsgi.input is directly passed to cgi.FieldStorage, + # since wsgi.input is directly passed to cgi.FieldStorage, # it can not be called multiple times. Saving the FieldStorage # object in ctx to allow calling web.input multiple times. a = ctx.get('_fieldstorage') @@ -323,7 +344,7 @@ def input(*requireds, **defaults): """ - Returns a `storage` object with the GET and POST arguments. + Returns a `storage` object with the GET and POST arguments. See `storify` for how `requireds` and `defaults` work. """ _method = defaults.pop('_method', 'both') @@ -359,10 +380,10 @@ if httponly: value += '; httponly' header('Set-Cookie', value) - + def decode_cookie(value): - r"""Safely decodes a cookie value to unicode. - + r"""Safely decodes a cookie value to unicode. + Tries us-ascii, utf-8 and io8859 encodings, in that order. >>> decode_cookie('') @@ -386,7 +407,7 @@ def parse_cookies(http_cookie): r"""Parse a HTTP_COOKIE header and return dict of cookie names and decoded values. - + >>> sorted(parse_cookies('').items()) [] >>> sorted(parse_cookies('a=1').items()) @@ -422,7 +443,7 @@ cookie.load(attr_value) except Cookie.CookieError: pass - cookies = dict((k, urllib.unquote(v.value)) for k, v in cookie.iteritems()) + cookies = dict([(k, urllib.unquote(v.value)) for k, v in cookie.iteritems()]) else: # HTTP_COOKIE doesn't have quotes, use fast cookie parsing cookies = {} @@ -435,18 +456,18 @@ def cookies(*requireds, **defaults): r"""Returns a `storage` object with all the request cookies in it. - + See `storify` for how `requireds` and `defaults` work. This is forgiving on bad HTTP_COOKIE input, it tries to parse at least the cookies it can. - + The values are converted to unicode if _unicode=True is passed. """ - # If _unicode=True is specified, use decode_cookie to convert cookie value to unicode + # If _unicode=True is specified, use decode_cookie to convert cookie value to unicode if defaults.get("_unicode") is True: defaults['_unicode'] = decode_cookie - + # parse cookie string and cache the result for next time. if '_parsed_cookies' not in ctx: http_cookie = ctx.env.get("HTTP_COOKIE", "") @@ -462,18 +483,18 @@ """ Prints a prettyprinted version of `args` to stderr. """ - try: + try: out = ctx.environ['wsgi.errors'] - except: + except: out = sys.stderr for arg in args: print >> out, pprint.pformat(arg) return '' def _debugwrite(x): - try: + try: out = ctx.environ['wsgi.errors'] - except: + except: out = sys.stderr out.write(x) debug.write = _debugwrite @@ -482,7 +503,7 @@ ctx.__doc__ = """ A `storage` object containing various information about the request: - + `environ` (aka `env`) : A dictionary containing the standard WSGI environment variables. @@ -500,7 +521,7 @@ `path` : The path request. - + `query` : If there are no query arguments, the empty string. Otherwise, a `?` followed by the query string. @@ -522,4 +543,4 @@ if __name__ == "__main__": import doctest - doctest.testmod() \ No newline at end of file + doctest.testmod() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/web.py-0.37/web/wsgi.py new/web.py-0.38/web/wsgi.py --- old/web.py-0.37/web/wsgi.py 2012-06-26 07:19:55.000000000 +0200 +++ new/web.py-0.38/web/wsgi.py 2016-07-08 09:03:09.000000000 +0200 @@ -7,7 +7,7 @@ import http import webapi as web -from utils import listget +from utils import listget, intget from net import validaddr, validip import httpserver @@ -51,7 +51,12 @@ else: return runscgi(func) - return httpserver.runsimple(func, validip(listget(sys.argv, 1, ''))) + + server_addr = validip(listget(sys.argv, 1, '')) + if os.environ.has_key('PORT'): # e.g. Heroku + server_addr = ('0.0.0.0', intget(os.environ['PORT'])) + + return httpserver.runsimple(func, server_addr) def _is_dev_mode(): # Some embedded python interpreters won't have sys.arv
participants (1)
-
root@hilbertn.suse.de