Hello community, here is the log from the commit of package python-bottle for openSUSE:Factory checked in at 2017-04-19 18:05:34 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-bottle (Old) and /work/SRC/openSUSE:Factory/.python-bottle.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-bottle" Wed Apr 19 18:05:34 2017 rev:12 rq:483405 version:0.12.13 Changes: -------- --- /work/SRC/openSUSE:Factory/python-bottle/python-bottle.changes 2014-05-21 16:22:06.000000000 +0200 +++ /work/SRC/openSUSE:Factory/.python-bottle.new/python-bottle.changes 2017-04-19 18:05:38.405854087 +0200 @@ -1,0 +2,27 @@ +Wed Mar 29 15:24:50 UTC 2017 - [email protected] + +- update pdf doc file. + +------------------------------------------------------------------- +Thu Mar 23 18:09:54 UTC 2017 - [email protected] + +- update for singlespec +- use automation for docs +- update to 0.12.13 + * bugfix release + * fixed TypeError on unicode WSGI headers + * fixed get_header on FileUpload + * fixed crlf header injection (CVE-2016-9964) + * switch to setuptools for build + * allow multiline dict/list/set comprehensions in templates + * allow unicode keys in ConfigDict + +------------------------------------------------------------------- +Sun Apr 26 18:21:13 UTC 2015 - [email protected] + +- update to version 0.12.8: no upstream changelog +- update bottle-docs.pdf +- point the source URL of the pdf to the project homepage +- create of separate subpackage for the pdf documentation + +------------------------------------------------------------------- Old: ---- bottle-0.12.7.tar.gz New: ---- bottle-0.12.13.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-bottle.spec ++++++ --- /var/tmp/diff_new_pack.zqTMq3/_old 2017-04-19 18:05:39.165746597 +0200 +++ /var/tmp/diff_new_pack.zqTMq3/_new 2017-04-19 18:05:39.169746031 +0200 @@ -1,7 +1,7 @@ # # spec file for package python-bottle # -# Copyright (c) 2014 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 @@ -16,22 +16,22 @@ # +%{?!python_module:%define python_module() python-%{**} python3-%{**}} Name: python-bottle -Version: 0.12.7 +Version: 0.12.13 Release: 0 Url: http://bottlepy.org/ Summary: Fast and simple WSGI-framework for small web-applications License: MIT Group: Development/Languages/Python -Source: http://pypi.python.org/packages/source/b/bottle/bottle-%{version}.tar.gz -Source1: bottle-docs.pdf -BuildRequires: python-base +Source: https://files.pythonhosted.org/packages/source/b/bottle/bottle-%{version}.tar.gz +Source1: http://bottlepy.org/docs/0.12/bottle-docs.pdf +BuildRequires: %{python_module setuptools} +BuildRequires: python-rpm-macros BuildRoot: %{_tmppath}/%{name}-%{version}-build -%if 0%{?suse_version} && 0%{?suse_version} <= 1110 -%{!?python_sitelib: %global python_sitelib %(python -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")} -%else BuildArch: noarch -%endif + +%python_subpackages %description Bottle is a fast and simple micro-framework for small web-applications. It @@ -40,21 +40,43 @@ template engines. All in a single file and with no dependencies other than the Python Standard Library. +%package -n %{name}-doc +Summary: Documentation for %{name} +Group: Documentation/Other +Requires: %{name} = %{version} +Provides: %{python_module bottle-doc = %{version}} + +%description -n %{name}-doc +Bottle is a fast and simple micro-framework for small web-applications. It +offers request dispatching (Routes) with url parameter support, Templates, a +built-in HTTP Server and adapters for many third party WSGI/HTTP-server and +template engines. All in a single file and with no dependencies other than the +Python Standard Library. + +This subpackage contains the PDF documentation for %{name}. + %prep %setup -q -n bottle-%{version} cp %{SOURCE1} . %build -python setup.py build +%python_build %install -python setup.py install --prefix=%{_prefix} --root=%{buildroot} +%python_install +%python_clone %{buildroot}%{_bindir}/bottle.py -%files +%files %{python_files} %defattr(-,root,root,-) -%doc README.rst bottle-docs.pdf -%{_bindir}/bottle.py +%doc README.rst +%{_bindir}/bottle.py-%{python_bin_suffix} +%python3_only %{_bindir}/bottle.py %{python_sitelib}/bottle.py* -%{python_sitelib}/bottle-%{version}-py%{py_ver}.egg-info +%pycache_only %{python_sitelib}/__pycache__ +%{python_sitelib}/bottle-%{version}-py%{python_version}.egg-info + +%files -n %{name}-doc +%defattr(-,root,root,-) +%doc bottle-docs.pdf %changelog ++++++ bottle-0.12.7.tar.gz -> bottle-0.12.13.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/bottle-0.12.7/MANIFEST.in new/bottle-0.12.13/MANIFEST.in --- old/bottle-0.12.7/MANIFEST.in 1970-01-01 01:00:00.000000000 +0100 +++ new/bottle-0.12.13/MANIFEST.in 2016-07-05 12:08:44.000000000 +0200 @@ -0,0 +1,6 @@ +include bottle.py +include setup.py +include README.rst +include LICENSE.txt +include test/views/*.tpl +include test/*.py diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/bottle-0.12.7/PKG-INFO new/bottle-0.12.13/PKG-INFO --- old/bottle-0.12.7/PKG-INFO 2014-04-29 19:25:40.000000000 +0200 +++ new/bottle-0.12.13/PKG-INFO 2017-01-09 13:33:49.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: bottle -Version: 0.12.7 +Version: 0.12.13 Summary: Fast and simple WSGI-framework for small web-applications. Home-page: http://bottlepy.org/ Author: Marcel Hellkamp @@ -15,7 +15,7 @@ Homepage and documentation: http://bottlepy.org/ - Copyright (c) 2013, Marcel Hellkamp. + Copyright (c) 2016, Marcel Hellkamp. License: MIT (see LICENSE for details) Platform: any diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/bottle-0.12.7/bottle.egg-info/PKG-INFO new/bottle-0.12.13/bottle.egg-info/PKG-INFO --- old/bottle-0.12.7/bottle.egg-info/PKG-INFO 1970-01-01 01:00:00.000000000 +0100 +++ new/bottle-0.12.13/bottle.egg-info/PKG-INFO 2017-01-09 13:33:48.000000000 +0100 @@ -0,0 +1,37 @@ +Metadata-Version: 1.1 +Name: bottle +Version: 0.12.13 +Summary: Fast and simple WSGI-framework for small web-applications. +Home-page: http://bottlepy.org/ +Author: Marcel Hellkamp +Author-email: [email protected] +License: MIT +Description: + Bottle is a fast and simple micro-framework for small web applications. It + offers request dispatching (Routes) with url parameter support, templates, + a built-in HTTP Server and adapters for many third party WSGI/HTTP-server and + template engines - all in a single file and with no dependencies other than the + Python Standard Library. + + Homepage and documentation: http://bottlepy.org/ + + Copyright (c) 2016, Marcel Hellkamp. + License: MIT (see LICENSE for details) + +Platform: any +Classifier: Development Status :: 4 - Beta +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content :: CGI Tools/Libraries +Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers +Classifier: Topic :: Internet :: WWW/HTTP :: WSGI +Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application +Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Middleware +Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Server +Classifier: Topic :: Software Development :: Libraries :: Application Frameworks +Classifier: Programming Language :: Python :: 2.5 +Classifier: Programming Language :: Python :: 2.6 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.2 +Classifier: Programming Language :: Python :: 3.3 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/bottle-0.12.7/bottle.egg-info/SOURCES.txt new/bottle-0.12.13/bottle.egg-info/SOURCES.txt --- old/bottle-0.12.7/bottle.egg-info/SOURCES.txt 1970-01-01 01:00:00.000000000 +0100 +++ new/bottle-0.12.13/bottle.egg-info/SOURCES.txt 2017-01-09 13:33:48.000000000 +0100 @@ -0,0 +1,46 @@ +MANIFEST.in +README.rst +bottle.py +setup.py +bottle.egg-info/PKG-INFO +bottle.egg-info/SOURCES.txt +bottle.egg-info/dependency_links.txt +bottle.egg-info/top_level.txt +test/servertest.py +test/test_auth.py +test/test_config.py +test/test_configdict.py +test/test_contextlocals.py +test/test_environ.py +test/test_fileupload.py +test/test_formsdict.py +test/test_importhook.py +test/test_jinja2.py +test/test_mako.py +test/test_mdict.py +test/test_mount.py +test/test_outputfilter.py +test/test_plugins.py +test/test_resources.py +test/test_route.py +test/test_router.py +test/test_securecookies.py +test/test_sendfile.py +test/test_server.py +test/test_stpl.py +test/test_wsgi.py +test/testall.py +test/tools.py +test/views/jinja2_base.tpl +test/views/jinja2_inherit.tpl +test/views/jinja2_simple.tpl +test/views/mako_base.tpl +test/views/mako_inherit.tpl +test/views/mako_simple.tpl +test/views/stpl_include.tpl +test/views/stpl_no_vars.tpl +test/views/stpl_simple.tpl +test/views/stpl_t2base.tpl +test/views/stpl_t2inc.tpl +test/views/stpl_t2main.tpl +test/views/stpl_unicode.tpl \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/bottle-0.12.7/bottle.egg-info/dependency_links.txt new/bottle-0.12.13/bottle.egg-info/dependency_links.txt --- old/bottle-0.12.7/bottle.egg-info/dependency_links.txt 1970-01-01 01:00:00.000000000 +0100 +++ new/bottle-0.12.13/bottle.egg-info/dependency_links.txt 2017-01-09 13:33:48.000000000 +0100 @@ -0,0 +1 @@ + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/bottle-0.12.7/bottle.egg-info/top_level.txt new/bottle-0.12.13/bottle.egg-info/top_level.txt --- old/bottle-0.12.7/bottle.egg-info/top_level.txt 1970-01-01 01:00:00.000000000 +0100 +++ new/bottle-0.12.13/bottle.egg-info/top_level.txt 2017-01-09 13:33:48.000000000 +0100 @@ -0,0 +1 @@ +bottle diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/bottle-0.12.7/bottle.py new/bottle-0.12.13/bottle.py --- old/bottle-0.12.7/bottle.py 2014-04-29 19:24:14.000000000 +0200 +++ new/bottle-0.12.13/bottle.py 2017-01-09 13:31:53.000000000 +0100 @@ -9,14 +9,14 @@ Homepage and documentation: http://bottlepy.org/ -Copyright (c) 2013, Marcel Hellkamp. +Copyright (c) 2016, Marcel Hellkamp. License: MIT (see LICENSE for details) """ from __future__ import with_statement __author__ = 'Marcel Hellkamp' -__version__ = '0.12.7' +__version__ = '0.12.13' __license__ = 'MIT' # The gevent server adapter needs to patch some modules before they are imported @@ -1117,7 +1117,10 @@ exhaustion. ''' ctype = self.environ.get('CONTENT_TYPE', '').lower().split(';')[0] if ctype == 'application/json': - return json_loads(self._get_body_string()) + b = self._get_body_string() + if not b: + return None + return json_loads(b) return None def _iter_body(self, read, bufsize): @@ -1154,7 +1157,7 @@ maxread -= len(part) if read(2) != rn: raise err - + @DictProperty('environ', 'bottle.request.body', read_only=True) def _body(self): body_iter = self._iter_chunked if self.chunked else self._iter_body @@ -1395,28 +1398,36 @@ self.environ['bottle.request.ext.%s'%name] = value +def _hkey(key): + if '\n' in key or '\r' in key or '\0' in key: + raise ValueError("Header names must not contain control characters: %r" % key) + return key.title().replace('_', '-') + +def _hval(value): + value = tonat(value) + if '\n' in value or '\r' in value or '\0' in value: + raise ValueError("Header value must not contain control characters: %r" % value) + return value -def _hkey(s): - return s.title().replace('_','-') class HeaderProperty(object): - def __init__(self, name, reader=None, writer=str, default=''): + def __init__(self, name, reader=None, writer=None, default=''): self.name, self.default = name, default self.reader, self.writer = reader, writer self.__doc__ = 'Current value of the %r header.' % name.title() def __get__(self, obj, cls): if obj is None: return self - value = obj.headers.get(self.name, self.default) + value = obj.get_header(self.name, self.default) return self.reader(value) if self.reader else value def __set__(self, obj, value): - obj.headers[self.name] = self.writer(value) + obj[self.name] = self.writer(value) if self.writer else value def __delete__(self, obj): - del obj.headers[self.name] + del obj[self.name] class BaseResponse(object): @@ -1469,7 +1480,7 @@ copy._headers = dict((k, v[:]) for (k, v) in self._headers.items()) if self._cookies: copy._cookies = SimpleCookie() - copy._cookies.load(self._cookies.output()) + copy._cookies.load(self._cookies.output(header='')) return copy def __iter__(self): @@ -1523,7 +1534,7 @@ def __contains__(self, name): return _hkey(name) in self._headers def __delitem__(self, name): del self._headers[_hkey(name)] def __getitem__(self, name): return self._headers[_hkey(name)][-1] - def __setitem__(self, name, value): self._headers[_hkey(name)] = [str(value)] + def __setitem__(self, name, value): self._headers[_hkey(name)] = [_hval(value)] def get_header(self, name, default=None): ''' Return the value of a previously defined header. If there is no @@ -1533,11 +1544,11 @@ def set_header(self, name, value): ''' Create a new response header, replacing any previously defined headers with the same name. ''' - self._headers[_hkey(name)] = [str(value)] + self._headers[_hkey(name)] = [_hval(value)] def add_header(self, name, value): ''' Add an additional response header, not removing duplicates. ''' - self._headers.setdefault(_hkey(name), []).append(str(value)) + self._headers.setdefault(_hkey(name), []).append(_hval(value)) def iter_headers(self): ''' Yield (header, value) tuples, skipping headers that are not @@ -1918,7 +1929,6 @@ return super(FormsDict, self).__getattr__(name) return self.getunicode(name, default=default) - class HeaderDict(MultiDict): """ A case-insensitive version of :class:`MultiDict` that defaults to replace the old value instead of appending it. """ @@ -1930,15 +1940,14 @@ def __contains__(self, key): return _hkey(key) in self.dict def __delitem__(self, key): del self.dict[_hkey(key)] def __getitem__(self, key): return self.dict[_hkey(key)][-1] - def __setitem__(self, key, value): self.dict[_hkey(key)] = [str(value)] - def append(self, key, value): - self.dict.setdefault(_hkey(key), []).append(str(value)) - def replace(self, key, value): self.dict[_hkey(key)] = [str(value)] + def __setitem__(self, key, value): self.dict[_hkey(key)] = [_hval(value)] + def append(self, key, value): self.dict.setdefault(_hkey(key), []).append(_hval(value)) + def replace(self, key, value): self.dict[_hkey(key)] = [_hval(value)] def getall(self, key): return self.dict.get(_hkey(key)) or [] def get(self, key, default=None, index=-1): return MultiDict.get(self, _hkey(key), default, index) def filter(self, names): - for name in [_hkey(n) for n in names]: + for name in (_hkey(n) for n in names): if name in self.dict: del self.dict[name] @@ -2094,7 +2103,7 @@ def load_dict(self, source, namespace='', make_namespaces=False): ''' Import values from a dictionary structure. Nesting can be used to represent namespaces. - + >>> ConfigDict().load_dict({'name': {'space': {'key': 'value'}}}) {'name.space.key': 'value'} ''' @@ -2104,7 +2113,7 @@ if not isinstance(source, dict): raise TypeError('Source is not a dict (r)' % type(key)) for key, value in source.items(): - if not isinstance(key, str): + if not isinstance(key, basestring): raise TypeError('Key is not a string (%r)' % type(key)) full_key = prefix + '.' + key if prefix else key if isinstance(value, dict): @@ -2120,7 +2129,7 @@ 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], str): + if a and isinstance(a[0], basestring): prefix = a[0].strip('.') + '.' a = a[1:] for key, value in dict(*a, **ka).items(): @@ -2132,7 +2141,7 @@ return self[key] def __setitem__(self, key, value): - if not isinstance(key, str): + if not isinstance(key, basestring): raise TypeError('Key has type %r (not a string)' % type(key)) value = self.meta_get(key, 'filter', lambda x: x)(value) @@ -2346,11 +2355,15 @@ content_type = HeaderProperty('Content-Type') content_length = HeaderProperty('Content-Length', reader=int, default=-1) + def get_header(self, name, default=None): + """ Return the value of a header within the mulripart part. """ + return self.headers.get(name, default) + @cached_property def filename(self): ''' Name of the file on the client file system, but normalized to ensure file system compatibility. An empty filename is returned as 'empty'. - + Only ASCII letters, digits, dashes, underscores and dots are allowed in the final filename. Accents are removed, if possible. Whitespace is replaced by a single dash. Leading or tailing dots @@ -2774,20 +2787,20 @@ from cherrypy import wsgiserver self.options['bind_addr'] = (self.host, self.port) self.options['wsgi_app'] = handler - + certfile = self.options.get('certfile') if certfile: del self.options['certfile'] keyfile = self.options.get('keyfile') if keyfile: del self.options['keyfile'] - + server = wsgiserver.CherryPyWSGIServer(**self.options) if certfile: server.ssl_certificate = certfile if keyfile: server.ssl_private_key = keyfile - + try: server.start() finally: @@ -3412,15 +3425,19 @@ _re_inl = _re_tok.replace('|\\n','') # We re-use this string pattern later # 2: Comments (until end of line, but not the newline itself) _re_tok += '|(#.*)' - # 3,4: Keywords that start or continue a python block (only start of line) + # 3,4: Open and close grouping tokens + _re_tok += '|([\[\{\(])' + _re_tok += '|([\]\}\)])' + # 5,6: Keywords that start or continue a python block (only start of line) _re_tok += '|^([ \\t]*(?:if|for|while|with|try|def|class)\\b)' \ '|^([ \\t]*(?:elif|else|except|finally)\\b)' - # 5: Our special 'end' keyword (but only if it stands alone) + # 7: Our special 'end' keyword (but only if it stands alone) _re_tok += '|((?:^|;)[ \\t]*end[ \\t]*(?=(?:%(block_close)s[ \\t]*)?\\r?$|;|#))' - # 6: A customizable end-of-code-block template token (only end of line) - _re_tok += '|(%(block_close)s[ \\t]*(?=$))' - # 7: And finally, a single newline. The 8th token is 'everything else' + # 8: A customizable end-of-code-block template token (only end of line) + _re_tok += '|(%(block_close)s[ \\t]*(?=\\r?$))' + # 9: And finally, a single newline. The 10th token is 'everything else' _re_tok += '|(\\r?\\n)' + # Match the start tokens of code areas in a template _re_split = '(?m)^[ \t]*(\\\\?)((%(line_start)s)|(%(block_start)s))(%%?)' # Match inline statements (may contain python strings) @@ -3434,6 +3451,7 @@ self.code_buffer, self.text_buffer = [], [] self.lineno, self.offset = 1, 0 self.indent, self.indent_mod = 0, 0 + self.paren_depth = 0 def get_syntax(self): ''' Tokens as a space separated string (default: <% %> % {{ }}) ''' @@ -3490,8 +3508,8 @@ return code_line += self.source[self.offset:self.offset+m.start()] self.offset += m.end() - _str, _com, _blk1, _blk2, _end, _cend, _nl = m.groups() - if code_line and (_blk1 or _blk2): # a if b else c + _str, _com, _po, _pc, _blk1, _blk2, _end, _cend, _nl = m.groups() + if (code_line or self.paren_depth > 0) and (_blk1 or _blk2): # a if b else c code_line += _blk1 or _blk2 continue if _str: # Python string @@ -3500,6 +3518,15 @@ comment = _com if multiline and _com.strip().endswith(self._tokens[1]): multiline = False # Allow end-of-block in comments + elif _po: # open parenthesis + self.paren_depth += 1 + code_line += _po + elif _pc: # close parenthesis + if self.paren_depth > 0: + # we could check for matching parentheses here, but it's + # easier to leave that to python - just check counts + self.paren_depth -= 1 + code_line += _pc elif _blk1: # Start-block keyword (if/for/while/def/try/...) code_line, self.indent_mod = _blk1, -1 self.indent += 1 @@ -3642,6 +3669,7 @@ #: A dict to map HTTP status codes (e.g. 404) to phrases (e.g. 'Not Found') HTTP_CODES = httplib.responses HTTP_CODES[418] = "I'm a teapot" # RFC 2324 +HTTP_CODES[422] = "Unprocessable Entity" # RFC 4918 HTTP_CODES[428] = "Precondition Required" HTTP_CODES[429] = "Too Many Requests" HTTP_CODES[431] = "Request Header Fields Too Large" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/bottle-0.12.7/setup.cfg new/bottle-0.12.13/setup.cfg --- old/bottle-0.12.7/setup.cfg 1970-01-01 01:00:00.000000000 +0100 +++ new/bottle-0.12.13/setup.cfg 2017-01-09 13:33:49.000000000 +0100 @@ -0,0 +1,5 @@ +[egg_info] +tag_build = +tag_date = 0 +tag_svn_revision = 0 + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/bottle-0.12.7/setup.py new/bottle-0.12.13/setup.py --- old/bottle-0.12.7/setup.py 2014-04-29 19:23:59.000000000 +0200 +++ new/bottle-0.12.13/setup.py 2017-01-09 13:19:52.000000000 +0100 @@ -2,7 +2,7 @@ import sys import os -from distutils.core import setup +from setuptools import setup if sys.version_info < (2,5): raise NotImplementedError("Sorry, you need at least Python 2.5 or Python 3.x to use bottle.") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/bottle-0.12.7/test/test_configdict.py new/bottle-0.12.13/test/test_configdict.py --- old/bottle-0.12.7/test/test_configdict.py 2014-04-29 19:23:59.000000000 +0200 +++ new/bottle-0.12.13/test/test_configdict.py 2017-01-09 13:19:52.000000000 +0100 @@ -75,6 +75,19 @@ self.assertRaises(TypeError, lambda: setitem(c, 5, 6)) self.assertRaises(TypeError, lambda: c.load_dict({5:6})) + def test_issue720(self): + """Accept unicode keys.""" + try: + key = unichr(12354) + except NameError: + key = chr(12354) + c = ConfigDict() + c.load_dict({key: 'value'}) + self.assertEqual('value', c[key]) + c = ConfigDict() + c.load_dict({key: {'subkey': 'value'}}) + self.assertEqual('value', c[key + '.subkey']) + if __name__ == '__main__': #pragma: no cover unittest.main() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/bottle-0.12.7/test/test_environ.py new/bottle-0.12.13/test/test_environ.py --- old/bottle-0.12.7/test/test_environ.py 2014-04-29 17:13:16.000000000 +0200 +++ new/bottle-0.12.13/test/test_environ.py 2017-01-09 13:30:30.000000000 +0100 @@ -3,6 +3,7 @@ import unittest import sys + import bottle from bottle import request, tob, touni, tonat, json_dumps, _e, HTTPError, parse_date import tools @@ -11,6 +12,18 @@ from bottle import BaseRequest, BaseResponse, LocalRequest + +try: + from itertools import product +except ImportError: + def product(*args): + pools = map(tuple, args) + result = [[]] + for pool in pools: + result = [x + [y] for x in result for y in pool] + for prod in result: + yield tuple(prod) + class TestRequest(unittest.TestCase): def test_app_property(self): @@ -394,6 +407,14 @@ e['CONTENT_LENGTH'] = str(len(json_dumps(test))) self.assertEqual(BaseRequest(e).json, None) + def test_json_header_empty_body(self): + """Request Content-Type is application/json but body is empty""" + e = {'CONTENT_TYPE': 'application/json'} + wsgiref.util.setup_testing_defaults(e) + wsgiref.util.setup_testing_defaults(e) + e['CONTENT_LENGTH'] = "0" + self.assertEqual(BaseRequest(e).json, None) + def test_isajax(self): e = {} wsgiref.util.setup_testing_defaults(e) @@ -646,6 +667,30 @@ self.assertEqual('5', response['x-test']) response['x-test'] = None self.assertEqual('None', response['x-test']) + response['x-test'] = touni('瓶') + self.assertEqual(tonat(touni('瓶')), response['x-test']) + + def test_prevent_control_characters_in_headers(self): + masks = '{}test', 'test{}', 'te{}st' + tests = '\n', '\r', '\n\r', '\0' + + # Test HeaderDict + apis = 'append', 'replace', '__setitem__', 'setdefault' + for api, mask, test in product(apis, masks, tests): + hd = bottle.HeaderDict() + func = getattr(hd, api) + value = mask.replace("{}", test) + self.assertRaises(ValueError, func, value, "test-value") + self.assertRaises(ValueError, func, "test-name", value) + + # Test functions on BaseResponse + apis = 'add_header', 'set_header', '__setitem__' + for api, mask, test in product(apis, masks, tests): + rs = bottle.BaseResponse() + func = getattr(rs, api) + value = mask.replace("{}", test) + self.assertRaises(ValueError, func, value, "test-value") + self.assertRaises(ValueError, func, "test-name", value) def test_expires_header(self): import datetime diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/bottle-0.12.7/test/test_fileupload.py new/bottle-0.12.13/test/test_fileupload.py --- old/bottle-0.12.7/test/test_fileupload.py 2014-04-29 17:13:16.000000000 +0200 +++ new/bottle-0.12.13/test/test_fileupload.py 2017-01-09 13:19:52.000000000 +0100 @@ -4,7 +4,7 @@ import unittest import sys, os.path import bottle -from bottle import FileUpload, BytesIO +from bottle import FileUpload, BytesIO, tob import tempfile class TestFileUpload(unittest.TestCase): @@ -14,6 +14,10 @@ def test_raw_filename(self): self.assertEqual(FileUpload(None, None, 'x/x').raw_filename, 'x/x') + def test_content_type(self): + fu = FileUpload(None, None, None, {"Content-type": "text/plain"}) + self.assertEqual(fu.content_type, 'text/plain') + def assertFilename(self, bad, good): fu = FileUpload(None, None, bad) self.assertEqual(fu.filename, good) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/bottle-0.12.7/test/test_stpl.py new/bottle-0.12.13/test/test_stpl.py --- old/bottle-0.12.7/test/test_stpl.py 2014-04-29 19:23:59.000000000 +0200 +++ new/bottle-0.12.13/test/test_stpl.py 2017-01-09 13:19:52.000000000 +0100 @@ -303,6 +303,9 @@ 18 ''' self.assertRenders(source, result) + source_wineol = '<% a = 5\r\nb = 6\r\nc = 7\r\n%>\r\n{{a+b+c}}' + result_wineol = '18' + self.assertRenders(source_wineol, result_wineol) def test_multiline_ignore_eob_in_string(self): source = ''' @@ -375,6 +378,18 @@ ''' self.assertRenders(source, result) + def test_multiline_comprehensions_in_code_line(self): + self.assertRenders(source=''' + % a = [ + % (i + 1) + % for i in range(5) + % if i%2 == 0 + % ] + {{a}} + ''', result=''' + [1, 3, 5] + ''') + if __name__ == '__main__': #pragma: no cover unittest.main() ++++++ bottle-docs.pdf ++++++ (binary differes)
