diff --git a/README.md b/README.md index 5166047..3365851 100644 --- a/README.md +++ b/README.md @@ -115,9 +115,11 @@ responsibility to ensure the passed location is correct. In the clone, directly run: ```python bottle_server.py``` -This will active a bottle server listening on port 8081, and can be +Note that this project uses Python3. This will active a bottle server listening on port 8081, and can be stopped by ctrl-C in the terminal. +To update bottle.py, one must use ```2to3 -w bottle.py```. No local modifications of the bottle.py need to be done. + This is the easiest way to run a test server to have a look at your changes immediately. diff --git a/bottle.py b/bottle.py index f231bc5..0b6dcf5 100644 --- a/bottle.py +++ b/bottle.py @@ -13,7 +13,7 @@ License: MIT (see LICENSE for details) """ -from __future__ import with_statement + import sys __author__ = 'Marcel Hellkamp' @@ -83,7 +83,7 @@ def _cli_patch(args): def getargspec(func): params = signature(func).parameters args, varargs, keywords, defaults = [], None, None, [] - for name, param in params.items(): + for name, param in list(params.items()): if param.kind == param.VAR_POSITIONAL: varargs = name elif param.kind == param.VAR_KEYWORD: @@ -151,8 +151,8 @@ def _e(): import pickle from io import BytesIO from configparser import ConfigParser, Error as ConfigParserError - basestring = str - unicode = str + str = str + str = str json_loads = lambda s: json_lds(touni(s)) callable = lambda x: hasattr(x, '__call__') imap = map @@ -160,26 +160,26 @@ def _e(): def _raise(*a): raise a[0](a[1]).with_traceback(a[2]) else: # 2.x - import httplib - import thread - from urlparse import urljoin, SplitResult as UrlSplitResult - from urllib import urlencode, quote as urlquote, unquote as urlunquote - from Cookie import SimpleCookie - from itertools import imap - import cPickle as pickle - from StringIO import StringIO as BytesIO - from ConfigParser import SafeConfigParser as ConfigParser, \ + import http.client + import _thread + from urllib.parse import urljoin, SplitResult as UrlSplitResult + from urllib.parse import urlencode, quote as urlquote, unquote as urlunquote + from http.cookies import SimpleCookie + + import pickle as pickle + from io import StringIO as BytesIO + from configparser import SafeConfigParser as ConfigParser, \ Error as ConfigParserError if py25: from UserDict import DictMixin def next(it): - return it.next() + return next(it) bytes = str else: # 2.6, 2.7 from collections import MutableMapping as DictMixin - unicode = unicode + str = str json_loads = json_lds eval(compile('def _raise(*a): raise a[0], a[1], a[2]', '', 'exec')) @@ -189,14 +189,14 @@ def next(it): # Some helpers for string/byte handling def tob(s, enc='utf8'): - return s.encode(enc) if isinstance(s, unicode) else bytes(s) + return s.encode(enc) if isinstance(s, str) else bytes(s) def touni(s, enc='utf8', err='strict'): if isinstance(s, bytes): return s.decode(enc, err) else: - return unicode(s or ("" if s is None else s)) + return str(s or ("" if s is None else s)) tonat = touni if py3k else tob @@ -627,8 +627,7 @@ def get_undecorated_callback(self): # in case of decorators with multiple arguments if not isinstance(func, FunctionType): # pick first FunctionType instance from multiple arguments - func = filter(lambda x: isinstance(x, FunctionType), - map(lambda x: x.cell_contents, attributes)) + func = [x for x in [x.cell_contents for x in attributes] if isinstance(x, FunctionType)] func = list(func)[0] # py3 support return func @@ -936,7 +935,7 @@ def hello(name): skiplist = makelist(skip) def decorator(callback): - if isinstance(callback, basestring): callback = load(callback) + if isinstance(callback, str): callback = load(callback) for rule in makelist(path) or yieldroutes(callback): for verb in makelist(method): verb = verb.upper() @@ -1037,10 +1036,10 @@ def _cast(self, out, peek=None): return [] # Join lists of byte or unicode strings. Mixed lists are NOT supported if isinstance(out, (tuple, list))\ - and isinstance(out[0], (bytes, unicode)): + and isinstance(out[0], (bytes, str)): out = out[0][0:0].join(out) # b'abc'[0:0] -> b'' # Encode unicode strings - if isinstance(out, unicode): + if isinstance(out, str): out = out.encode(response.charset) # Byte Strings are just returned if isinstance(out, bytes): @@ -1086,9 +1085,9 @@ def _cast(self, out, peek=None): return self._cast(first) elif isinstance(first, bytes): new_iter = itertools.chain([first], iout) - elif isinstance(first, unicode): + elif isinstance(first, str): encoder = lambda x: x.encode(response.charset) - new_iter = imap(encoder, itertools.chain([first], iout)) + new_iter = map(encoder, itertools.chain([first], iout)) else: msg = 'Unsupported response type: %s' % type(first) return self._cast(HTTPError(500, msg)) @@ -1206,7 +1205,7 @@ def get_header(self, name, default=None): def cookies(self): """ Cookies parsed into a :class:`FormsDict`. Signed cookies are NOT decoded. Use :meth:`get_cookie` if you expect signed cookies. """ - cookies = SimpleCookie(self.environ.get('HTTP_COOKIE', '')).values() + cookies = list(SimpleCookie(self.environ.get('HTTP_COOKIE', '')).values()) return FormsDict((c.key, c.value) for c in cookies) def get_cookie(self, key, default=None, secret=None): @@ -1545,7 +1544,7 @@ def __len__(self): return len(self.environ) def keys(self): - return self.environ.keys() + return list(self.environ.keys()) def __setitem__(self, key, value): """ Change an environ value and clear all caches that depend on it. """ @@ -1647,11 +1646,11 @@ def __init__(self, body='', status=None, headers=None, **more_headers): self.status = status or self.default_status if headers: if isinstance(headers, dict): - headers = headers.items() + headers = list(headers.items()) for name, value in headers: self.add_header(name, value) if more_headers: - for name, value in more_headers.items(): + for name, value in list(more_headers.items()): self.add_header(name, value) def copy(self, cls=None): @@ -1660,7 +1659,7 @@ def copy(self, cls=None): assert issubclass(cls, BaseResponse) copy = cls() copy.status = self.status - copy._headers = dict((k, v[:]) for (k, v) in self._headers.items()) + copy._headers = dict((k, v[:]) for (k, v) in list(self._headers.items())) if self._cookies: copy._cookies = SimpleCookie() copy._cookies.load(self._cookies.output(header='')) @@ -1726,7 +1725,7 @@ def __getitem__(self, name): return self._headers[_hkey(name)][-1] def __setitem__(self, name, value): - self._headers[_hkey(name)] = [value if isinstance(value, unicode) else + self._headers[_hkey(name)] = [value if isinstance(value, str) else str(value)] def get_header(self, name, default=None): @@ -1737,13 +1736,13 @@ def get_header(self, name, default=None): def set_header(self, name, value): """ Create a new response header, replacing any previously defined headers with the same name. """ - self._headers[_hkey(name)] = [value if isinstance(value, unicode) + self._headers[_hkey(name)] = [value if isinstance(value, str) else str(value)] def add_header(self, name, value): """ Add an additional response header, not removing duplicates. """ self._headers.setdefault(_hkey(name), []).append( - value if isinstance(value, unicode) else str(value)) + value if isinstance(value, str) else str(value)) def iter_headers(self): """ Yield (header, value) tuples, skipping headers that are not @@ -1762,12 +1761,12 @@ def headerlist(self): headers = [h for h in headers if h[0] not in bad_headers] out += [(name, val) for (name, vals) in headers for val in vals] if self._cookies: - for c in self._cookies.values(): + for c in list(self._cookies.values()): out.append(('Set-Cookie', c.OutputString())) if py3k: return [(k, v.encode('utf8').decode('latin1')) for (k, v) in out] else: - return [(k, v.encode('utf8') if isinstance(v, unicode) else v) + return [(k, v.encode('utf8') if isinstance(v, str) else v) for (k, v) in out] content_type = HeaderProperty('Content-Type') @@ -1822,7 +1821,7 @@ def set_cookie(self, name, value, secret=None, **options): if secret: value = touni(cookie_encode((name, value), secret)) - elif not isinstance(value, basestring): + elif not isinstance(value, str): raise TypeError('Secret key missing for non-string Cookie.') # Cookie size plus options must not exceed 4kb. @@ -1831,7 +1830,7 @@ def set_cookie(self, name, value, secret=None, **options): self._cookies[name] = value - for key, value in options.items(): + for key, value in list(options.items()): if key == 'max_age': if isinstance(value, timedelta): value = value.seconds + value.days * 24 * 3600 @@ -2035,7 +2034,7 @@ class MultiDict(DictMixin): """ def __init__(self, *a, **k): - self.dict = dict((k, [v]) for (k, v) in dict(*a, **k).items()) + self.dict = dict((k, [v]) for (k, v) in list(dict(*a, **k).items())) def __len__(self): return len(self.dict) @@ -2056,18 +2055,18 @@ def __setitem__(self, key, value): self.append(key, value) def keys(self): - return self.dict.keys() + return list(self.dict.keys()) if py3k: def values(self): - return (v[-1] for v in self.dict.values()) + return (v[-1] for v in list(self.dict.values())) def items(self): - return ((k, v[-1]) for k, v in self.dict.items()) + return ((k, v[-1]) for k, v in list(self.dict.items())) def allitems(self): - return ((k, v) for k, vl in self.dict.items() for v in vl) + return ((k, v) for k, vl in list(self.dict.items()) for v in vl) iterkeys = keys itervalues = values @@ -2077,25 +2076,25 @@ def allitems(self): else: def values(self): - return [v[-1] for v in self.dict.values()] + return [v[-1] for v in list(self.dict.values())] def items(self): - return [(k, v[-1]) for k, v in self.dict.items()] + return [(k, v[-1]) for k, v in list(self.dict.items())] def iterkeys(self): - return self.dict.iterkeys() + return iter(self.dict.keys()) def itervalues(self): - return (v[-1] for v in self.dict.itervalues()) + return (v[-1] for v in self.dict.values()) def iteritems(self): - return ((k, v[-1]) for k, v in self.dict.iteritems()) + return ((k, v[-1]) for k, v in self.dict.items()) def iterallitems(self): - return ((k, v) for k, vl in self.dict.iteritems() for v in vl) + return ((k, v) for k, vl in self.dict.items() for v in vl) def allitems(self): - return [(k, v) for k, vl in self.dict.iteritems() for v in vl] + return [(k, v) for k, vl in self.dict.items() for v in vl] def get(self, key, default=None, index=-1, type=None): """ Return the most recent value for a key. @@ -2146,7 +2145,7 @@ class FormsDict(MultiDict): recode_unicode = True def _fix(self, s, encoding=None): - if isinstance(s, unicode) and self.recode_unicode: # Python 3 WSGI + if isinstance(s, str) and self.recode_unicode: # Python 3 WSGI return s.encode('latin1').decode(encoding or self.input_encoding) elif isinstance(s, bytes): # Python 2 WSGI return s.decode(encoding or self.input_encoding) @@ -2171,7 +2170,7 @@ def getunicode(self, name, default=None, encoding=None): except (UnicodeError, KeyError): return default - def __getattr__(self, name, default=unicode()): + def __getattr__(self, name, default=str()): # Without this guard, pickle generates a cryptic TypeError: if name.startswith('__') and name.endswith('__'): return super(FormsDict, self).__getattr__(name) @@ -2196,15 +2195,15 @@ def __getitem__(self, key): return self.dict[_hkey(key)][-1] def __setitem__(self, key, value): - self.dict[_hkey(key)] = [value if isinstance(value, unicode) else + self.dict[_hkey(key)] = [value if isinstance(value, str) else str(value)] def append(self, key, value): self.dict.setdefault(_hkey(key), []).append( - value if isinstance(value, unicode) else str(value)) + value if isinstance(value, str) else str(value)) def replace(self, key, value): - self.dict[_hkey(key)] = [value if isinstance(value, unicode) else + self.dict[_hkey(key)] = [value if isinstance(value, str) else str(value)] def getall(self, key): @@ -2250,7 +2249,7 @@ def raw(self, key, default=None): def __getitem__(self, key): val = self.environ[self._ekey(key)] if py3k: - if isinstance(val, unicode): + if isinstance(val, str): val = val.encode('latin1').decode('utf8') else: val = val.decode('utf8') @@ -2273,7 +2272,7 @@ def keys(self): return [x for x in self] def __len__(self): - return len(self.keys()) + return len(list(self.keys())) def __contains__(self, key): return self._ekey(key) in self.environ @@ -2331,8 +2330,8 @@ def load_dict(self, source, namespace=''): >>> c.load_dict({'some': {'namespace': {'key': 'value'} } }) {'some.namespace.key': 'value'} """ - for key, value in source.items(): - if isinstance(key, basestring): + for key, value in list(source.items()): + if isinstance(key, str): nskey = (namespace + '.' + key).strip('.') if isinstance(value, dict): self.load_dict(value, namespace=nskey) @@ -2347,10 +2346,10 @@ def update(self, *a, **ka): namespace. Apart from that it works just as the usual dict.update(). Example: ``update('some.namespace', key='value')`` """ prefix = '' - if a and isinstance(a[0], basestring): + if a and isinstance(a[0], str): prefix = a[0].strip('.') + '.' a = a[1:] - for key, value in dict(*a, **ka).items(): + for key, value in list(dict(*a, **ka).items()): self[prefix + key] = value def setdefault(self, key, value): @@ -2359,7 +2358,7 @@ def setdefault(self, key, value): return self[key] def __setitem__(self, key, value): - if not isinstance(key, basestring): + if not isinstance(key, str): raise TypeError('Key has type %r (not a string)' % type(key)) value = self.meta_get(key, 'filter', lambda x: x)(value) @@ -2413,7 +2412,7 @@ def meta_set(self, key, metafield, value): def meta_list(self, key): """ Return an iterable of meta field names defined for a key. """ - return self._meta.get(key, {}).keys() + return list(self._meta.get(key, {}).keys()) class AppStack(list): @@ -2580,7 +2579,7 @@ def filename(self): or dashes are removed. The filename is limited to 255 characters. """ fname = self.raw_filename - if not isinstance(fname, unicode): + if not isinstance(fname, str): fname = fname.decode('utf8', 'ignore') fname = normalize('NFKD', fname) fname = fname.encode('ASCII', 'ignore').decode('ASCII') @@ -2606,7 +2605,7 @@ def save(self, destination, overwrite=False, chunk_size=2 ** 16): :param overwrite: If True, replace existing files. (default: False) :param chunk_size: Bytes to read at a time. (default: 64kb) """ - if isinstance(destination, basestring): # Except file-likes here + if isinstance(destination, str): # Except file-likes here if os.path.isdir(destination): destination = os.path.join(destination, self.filename) if not overwrite and os.path.exists(destination): @@ -2744,7 +2743,7 @@ def http_date(value): value = value.utctimetuple() elif isinstance(value, (int, float)): value = time.gmtime(value) - if not isinstance(value, basestring): + if not isinstance(value, str): value = time.strftime("%a, %d %b %Y %H:%M:%S GMT", value) return value @@ -2995,7 +2994,7 @@ def run(self, handler): # pragma: no cover def __repr__(self): args = ', '.join(['%s=%s' % (k, repr(v)) - for k, v in self.options.items()]) + for k, v in list(self.options.items())]) return "%s(%s)" % (self.__class__.__name__, args) @@ -3435,13 +3434,13 @@ def run(app=None, try: if debug is not None: _debug(debug) app = app or default_app() - if isinstance(app, basestring): + if isinstance(app, str): app = load_app(app) if not callable(app): raise ValueError("Application is not callable: %r" % app) for plugin in plugins or []: - if isinstance(plugin, basestring): + if isinstance(plugin, str): plugin = load(plugin) app.install(plugin) @@ -3450,7 +3449,7 @@ def run(app=None, if server in server_names: server = server_names.get(server) - if isinstance(server, basestring): + if isinstance(server, str): server = load(server) if isinstance(server, type): server = server(host=host, port=port, **kargs) @@ -3511,11 +3510,11 @@ def run(self): if not exists(self.lockfile)\ or mtime(self.lockfile) < time.time() - self.interval - 5: self.status = 'error' - thread.interrupt_main() + _thread.interrupt_main() for path, lmtime in list(files.items()): if not exists(path) or mtime(path) > lmtime: self.status = 'reload' - thread.interrupt_main() + _thread.interrupt_main() break time.sleep(self.interval) @@ -3840,8 +3839,8 @@ def set_syntax(self, syntax): self._tokens = syntax.split() if not syntax in self._re_cache: names = 'block_start block_close line_start inline_start inline_end' - etokens = map(re.escape, self._tokens) - pattern_vars = dict(zip(names.split(), etokens)) + etokens = list(map(re.escape, self._tokens)) + pattern_vars = dict(list(zip(names.split(), etokens))) patterns = (self._re_split, self._re_tok, self._re_inl) patterns = [re.compile(p % pattern_vars) for p in patterns] self._re_cache[syntax] = patterns @@ -4030,14 +4029,14 @@ def wrapper(*args, **kwargs): NORUN = False # If set, run() does nothing. Used by load_app() #: A dict to map HTTP status codes (e.g. 404) to phrases (e.g. 'Not Found') -HTTP_CODES = httplib.responses.copy() +HTTP_CODES = http.client.responses.copy() HTTP_CODES[418] = "I'm a teapot" # RFC 2324 HTTP_CODES[428] = "Precondition Required" HTTP_CODES[429] = "Too Many Requests" HTTP_CODES[431] = "Request Header Fields Too Large" HTTP_CODES[511] = "Network Authentication Required" _HTTP_STATUS_LINES = dict((k, '%d %s' % (k, v)) - for (k, v) in HTTP_CODES.items()) + for (k, v) in list(HTTP_CODES.items())) #: The default template used for error pages. Override with @error() ERROR_PAGE_TEMPLATE = """ diff --git a/bottle_server.py b/bottle_server.py index f198fdd..6cf0e2a 100644 --- a/bottle_server.py +++ b/bottle_server.py @@ -36,14 +36,17 @@ app = Bottle() default_app.push(app) -import StringIO # NB: don't use cStringIO since it doesn't support unicode!!! +import io # NB: don't use cStringIO since it doesn't support unicode!!! import json # import pg_logger -import urllib -import urllib2 +import urllib.request,urllib.parse,urllib.error +import urllib.request,urllib.error,urllib.parse appPath = dirname(abspath(__file__)) - +# The Checker Framework path parameter default. +# The dev-checker-framework subdirectory should be a symbolic link to the Checker Framework to use. +# This parameter can be set by different deployments of the server, e.g. in +# https://github.com/eisop/webserver/blob/44c3c7b1c9740e7c921fe3365a8eabb5bc2766cd/wsgi-scripts/checkerweb.wsgi#L14 cfPath = join(appPath, "dev-checker-framework") isRise4Fun = False diff --git a/shell-scripts/run-checker.sh b/shell-scripts/run-checker.sh index 489bd75..f6f911b 100755 --- a/shell-scripts/run-checker.sh +++ b/shell-scripts/run-checker.sh @@ -22,6 +22,6 @@ fi CF=$2 IS_RISE4FUN=$3 -cat <