Hello community, here is the log from the commit of package python-web.py for openSUSE:Factory checked in at 2020-11-10 13:46:27 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-web.py (Old) and /work/SRC/openSUSE:Factory/.python-web.py.new.11331 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-web.py" Tue Nov 10 13:46:27 2020 rev:13 rq:847353 version:0.62 Changes: -------- --- /work/SRC/openSUSE:Factory/python-web.py/python-web.py.changes 2020-08-13 10:12:50.102591522 +0200 +++ /work/SRC/openSUSE:Factory/.python-web.py.new.11331/python-web.py.changes 2020-11-10 13:53:27.962848553 +0100 @@ -1,0 +2,9 @@ +Mon Nov 9 21:01:36 UTC 2020 - Michael Ströder <[email protected]> + +- version update to 0.62: + * Fixed: application.load() assumes ctx.path will be a latin1 string #687 + * Fixed: can not reset session data to same value as initialized. #683 + * Fixed: can not set session expire time. #655 + * Fixed: not export session store `MemoryStore`. + +------------------------------------------------------------------- Old: ---- web.py-0.61.tar.gz New: ---- web.py-0.62.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-web.py.spec ++++++ --- /var/tmp/diff_new_pack.ElIAe1/_old 2020-11-10 13:53:28.878846822 +0100 +++ /var/tmp/diff_new_pack.ElIAe1/_new 2020-11-10 13:53:28.882846814 +0100 @@ -15,11 +15,12 @@ # Please submit bugfixes or comments via https://bugs.opensuse.org/ # + %define skip_python2 1 %{?!python_module:%define python_module() python-%{**} python3-%{**}} Name: python-web.py -Version: 0.61 +Version: 0.62 Release: 0 Summary: web.py: makes web apps License: SUSE-Public-Domain AND BSD-3-Clause ++++++ web.py-0.61.tar.gz -> web.py-0.62.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/web.py-0.61/PKG-INFO new/web.py-0.62/PKG-INFO --- old/web.py-0.61/PKG-INFO 2020-07-27 17:34:14.777639600 +0200 +++ new/web.py-0.62/PKG-INFO 2020-11-09 12:20:52.308910800 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: web.py -Version: 0.61 +Version: 0.62 Summary: web.py: makes web apps Home-page: http://webpy.org/ Author: Aaron Swartz @@ -15,11 +15,11 @@ [](https://travis-ci.org/webpy/webpy) [](https://codecov.io/gh/webpy/webpy) - The latest stable release `0.61` only supports Python >= 3.5. + The latest stable release `0.62` only supports Python >= 3.5. To install it, please run: ``` # For Python 3 - python3 -m pip install web.py==0.61 + python3 -m pip install web.py==0.62 ``` If you are still using Python 2.7, then please use web.py version 0.51 @@ -32,12 +32,13 @@ You can also download it from [GitHub Releases](https://github.com/webpy/webpy/releases) page, then install it manually: ``` - unzip webpy-0.61.zip - cd webpy-0.61/ + unzip webpy-0.62.zip + cd webpy-0.62/ python3 setup.py install ``` Note: `0.5x` (e.g. 0.50, 0.51) are our last releases which support Python 2. + Note: `0.6x` (e.g. 0.60, 0.61, 0.62) are our last releases which support Python 3.5. Platform: any Classifier: License :: Public Domain @@ -47,5 +48,6 @@ Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 Requires-Python: >=3.5 Description-Content-Type: text/markdown diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/web.py-0.61/README.md new/web.py-0.62/README.md --- old/web.py-0.61/README.md 2020-07-27 16:43:53.000000000 +0200 +++ new/web.py-0.62/README.md 2020-11-09 10:11:31.000000000 +0100 @@ -5,11 +5,11 @@ [](https://travis-ci.org/webpy/webpy) [](https://codecov.io/gh/webpy/webpy) -The latest stable release `0.61` only supports Python >= 3.5. +The latest stable release `0.62` only supports Python >= 3.5. To install it, please run: ``` # For Python 3 -python3 -m pip install web.py==0.61 +python3 -m pip install web.py==0.62 ``` If you are still using Python 2.7, then please use web.py version 0.51 @@ -22,9 +22,10 @@ You can also download it from [GitHub Releases](https://github.com/webpy/webpy/releases) page, then install it manually: ``` -unzip webpy-0.61.zip -cd webpy-0.61/ +unzip webpy-0.62.zip +cd webpy-0.62/ python3 setup.py install ``` Note: `0.5x` (e.g. 0.50, 0.51) are our last releases which support Python 2. +Note: `0.6x` (e.g. 0.60, 0.61, 0.62) are our last releases which support Python 3.5. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/web.py-0.61/setup.py new/web.py-0.62/setup.py --- old/web.py-0.61/setup.py 2020-07-27 16:43:53.000000000 +0200 +++ new/web.py-0.62/setup.py 2020-11-09 10:11:31.000000000 +0100 @@ -36,5 +36,6 @@ "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", ], ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/web.py-0.61/web/__init__.py new/web.py-0.62/web/__init__.py --- old/web.py-0.61/web/__init__.py 2020-07-27 16:43:53.000000000 +0200 +++ new/web.py-0.62/web/__init__.py 2020-11-09 10:11:31.000000000 +0100 @@ -24,7 +24,7 @@ from .webapi import * # noqa: F401,F403 from .wsgi import * # noqa: F401,F403 -__version__ = "0.61" +__version__ = "0.62" __author__ = [ "Aaron Swartz <[email protected]>", "Anand Chitipothu <[email protected]>", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/web.py-0.61/web/application.py new/web.py-0.62/web/application.py --- old/web.py-0.61/web/application.py 2020-07-27 16:43:53.000000000 +0200 +++ new/web.py-0.62/web/application.py 2020-11-09 09:31:55.000000000 +0100 @@ -360,8 +360,7 @@ return wsgi.runwsgi(self.wsgifunc(*middleware)) def stop(self): - """Stops the http server started by run. - """ + """Stops the http server started by run.""" if httpserver.server: httpserver.server.stop() httpserver.server = None @@ -454,13 +453,17 @@ ctx.realhome = ctx.home ctx.ip = env.get("REMOTE_ADDR") ctx.method = env.get("REQUEST_METHOD") - ctx.path = env.get("PATH_INFO").encode("latin1").decode("utf8") + try: + ctx.path = bytes(env.get("PATH_INFO"), "latin1").decode("utf8") + except UnicodeDecodeError: # If there are Unicode characters... + ctx.path = env.get("PATH_INFO") # http://trac.lighttpd.net/trac/ticket/406 requires: - if env.get("SERVER_SOFTWARE", "").startswith("lighttpd/"): + if env.get("SERVER_SOFTWARE", "").startswith(("lighttpd/", "nginx/")): ctx.path = lstrips(env.get("REQUEST_URI").split("?")[0], ctx.homepath) - # Apache and CherryPy webservers unquote the url but lighttpd doesn't. - # unquote explicitly for lighttpd to make ctx.path uniform across all servers. + # Apache and CherryPy webservers unquote urls but lighttpd and nginx do not. + # Unquote explicitly for lighttpd and nginx to make ctx.path uniform across + # all servers. ctx.path = unquote(ctx.path) if env.get("QUERY_STRING"): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/web.py-0.61/web/browser.py new/web.py-0.62/web/browser.py --- old/web.py-0.61/web/browser.py 2020-07-27 16:43:53.000000000 +0200 +++ new/web.py-0.62/web/browser.py 2020-11-08 19:37:39.000000000 +0100 @@ -239,17 +239,17 @@ class AppBrowser(Browser): """Browser interface to test web.py apps. - b = AppBrowser(app) - b.open('/') - b.follow_link(text='Login') + b = AppBrowser(app) + b.open('/') + b.follow_link(text='Login') - b.select_form(name='login') - b['username'] = 'joe' - b['password'] = 'secret' - b.submit() + b.select_form(name='login') + b['username'] = 'joe' + b['password'] = 'secret' + b.submit() - assert b.path == '/' - assert 'Welcome joe' in b.get_text() + assert b.path == '/' + assert 'Welcome joe' in b.get_text() """ def __init__(self, app): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/web.py-0.61/web/db.py new/web.py-0.62/web/db.py --- old/web.py-0.61/web/db.py 2020-07-27 16:43:53.000000000 +0200 +++ new/web.py-0.62/web/db.py 2020-11-08 19:37:39.000000000 +0100 @@ -154,15 +154,15 @@ def __init__(self, items=None): r"""Creates a new SQLQuery. - >>> SQLQuery("x") - <sql: 'x'> - >>> q = SQLQuery(['SELECT * FROM ', 'test', ' WHERE x=', SQLParam(1)]) - >>> q - <sql: 'SELECT * FROM test WHERE x=1'> - >>> q.query(), q.values() - ('SELECT * FROM test WHERE x=%s', [1]) - >>> SQLQuery(SQLParam(1)) - <sql: '1'> + >>> SQLQuery("x") + <sql: 'x'> + >>> q = SQLQuery(['SELECT * FROM ', 'test', ' WHERE x=', SQLParam(1)]) + >>> q + <sql: 'SELECT * FROM test WHERE x=1'> + >>> q.query(), q.values() + ('SELECT * FROM test WHERE x=%s', [1]) + >>> SQLQuery(SQLParam(1)) + <sql: '1'> """ if items is None: self.items = [] @@ -327,12 +327,12 @@ def _sqllist(values): """ - >>> _sqllist([1, 2, 3]) - <sql: '(1, 2, 3)'> - >>> _sqllist(set([5, 1, 3, 2])) - <sql: '(1, 2, 3, 5)'> - >>> _sqllist((5, 1, 3, 2, 2, 5)) - <sql: '(1, 2, 3, 5)'> + >>> _sqllist([1, 2, 3]) + <sql: '(1, 2, 3)'> + >>> _sqllist(set([5, 1, 3, 2])) + <sql: '(1, 2, 3, 5)'> + >>> _sqllist((5, 1, 3, 2, 2, 5)) + <sql: '(1, 2, 3, 5)'> """ items = [] items.append("(") @@ -486,8 +486,7 @@ class BaseResultSet: - """Base implementation of Result Set, the result of a db query. - """ + """Base implementation of Result Set, the result of a db query.""" def __init__(self, cursor): self.cursor = cursor @@ -542,8 +541,7 @@ class ResultSet(BaseResultSet): - """The result of a database query. - """ + """The result of a database query.""" def __len__(self): return int(self.cursor.rowcount) @@ -656,8 +654,7 @@ """Database""" def __init__(self, db_module, keywords): - """Creates a database. - """ + """Creates a database.""" # some DB implementations take optional parameter `driver` to use a # specific driver module but it should not be passed to `connect`. keywords.pop("driver", None) @@ -783,8 +780,7 @@ return out def _process_query(self, sql_query): - """Takes the SQLQuery object and returns query string and parameters. - """ + """Takes the SQLQuery object and returns query string and parameters.""" paramstyle = getattr(self, "paramstyle", "pyformat") query = sql_query.query(paramstyle) params = sql_query.values() @@ -1283,8 +1279,7 @@ def import_driver(drivers, preferred=None): - """Import the first available driver or preferred driver. - """ + """Import the first available driver or preferred driver.""" if preferred: drivers = (preferred,) @@ -1326,8 +1321,7 @@ class FirebirdDB(DB): - """Firebird Database. - """ + """Firebird Database.""" def __init__(self, **keywords): try: @@ -1372,8 +1366,7 @@ DB.__init__(self, db, keywords) def _process_query(self, sql_query): - """Takes the SQLQuery object and returns query string and parameters. - """ + """Takes the SQLQuery object and returns query string and parameters.""" # MSSQLDB expects params to be a tuple. # Overwriting the default implementation to convert params to tuple. paramstyle = getattr(self, "paramstyle", "pyformat") @@ -1395,14 +1388,14 @@ def _test(self): """Test LIMIT. - Fake presence of pymssql module for running tests. - >>> import sys - >>> sys.modules['pymssql'] = sys.modules['sys'] - - MSSQL has TOP clause instead of LIMIT clause. - >>> db = MSSQLDB(db='test', user='joe', pw='secret') - >>> db.select('foo', limit=4, _test=True) - <sql: 'SELECT * TOP 4 FROM foo'> + Fake presence of pymssql module for running tests. + >>> import sys + >>> sys.modules['pymssql'] = sys.modules['sys'] + + MSSQL has TOP clause instead of LIMIT clause. + >>> db = MSSQLDB(db='test', user='joe', pw='secret') + >>> db.select('foo', limit=4, _test=True) + <sql: 'SELECT * TOP 4 FROM foo'> """ pass @@ -1615,8 +1608,7 @@ self.text = "" def parse(self, text): - """Parses the given text and returns a parse tree. - """ + """Parses the given text and returns a parse tree.""" self.reset() self.text = text return self.parse_all() @@ -1697,8 +1689,7 @@ class SafeEval(object): - """Safe evaluator for binding params to db queries. - """ + """Safe evaluator for binding params to db queries.""" def safeeval(self, text, mapping): nodes = Parser().parse(text) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/web.py-0.61/web/form.py new/web.py-0.62/web/form.py --- old/web.py-0.61/web/form.py 2020-07-27 16:43:53.000000000 +0200 +++ new/web.py-0.62/web/form.py 2020-11-08 19:37:39.000000000 +0100 @@ -270,10 +270,10 @@ class Textbox(Input): """Textbox input. - >>> Textbox(name='foo', value='bar').render() - u'<input id="foo" name="foo" type="text" value="bar"/>' - >>> Textbox(name='foo', value=0).render() - u'<input id="foo" name="foo" type="text" value="0"/>' + >>> Textbox(name='foo', value='bar').render() + u'<input id="foo" name="foo" type="text" value="bar"/>' + >>> Textbox(name='foo', value=0).render() + u'<input id="foo" name="foo" type="text" value="0"/>' """ def get_type(self): @@ -283,8 +283,8 @@ class Password(Input): """Password input. - >>> Password(name='password', value='secret').render() - u'<input id="password" name="password" type="password" value="secret"/>' + >>> Password(name='password', value='secret').render() + u'<input id="password" name="password" type="password" value="secret"/>' """ def get_type(self): @@ -294,8 +294,8 @@ class Textarea(Input): """Textarea input. - >>> Textarea(name='foo', value='bar').render() - u'<textarea id="foo" name="foo">bar</textarea>' + >>> Textarea(name='foo', value='bar').render() + u'<textarea id="foo" name="foo">bar</textarea>' """ def render(self): @@ -308,10 +308,10 @@ class Dropdown(Input): r"""Dropdown/select input. - >>> Dropdown(name='foo', args=['a', 'b', 'c'], value='b').render() - u'<select id="foo" name="foo">\n <option value="a">a</option>\n <option selected="selected" value="b">b</option>\n <option value="c">c</option>\n</select>\n' - >>> Dropdown(name='foo', args=[('a', 'aa'), ('b', 'bb'), ('c', 'cc')], value='b').render() - u'<select id="foo" name="foo">\n <option value="a">aa</option>\n <option selected="selected" value="b">bb</option>\n <option value="c">cc</option>\n</select>\n' + >>> Dropdown(name='foo', args=['a', 'b', 'c'], value='b').render() + u'<select id="foo" name="foo">\n <option value="a">a</option>\n <option selected="selected" value="b">b</option>\n <option value="c">c</option>\n</select>\n' + >>> Dropdown(name='foo', args=[('a', 'aa'), ('b', 'bb'), ('c', 'cc')], value='b').render() + u'<select id="foo" name="foo">\n <option value="a">aa</option>\n <option selected="selected" value="b">bb</option>\n <option value="c">cc</option>\n</select>\n' """ def __init__(self, name, args, *validators, **attrs): @@ -356,10 +356,10 @@ class GroupedDropdown(Dropdown): r"""Grouped Dropdown/select input. - >>> GroupedDropdown(name='car_type', args=(('Swedish Cars', ('Volvo', 'Saab')), ('German Cars', ('Mercedes', 'Audi'))), value='Audi').render() - u'<select id="car_type" name="car_type">\n <optgroup label="Swedish Cars">\n <option value="Volvo">Volvo</option>\n <option value="Saab">Saab</option>\n </optgroup>\n <optgroup label="German Cars">\n <option value="Mercedes">Mercedes</option>\n <option selected="selected" value="Audi">Audi</option>\n </optgroup>\n</select>\n' - >>> GroupedDropdown(name='car_type', args=(('Swedish Cars', (('v', 'Volvo'), ('s', 'Saab'))), ('German Cars', (('m', 'Mercedes'), ('a', 'Audi')))), value='a').render() - u'<select id="car_type" name="car_type">\n <optgroup label="Swedish Cars">\n <option value="v">Volvo</option>\n <option value="s">Saab</option>\n </optgroup>\n <optgroup label="German Cars">\n <option value="m">Mercedes</option>\n <option selected="selected" value="a">Audi</option>\n </optgroup>\n</select>\n' + >>> GroupedDropdown(name='car_type', args=(('Swedish Cars', ('Volvo', 'Saab')), ('German Cars', ('Mercedes', 'Audi'))), value='Audi').render() + u'<select id="car_type" name="car_type">\n <optgroup label="Swedish Cars">\n <option value="Volvo">Volvo</option>\n <option value="Saab">Saab</option>\n </optgroup>\n <optgroup label="German Cars">\n <option value="Mercedes">Mercedes</option>\n <option selected="selected" value="Audi">Audi</option>\n </optgroup>\n</select>\n' + >>> GroupedDropdown(name='car_type', args=(('Swedish Cars', (('v', 'Volvo'), ('s', 'Saab'))), ('German Cars', (('m', 'Mercedes'), ('a', 'Audi')))), value='a').render() + u'<select id="car_type" name="car_type">\n <optgroup label="Swedish Cars">\n <option value="v">Volvo</option>\n <option value="s">Saab</option>\n </optgroup>\n <optgroup label="German Cars">\n <option value="m">Mercedes</option>\n <option selected="selected" value="a">Audi</option>\n </optgroup>\n</select>\n' """ @@ -471,8 +471,8 @@ class Hidden(Input): """Hidden Input. - >>> Hidden(name='foo', value='bar').render() - u'<input id="foo" name="foo" type="hidden" value="bar"/>' + >>> Hidden(name='foo', value='bar').render() + u'<input id="foo" name="foo" type="hidden" value="bar"/>' """ def is_hidden(self): @@ -485,8 +485,8 @@ class File(Input): """File input. - >>> File(name='f', accept=".doc,.docx,.xml").render() - u'<input accept=".doc,.docx,.xml" id="f" name="f" type="file"/>' + >>> File(name='f', accept=".doc,.docx,.xml").render() + u'<input accept=".doc,.docx,.xml" id="f" name="f" type="file"/>' """ def get_type(self): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/web.py-0.61/web/httpserver.py new/web.py-0.62/web/httpserver.py --- old/web.py-0.61/web/httpserver.py 2020-07-27 16:43:53.000000000 +0200 +++ new/web.py-0.62/web/httpserver.py 2020-11-08 19:37:39.000000000 +0100 @@ -28,13 +28,13 @@ def runbasic(func, server_address=("0.0.0.0", 8080)): """ - Runs a simple HTTP server hosting WSGI app `func`. The directory `static/` - is hosted statically. + Runs a simple HTTP server hosting WSGI app `func`. The directory `static/` + is hosted statically. - Based on [WsgiServer][ws] from [Colin Stewart][cs]. + Based on [WsgiServer][ws] from [Colin Stewart][cs]. - [ws]: http://www.owlfish.com/software/wsgiutils/documentation/wsgi-server-api.html - [cs]: http://www.owlfish.com/ + [ws]: http://www.owlfish.com/software/wsgiutils/documentation/wsgi-server-api.html + [cs]: http://www.owlfish.com/ """ # Copyright (c) 2004 Colin Stewart (http://www.owlfish.com/) # Modified somewhat for simplicity diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/web.py-0.61/web/session.py new/web.py-0.62/web/session.py --- old/web.py-0.61/web/session.py 2020-07-27 16:43:53.000000000 +0200 +++ new/web.py-0.62/web/session.py 2020-11-08 19:37:39.000000000 +0100 @@ -25,7 +25,7 @@ from base64 import encodebytes, decodebytes -__all__ = ["Session", "SessionExpired", "Store", "DiskStore", "DBStore"] +__all__ = ["Session", "SessionExpired", "Store", "DiskStore", "DBStore", "MemoryStore"] web.config.session_parameters = utils.storage( { @@ -50,8 +50,7 @@ class Session(object): - """Session management for web.py - """ + """Session management for web.py""" __slots__ = [ "store", @@ -149,16 +148,12 @@ del current_values["session_id"] del current_values["ip"] - if not self.get("_killed") and current_values != self._initializer: + if not self.get("_killed"): self._setcookie(self.session_id) self.store[self.session_id] = dict(self._data) else: if web.cookies().get(self._config.cookie_name): - self._setcookie( - self.session_id, - expires=self._config.timeout, - samesite=self._config.get("samesite"), - ) + self._setcookie(self.session_id, expires=-1) def _setcookie(self, session_id, expires="", **kw): cookie_name = self._config.cookie_name @@ -170,7 +165,7 @@ web.setcookie( cookie_name, session_id, - expires=expires or self._config.timeout, + expires=expires, domain=cookie_domain, httponly=httponly, secure=secure, @@ -317,7 +312,10 @@ path = self._get_path(f) atime = os.stat(path).st_atime if now - atime > timeout: - shutil.rmtree(path) + if os.path.isdir(path): + shutil.rmtree(path) + else: + os.remove(path) class DBStore(Store): @@ -432,8 +430,7 @@ return key in self.d_store def __getitem__(self, key): - """ Return the value and update the last seen value - """ + """Return the value and update the last seen value""" t, value = self.d_store[key] self.d_store[key] = (time.time(), value) return value diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/web.py-0.61/web/template.py new/web.py-0.62/web/template.py --- old/web.py-0.61/web/template.py 2020-07-27 16:43:53.000000000 +0200 +++ new/web.py-0.62/web/template.py 2020-11-08 19:37:39.000000000 +0100 @@ -73,8 +73,7 @@ class Parser: - """Parser Base. - """ + """Parser Base.""" def __init__(self): self.statement_nodes = STATEMENT_NODES @@ -129,11 +128,11 @@ def read_var(self, text): r"""Reads a var statement. - >>> read_var = Parser().read_var - >>> read_var('var x=10\nfoo') - (<var: x = 10>, 'foo') - >>> read_var('var x: hello $name\nfoo') - (<var: x = join_(u'hello ', escape_(name, True))>, 'foo') + >>> read_var = Parser().read_var + >>> read_var('var x=10\nfoo') + (<var: x = 10>, 'foo') + >>> read_var('var x: hello $name\nfoo') + (<var: x = join_(u'hello ', escape_(name, True))>, 'foo') """ line, text = splitline(text) tokens = self.python_tokens(line) @@ -167,9 +166,9 @@ def read_suite(self, text): r"""Reads section by section till end of text. - >>> read_suite = Parser().read_suite - >>> read_suite('hello $name\nfoo\n') - [<line: [t'hello ', $name, t'\n']>, <line: [t'foo\n']>] + >>> read_suite = Parser().read_suite + >>> read_suite('hello $name\nfoo\n') + [<line: [t'hello ', $name, t'\n']>, <line: [t'foo\n']>] """ sections = [] while text: @@ -180,13 +179,13 @@ def readline(self, text): r"""Reads one line from the text. Newline is suppressed if the line ends with \. - >>> readline = Parser().readline - >>> readline('hello $name!\nbye!') - (<line: [t'hello ', $name, t'!\n']>, 'bye!') - >>> readline('hello $name!\\\nbye!') - (<line: [t'hello ', $name, t'!']>, 'bye!') - >>> readline('$f()\n\n') - (<line: [$f(), t'\n']>, '\n') + >>> readline = Parser().readline + >>> readline('hello $name!\nbye!') + (<line: [t'hello ', $name, t'!\n']>, 'bye!') + >>> readline('hello $name!\\\nbye!') + (<line: [t'hello ', $name, t'!']>, 'bye!') + >>> readline('$f()\n\n') + (<line: [$f(), t'\n']>, '\n') """ line, text = splitline(text) @@ -204,11 +203,11 @@ def read_node(self, text): r"""Reads a node from the given text and returns the node and remaining text. - >>> read_node = Parser().read_node - >>> read_node('hello $name') - (t'hello ', '$name') - >>> read_node('$name') - ($name, '') + >>> read_node = Parser().read_node + >>> read_node('hello $name') + (t'hello ', '$name') + >>> read_node('$name') + ($name, '') """ if text.startswith("$$"): return TextNode("$"), text[2:] @@ -229,9 +228,9 @@ def read_text(self, text): r"""Reads a text node from the given text. - >>> read_text = Parser().read_text - >>> read_text('hello $name') - (t'hello ', '$name') + >>> read_text = Parser().read_text + >>> read_text('hello $name') + (t'hello ', '$name') """ index = text.find("$") if index < 0: @@ -368,9 +367,9 @@ def read_assignment(self, text): r"""Reads assignment statement from text. - >>> read_assignment = Parser().read_assignment - >>> read_assignment('a = b + 1\nfoo') - (<assignment: 'a = b + 1'>, 'foo') + >>> read_assignment = Parser().read_assignment + >>> read_assignment('a = b + 1\nfoo') + (<assignment: 'a = b + 1'>, 'foo') """ line, text = splitline(text) return AssignmentNode(line.strip()), text @@ -378,13 +377,13 @@ def python_lookahead(self, text): """Returns the first python token from the given text. - >>> python_lookahead = Parser().python_lookahead - >>> python_lookahead('for i in range(10):') - 'for' - >>> python_lookahead('else:') - 'else' - >>> python_lookahead(' x = 1') - ' ' + >>> python_lookahead = Parser().python_lookahead + >>> python_lookahead('for i in range(10):') + 'for' + >>> python_lookahead('else:') + 'else' + >>> python_lookahead(' x = 1') + ' ' """ i = iter([text]) readline = lambda: next(i) @@ -427,9 +426,9 @@ def read_statement(self, text): r"""Reads a python statement. - >>> read_statement = Parser().read_statement - >>> read_statement('for i in range(10): hello $name') - ('for i in range(10):', ' hello $name') + >>> read_statement = Parser().read_statement + >>> read_statement('for i in range(10): hello $name') + ('for i in range(10):', ' hello $name') """ tok = PythonTokenizer(text) tok.consume_till(":") @@ -498,12 +497,12 @@ def consume_till(self, delim): """Consumes tokens till colon. - >>> tok = PythonTokenizer('for i in range(10): hello $i') - >>> tok.consume_till(':') - >>> tok.text[:tok.index] - 'for i in range(10):' - >>> tok.text[tok.index:] - ' hello $i' + >>> tok = PythonTokenizer('for i in range(10): hello $i') + >>> tok.consume_till(':') + >>> tok.text[:tok.index] + 'for i in range(10):' + >>> tok.text[tok.index:] + ' hello $i' """ try: while True: @@ -829,8 +828,7 @@ class ForLoopContext: - """Stackable context for ForLoop to support nested for loops. - """ + """Stackable context for ForLoop to support nested for loops.""" def __init__(self, forloop, parent): self._forloop = forloop @@ -942,6 +940,13 @@ builtins=builtins, ) + def __repr__(self): + """ + >>> Template(text='Template text', filename='burndown_chart.html') + <Template burndown_chart.html> + """ + return "<{} {}>".format(self.__class__.__name__, self.filename) + def normalize_text(text): """Normalizes template text by correcting \r\n, tabs and BOM chars.""" text = text.replace("\r\n", "\n").replace("\r", "\n").expandtabs() @@ -1086,9 +1091,8 @@ path, cache=self._cache is not None, base=self._base, **self._keywords ) elif kind == "file": - return Template( - open(path, encoding="utf-8").read(), filename=path, **self._keywords - ) + with open(path, encoding="utf-8") as tmpl_file: + return Template(tmpl_file.read(), filename=path, **self._keywords) else: raise AttributeError("No template named " + name) @@ -1167,8 +1171,7 @@ def frender(path, **keywords): - """Creates a template from the given file path. - """ + """Creates a template from the given file path.""" return Template(open(path, encoding="utf-8").read(), filename=path, **keywords) @@ -1444,8 +1447,7 @@ return self._d.keys() def _prepare_body(self): - """Prepare value of __body__ by joining parts. - """ + """Prepare value of __body__ by joining parts.""" if self._parts: value = u"".join(self._parts) self._parts[:] = [] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/web.py-0.61/web/utils.py new/web.py-0.62/web/utils.py --- old/web.py-0.61/web/utils.py 2020-07-27 16:43:53.000000000 +0200 +++ new/web.py-0.62/web/utils.py 2020-11-08 19:37:39.000000000 +0100 @@ -204,19 +204,19 @@ class Counter(storage): """Keeps count of how many times something is added. - >>> c = counter() - >>> c.add('x') - >>> c.add('x') - >>> c.add('x') - >>> c.add('x') - >>> c.add('x') - >>> c.add('y') - >>> c['y'] - 1 - >>> c['x'] - 5 - >>> c.most() - ['x'] + >>> c = counter() + >>> c.add('x') + >>> c.add('x') + >>> c.add('x') + >>> c.add('x') + >>> c.add('x') + >>> c.add('y') + >>> c['y'] + 1 + >>> c['x'] + 5 + >>> c.most() + ['x'] """ def add(self, n): @@ -236,51 +236,51 @@ def percent(self, key): """Returns what percentage a certain key is of all entries. - >>> c = counter() - >>> c.add('x') - >>> c.add('x') - >>> c.add('x') - >>> c.add('y') - >>> c.percent('x') - 0.75 - >>> c.percent('y') - 0.25 + >>> c = counter() + >>> c.add('x') + >>> c.add('x') + >>> c.add('x') + >>> c.add('y') + >>> c.percent('x') + 0.75 + >>> c.percent('y') + 0.25 """ return float(self[key]) / sum(self.values()) def sorted_keys(self): """Returns keys sorted by value. - >>> c = counter() - >>> c.add('x') - >>> c.add('x') - >>> c.add('y') - >>> c.sorted_keys() - ['x', 'y'] + >>> c = counter() + >>> c.add('x') + >>> c.add('x') + >>> c.add('y') + >>> c.sorted_keys() + ['x', 'y'] """ return sorted(self.keys(), key=lambda k: self[k], reverse=True) def sorted_values(self): """Returns values sorted by value. - >>> c = counter() - >>> c.add('x') - >>> c.add('x') - >>> c.add('y') - >>> c.sorted_values() - [2, 1] + >>> c = counter() + >>> c.add('x') + >>> c.add('x') + >>> c.add('y') + >>> c.sorted_values() + [2, 1] """ return [self[k] for k in self.sorted_keys()] def sorted_items(self): """Returns items sorted by value. - >>> c = counter() - >>> c.add('x') - >>> c.add('x') - >>> c.add('y') - >>> c.sorted_items() - [('x', 2), ('y', 1)] + >>> c = counter() + >>> c.add('x') + >>> c.add('x') + >>> c.add('y') + >>> c.sorted_items() + [('x', 2), ('y', 1)] """ return [(k, self[k]) for k in self.sorted_keys()] @@ -736,8 +736,7 @@ def safeiter(it, cleanup=None, ignore_errors=True): - """Makes an iterator safe by ignoring the exceptions occurred during the iteration. - """ + """Makes an iterator safe by ignoring the exceptions occurred during the iteration.""" def next(): while True: @@ -842,11 +841,11 @@ def requeue(queue, index=-1): """Returns the element at index after moving it to the beginning of the queue. - >>> x = [1, 2, 3, 4] - >>> requeue(x) - 4 - >>> x - [4, 1, 2, 3] + >>> x = [1, 2, 3, 4] + >>> requeue(x) + 4 + >>> x + [4, 1, 2, 3] """ x = queue.pop(index) queue.insert(0, x) @@ -856,11 +855,11 @@ def restack(stack, index=0): """Returns the element at index after moving it to the top of stack. - >>> x = [1, 2, 3, 4] - >>> restack(x) - 1 - >>> x - [2, 3, 4, 1] + >>> x = [1, 2, 3, 4] + >>> restack(x) + 1 + >>> x + [2, 3, 4, 1] """ x = stack.pop(index) stack.append(x) @@ -1282,8 +1281,7 @@ return id(self) def clear_all(): - """Clears all ThreadedDict instances. - """ + """Clears all ThreadedDict instances.""" for t in list(ThreadedDict._instances): t.clear() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/web.py-0.61/web/webapi.py new/web.py-0.62/web/webapi.py --- old/web.py-0.61/web/webapi.py 2020-07-27 16:43:53.000000000 +0200 +++ new/web.py-0.62/web/webapi.py 2020-11-08 19:37:39.000000000 +0100 @@ -237,8 +237,7 @@ def NotFound(message=None): - """Returns HTTPError with '404 Not Found' error from the active application. - """ + """Returns HTTPError with '404 Not Found' error from the active application.""" if message: return _NotFound(message) elif ctx.get("app_stack"): @@ -353,8 +352,7 @@ def UnavailableForLegalReasons(message=None): - """Returns HTTPError with '415 Unavailable For Legal Reasons' error from the active application. - """ + """Returns HTTPError with '415 Unavailable For Legal Reasons' error from the active application.""" if message: return _UnavailableForLegalReasons(message) elif ctx.get("app_stack"): @@ -378,8 +376,7 @@ def InternalError(message=None): - """Returns HTTPError with '500 internal error' error from the active application. - """ + """Returns HTTPError with '500 internal error' error from the active application.""" if message: return _InternalError(message) elif ctx.get("app_stack"): @@ -428,8 +425,7 @@ def rawinput(method=None): - """Returns storage object with GET or POST arguments. - """ + """Returns storage object with GET or POST arguments.""" method = method or "both" def dictify(fs): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/web.py-0.61/web.py.egg-info/PKG-INFO new/web.py-0.62/web.py.egg-info/PKG-INFO --- old/web.py-0.61/web.py.egg-info/PKG-INFO 2020-07-27 17:34:14.000000000 +0200 +++ new/web.py-0.62/web.py.egg-info/PKG-INFO 2020-11-09 12:20:51.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: web.py -Version: 0.61 +Version: 0.62 Summary: web.py: makes web apps Home-page: http://webpy.org/ Author: Aaron Swartz @@ -15,11 +15,11 @@ [](https://travis-ci.org/webpy/webpy) [](https://codecov.io/gh/webpy/webpy) - The latest stable release `0.61` only supports Python >= 3.5. + The latest stable release `0.62` only supports Python >= 3.5. To install it, please run: ``` # For Python 3 - python3 -m pip install web.py==0.61 + python3 -m pip install web.py==0.62 ``` If you are still using Python 2.7, then please use web.py version 0.51 @@ -32,12 +32,13 @@ You can also download it from [GitHub Releases](https://github.com/webpy/webpy/releases) page, then install it manually: ``` - unzip webpy-0.61.zip - cd webpy-0.61/ + unzip webpy-0.62.zip + cd webpy-0.62/ python3 setup.py install ``` Note: `0.5x` (e.g. 0.50, 0.51) are our last releases which support Python 2. + Note: `0.6x` (e.g. 0.60, 0.61, 0.62) are our last releases which support Python 3.5. Platform: any Classifier: License :: Public Domain @@ -47,5 +48,6 @@ Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 Requires-Python: >=3.5 Description-Content-Type: text/markdown
