From aa63296e701e8f0bacee62ef373c1bc7b65576fc Mon Sep 17 00:00:00 2001 From: AniX Date: Mon, 5 Nov 2018 21:34:16 +0100 Subject: [PATCH 1/6] #140: upgrade gcp-devrel-py-tools to 0.0.15 reason: travis build environment upgraded pip from 6.0.7 to 9.0.1, which is incompatible with gcp-devrel-py-tools 0.0.8; otherwise build-job `tests_gaesdk` would keep failing with `ImportError` --- requirements-dev-gaesdk.txt | 2 +- requirements-dev.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements-dev-gaesdk.txt b/requirements-dev-gaesdk.txt index f2bfad1..2ed95ad 100644 --- a/requirements-dev-gaesdk.txt +++ b/requirements-dev-gaesdk.txt @@ -11,4 +11,4 @@ gaepytz==2011h pytest==2.9.1 pytest-cov==2.2.1 mock==2.0.0 -gcp-devrel-py-tools==0.0.8 +gcp-devrel-py-tools==0.0.15 diff --git a/requirements-dev.txt b/requirements-dev.txt index 648d555..d9acb7d 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -9,4 +9,4 @@ pytest==2.9.1 pytest-cov==2.2.1 mock==2.0.0 six==1.10.0 -gcp-devrel-py-tools==0.0.8 +gcp-devrel-py-tools==0.0.15 From 7136b61ce972ccb13eca8af42f6e84d3bd1038bf Mon Sep 17 00:00:00 2001 From: AniX Date: Tue, 6 Nov 2018 01:00:29 +0100 Subject: [PATCH 2/6] #139: resolve lint errors after changes in more recent flake8 versions - add .flake8, ignore must also include (replaced) default-ignore, and a few of them are mutually exclusive - ignore class I, for import rules that are in conflict with Google Python Style Guide - reformatting some places in code to avoid errors - not yet addressed: W605 invalid escape sequence '\d' --- .flake8 | 15 +++++++++++++ webapp2.py | 4 ++-- webapp2_extras/auth.py | 16 ++++++------- webapp2_extras/i18n.py | 46 +++++++++++++++++++------------------- webapp2_extras/jinja2.py | 6 ++--- webapp2_extras/local.py | 2 +- webapp2_extras/sessions.py | 22 +++++++++--------- 7 files changed, 63 insertions(+), 48 deletions(-) create mode 100644 .flake8 diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..137a10a --- /dev/null +++ b/.flake8 @@ -0,0 +1,15 @@ +[flake8] +# flake8 added error class "I" that we need to ignore in this project +# as they contradict import grouping as suggested by Google Python Style Guide, +# https://github.com/google/styleguide/blob/gh-pages/pyguide.md#313-imports-formatting +# +# I100: Your import statements are in the wrong order. +# I101: The names in your from import are in the wrong order. +# I201: Missing newline between import groups. +# I202: Additional newline in a group of imports. +# W503: line break before binary operator - see updated PEP8 (01-Aug-2013) +# DEFAULTS: E121, E123, E126, E133, E226, E241, E242, E704, W503, W504 and W505 +# are ignored because they are not rules unanimously accepted +# +# ATTENTION: Replaces all default ignores: +ignore = I100,I101,I201,I202,E121,E123,E126,E133,E226,E241,E242,E704,W503,W505 \ No newline at end of file diff --git a/webapp2.py b/webapp2.py index e48e7bf..fff4519 100755 --- a/webapp2.py +++ b/webapp2.py @@ -935,8 +935,8 @@ def __init__(self, template, handler=None, name=None, defaults=None, Format Example ================= ================================== ```` ``'/blog//'`` - ``<:regex>`` ``'/blog/<:\d{4}>/<:\d{2}>'`` - ```` ``'/blog//'`` + ``<:regex>`` ``r'/blog/<:\d{4}>/<:\d{2}>'`` + ```` ``r'/blog//'`` ================= ================================== The same template can mix parts with name, regular expression or diff --git a/webapp2_extras/auth.py b/webapp2_extras/auth.py index 30490d0..5b44b6f 100644 --- a/webapp2_extras/auth.py +++ b/webapp2_extras/auth.py @@ -61,11 +61,11 @@ # The user object must provide all of them as attributes. #: Default is an empty list. default_config = { - 'user_model': 'webapp2_extras.appengine.auth.models.User', + 'user_model': 'webapp2_extras.appengine.auth.models.User', 'session_backend': 'securecookie', - 'cookie_name': 'auth', - 'token_max_age': 86400 * 7 * 3, - 'token_new_age': 86400, + 'cookie_name': 'auth', + 'token_max_age': 86400 * 7 * 3, + 'token_new_age': 86400, 'token_cache_age': 3600, 'user_attributes': [], } @@ -418,9 +418,9 @@ def get_user_by_token(self, user_id, token, token_ts=None, cache=None, A user dict or None. """ if self._user is not None: - assert (self._user is not _anon and - self._user['user_id'] == user_id and - self._user['token'] == token) + assert (self._user is not _anon + and self._user['user_id'] == user_id + and self._user['token'] == token) return self._user_or_none() if cache and cache_ts: @@ -518,7 +518,7 @@ def set_session(self, user, token=None, token_ts=None, cache_ts=None, # the session metadata (token, timestamps etc). This is easier to test. # But we could store only user_id and custom user attributes instead. user.update({ - 'token': token, + 'token': token, 'token_ts': token_ts, 'cache_ts': cache_ts, 'remember': int(remember), diff --git a/webapp2_extras/i18n.py b/webapp2_extras/i18n.py index 37f17e4..e55bb1f 100644 --- a/webapp2_extras/i18n.py +++ b/webapp2_extras/i18n.py @@ -66,31 +66,31 @@ #: date_formats #: Default date formats for datetime, date and time. default_config = { - 'translations_path': 'locale', - 'domains': ['messages'], - 'default_locale': 'en_US', - 'default_timezone': 'UTC', - 'locale_selector': None, - 'timezone_selector': None, + 'translations_path': 'locale', + 'domains': ['messages'], + 'default_locale': 'en_US', + 'default_timezone': 'UTC', + 'locale_selector': None, + 'timezone_selector': None, 'date_formats': { - 'time': 'medium', - 'date': 'medium', - 'datetime': 'medium', - 'time.short': None, - 'time.medium': None, - 'time.full': None, - 'time.long': None, - 'time.iso': "HH':'mm':'ss", - 'date.short': None, - 'date.medium': None, - 'date.full': None, - 'date.long': None, - 'date.iso': "yyyy'-'MM'-'dd", - 'datetime.short': None, + 'time': 'medium', + 'date': 'medium', + 'datetime': 'medium', + 'time.short': None, + 'time.medium': None, + 'time.full': None, + 'time.long': None, + 'time.iso': "HH':'mm':'ss", + 'date.short': None, + 'date.medium': None, + 'date.full': None, + 'date.long': None, + 'date.iso': "yyyy'-'MM'-'dd", + 'datetime.short': None, 'datetime.medium': None, - 'datetime.full': None, - 'datetime.long': None, - 'datetime.iso': "yyyy'-'MM'-'dd'T'HH':'mm':'ssZ", + 'datetime.full': None, + 'datetime.long': None, + 'datetime.iso': "yyyy'-'MM'-'dd'T'HH':'mm':'ssZ", }, } diff --git a/webapp2_extras/jinja2.py b/webapp2_extras/jinja2.py index 7f88b75..c4753db 100644 --- a/webapp2_extras/jinja2.py +++ b/webapp2_extras/jinja2.py @@ -151,9 +151,9 @@ def __init__(self, app, config=None): lambda s, p, n: i18n.ngettext(s, p, n), newstyle=True) env.filters.update({ - 'format_date': i18n.format_date, - 'format_time': i18n.format_time, - 'format_datetime': i18n.format_datetime, + 'format_date': i18n.format_date, + 'format_time': i18n.format_time, + 'format_datetime': i18n.format_datetime, 'format_timedelta': i18n.format_timedelta, }) diff --git a/webapp2_extras/local.py b/webapp2_extras/local.py index 9270078..91c63af 100644 --- a/webapp2_extras/local.py +++ b/webapp2_extras/local.py @@ -31,7 +31,7 @@ get_current_greenlet = greenlet.getcurrent del greenlet - except: + except: # noqa: E722 # catch all, py.* fails with so many different errors. get_current_greenlet = int try: diff --git a/webapp2_extras/sessions.py b/webapp2_extras/sessions.py index 7a0ec2e..7391561 100644 --- a/webapp2_extras/sessions.py +++ b/webapp2_extras/sessions.py @@ -67,22 +67,22 @@ #: A dictionary of available session backend classes used by #: :meth:`SessionStore.get_session`. default_config = { - 'secret_key': None, - 'cookie_name': 'session', + 'secret_key': None, + 'cookie_name': 'session', 'session_max_age': None, 'cookie_args': { - 'max_age': None, - 'domain': None, - 'path': '/', - 'secure': None, - 'httponly': False, + 'max_age': None, + 'domain': None, + 'path': '/', + 'secure': None, + 'httponly': False, }, 'backends': { 'securecookie': 'webapp2_extras.sessions.SecureCookieSessionFactory', - 'datastore': 'webapp2_extras.appengine.sessions_ndb.' - 'DatastoreSessionFactory', - 'memcache': 'webapp2_extras.appengine.sessions_memcache.' - 'MemcacheSessionFactory', + 'datastore': 'webapp2_extras.appengine.sessions_ndb.' + 'DatastoreSessionFactory', + 'memcache': 'webapp2_extras.appengine.sessions_memcache.' + 'MemcacheSessionFactory', }, } From f51d59db1a6434c82ed60bfb7e802805a8ce2a83 Mon Sep 17 00:00:00 2001 From: AniX Date: Tue, 6 Nov 2018 02:25:02 +0100 Subject: [PATCH 3/6] commit to trigger travis build --- .flake8 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.flake8 b/.flake8 index 137a10a..7604382 100644 --- a/.flake8 +++ b/.flake8 @@ -12,4 +12,4 @@ # are ignored because they are not rules unanimously accepted # # ATTENTION: Replaces all default ignores: -ignore = I100,I101,I201,I202,E121,E123,E126,E133,E226,E241,E242,E704,W503,W505 \ No newline at end of file +ignore = I100,I101,I201,I202,E121,E123,E126,E133,E226,E241,E242,E704,W503,W505 From da2810e67c63d0494bec3d6f23af78e42cbb9131 Mon Sep 17 00:00:00 2001 From: AniX Date: Tue, 6 Nov 2018 11:12:39 +0100 Subject: [PATCH 4/6] #139: resolve lint errors after changes in more recent flake8 versions - address W504 line break after binary operator and E501 line too long - not yet addressed: W605 invalid escape sequence '\d' --- webapp2.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/webapp2.py b/webapp2.py index fff4519..63bb24b 100755 --- a/webapp2.py +++ b/webapp2.py @@ -514,8 +514,8 @@ def wsgi_write(self, start_response): :param start_response: The WSGI-compatible start_response function. """ - if (self.headers.get('Cache-Control') == 'no-cache' and - not self.headers.get('Expires')): + if (self.headers.get('Cache-Control') == 'no-cache' + and not self.headers.get('Expires')): self.headers['Expires'] = 'Fri, 01 Jan 1990 00:00:00 GMT' self.headers['Content-Length'] = str(len(self.body)) From 561edd552edd0976cae8b0c6e8e084abdcbafcb4 Mon Sep 17 00:00:00 2001 From: AniX Date: Tue, 6 Nov 2018 11:49:30 +0100 Subject: [PATCH 5/6] #139: resolve lint errors after changes in more recent flake8 versions resolve `W605 invalid escape sequence '\x'`, see https://docs.python.org/3/whatsnew/3.6.html#deprecated-python-behavior flake8 3.6.0 produces W605 in docstrings that are not raw-string --- tests/extras_routes_test.py | 4 ++-- tests/handler_test.py | 4 ++-- tests/routing_test.py | 6 +++--- webapp2.py | 4 ++-- webapp2_extras/routes.py | 26 +++++++++++++------------- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/tests/extras_routes_test.py b/tests/extras_routes_test.py index 8f35c22..a9810a0 100644 --- a/tests/extras_routes_test.py +++ b/tests/extras_routes_test.py @@ -225,7 +225,7 @@ def test_simple(self): def test_with_variables_name_and_handler(self): router = webapp2.Router([ - PathPrefixRoute('/user/', [ + PathPrefixRoute(r'/user/', [ HandlerPrefixRoute('apps.users.', [ NamePrefixRoute('user-', [ webapp2.Route('/', 'UserOverviewHandler', 'overview'), @@ -301,7 +301,7 @@ def test_simple(self): def test_with_variables_name_and_handler(self): router = webapp2.Router([ DomainRoute('.<:.*>', [ - PathPrefixRoute('/user/', [ + PathPrefixRoute(r'/user/', [ HandlerPrefixRoute('apps.users.', [ NamePrefixRoute('user-', [ webapp2.Route( diff --git a/tests/handler_test.py b/tests/handler_test.py index ffb2c3c..45624b3 100644 --- a/tests/handler_test.py +++ b/tests/handler_test.py @@ -163,9 +163,9 @@ def get_redirect_url(handler, **kwargs): webapp2.Route('/methods', MethodsHandler, name='methods'), webapp2.Route('/broken', BrokenHandler), webapp2.Route('/broken-but-fixed', BrokenButFixedHandler), - webapp2.Route('///', None, + webapp2.Route(r'///', None, name='route-test'), - webapp2.Route('/<:\d\d>/<:\d{2}>/<:\w+>', PositionalHandler, + webapp2.Route(r'/<:\d\d>/<:\d{2}>/<:\w+>', PositionalHandler, name='positional'), webapp2.Route('/redirect-me', webapp2.RedirectHandler, defaults={'_uri': '/broken'}), diff --git a/tests/routing_test.py b/tests/routing_test.py index 463d114..fde7bf2 100644 --- a/tests/routing_test.py +++ b/tests/routing_test.py @@ -209,7 +209,7 @@ def test_build_int_variable(self): def test_router_build_error(self): router = Router(None) - router.add(Route('/', None, name='year-page')) + router.add(Route(r'/', None, name='year-page')) url = router.build( Request.blank('/'), 'year-page', (), dict(year='2010')) @@ -233,7 +233,7 @@ def test_reverse_template(self): # Access route.regex just to set the lazy properties. self.assertEqual(route.reverse_template, '/foo/%(bar)s') - route = Route('/foo//', None) + route = Route(r'/foo//', None) route.regex # Access route.regex just to set the lazy properties. self.assertEqual(route.reverse_template, '/foo/%(bar)s/%(baz)s') @@ -259,7 +259,7 @@ def test_build_full_without_request(self): ) def test_positions(self): - template = '/<:\d+>' * 98 + template = r'/<:\d+>' * 98 args = tuple(str(i) for i in range(98)) url_res = '/' + '/'.join(args) diff --git a/webapp2.py b/webapp2.py index 63bb24b..9812a2a 100755 --- a/webapp2.py +++ b/webapp2.py @@ -924,7 +924,7 @@ class Route(BaseRoute): def __init__(self, template, handler=None, name=None, defaults=None, build_only=False, handler_method=None, methods=None, schemes=None): - """Initializes this route. + r"""Initializes this route. :param template: A route template to match against the request path. A template @@ -951,7 +951,7 @@ def __init__(self, template, handler=None, name=None, defaults=None, Route('//settings', handler=SettingsHandler, name='user-settings') - Route('//settings', handler=SettingsHandler, + Route(r'//settings', handler=SettingsHandler, name='user-settings') .. note:: diff --git a/webapp2_extras/routes.py b/webapp2_extras/routes.py index 24b5bfc..39fd76b 100644 --- a/webapp2_extras/routes.py +++ b/webapp2_extras/routes.py @@ -121,19 +121,19 @@ def match(self, request): def regex(self): regex, reverse_template, args_count, kwargs_count, variables = \ webapp2._parse_route_template(self.template, - default_sufix='[^\.]+') + default_sufix=r'[^\.]+') return regex class NamePrefixRoute(MultiRoute): - """The idea of this route is to set a base name for other routes:: + r"""The idea of this route is to set a base name for other routes:: app = WSGIApplication([ NamePrefixRoute('user-', [ - Route('/users//', UserOverviewHandler, 'overview'), - Route('/users//profile', UserProfileHandler, + Route(r'/users//', UserOverviewHandler, 'overview'), + Route(r'/users//profile', UserProfileHandler, 'profile'), - Route('/users//projects', UserProjectsHandler, + Route(r'/users//projects', UserProjectsHandler, 'projects'), ]), ]) @@ -142,10 +142,10 @@ class NamePrefixRoute(MultiRoute): convenient as you can reuse the name prefix:: app = WSGIApplication([ - Route('/users//', UserOverviewHandler, 'user-overview'), - Route('/users//profile', UserProfileHandler, + Route(r'/users//', UserOverviewHandler, 'user-overview'), + Route(r'/users//profile', UserProfileHandler, 'user-profile'), - Route('/users//projects', UserProjectsHandler, + Route(r'/users//projects', UserProjectsHandler, 'user-projects'), ]) """ @@ -174,23 +174,23 @@ class HandlerPrefixRoute(NamePrefixRoute): class PathPrefixRoute(NamePrefixRoute): - """Same as :class:`NamePrefixRoute`, but prefixes the route path. + r"""Same as :class:`NamePrefixRoute`, but prefixes the route path. For example, imagine we have these routes:: app = WSGIApplication([ - Route('/users//', UserOverviewHandler, + Route(r'/users//', UserOverviewHandler, 'user-overview'), - Route('/users//profile', UserProfileHandler, + Route(r'/users//profile', UserProfileHandler, 'user-profile'), - Route('/users//projects', UserProjectsHandler, + Route(r'/users//projects', UserProjectsHandler, 'user-projects'), ]) We could refactor them to reuse the common path prefix:: app = WSGIApplication([ - PathPrefixRoute('/users/', [ + PathPrefixRoute(r'/users/', [ Route('/', UserOverviewHandler, 'user-overview'), Route('/profile', UserProfileHandler, 'user-profile'), Route('/projects', UserProjectsHandler, 'user-projects'), From 89223170fbe28dd61690457b9b4493c5418d0fea Mon Sep 17 00:00:00 2001 From: AniX Date: Tue, 6 Nov 2018 13:09:33 +0100 Subject: [PATCH 6/6] #140: Replace webapp1 tests by webapp2 tests It seems that somewhere between 1.9.54 and 1.9.66, App Engine SDK removed support of webapp1, probably because support of Python 2.5 also had been removed from App Engine Python standard environment. As a result, the behavior with using old style request handlers changed, and so two tests in webapp1_test.py would fail. Resolution: Replace tests regarding old WSGIApplication and/or old RequestHandler. All gaesdk tests use WSGIApplication and RequestHandler of webapp2. Asserts reflect behavior of webapp2 app and handler. --- .../gae/{webapp1_test.py => webapp2_test.py} | 59 +++++++------------ 1 file changed, 21 insertions(+), 38 deletions(-) rename tests/gae/{webapp1_test.py => webapp2_test.py} (73%) diff --git a/tests/gae/webapp1_test.py b/tests/gae/webapp2_test.py similarity index 73% rename from tests/gae/webapp1_test.py rename to tests/gae/webapp2_test.py index 3de8f0e..50d1a98 100644 --- a/tests/gae/webapp1_test.py +++ b/tests/gae/webapp2_test.py @@ -13,35 +13,18 @@ # See the License for the specific language governing permissions and # limitations under the License. -from google.appengine.ext import webapp - import test_base import webapp2 -# Old WSGIApplication, new RequestHandler. +# WSGIApplication and RequestHandler of webapp2 +# webapp1 is not supported anymore class NewStyleHandler(webapp2.RequestHandler): def get(self, text): self.response.out.write(text) -app = webapp.WSGIApplication([ - (r'/test/(.*)', NewStyleHandler), -]) - - -# New WSGIApplication, old RequestHandler. -class OldStyleHandler(webapp.RequestHandler): - def get(self, text): - self.response.out.write(text) - - -class OldStyleHandler2(webapp.RequestHandler): - def get(self, text=None): - self.response.out.write(text) - - -class OldStyleHandlerWithError(webapp.RequestHandler): +class NewStyleHandlerWithError(webapp2.RequestHandler): def get(self, text): raise ValueError() @@ -51,27 +34,27 @@ def handle_exception(self, e, debug): app2 = webapp2.WSGIApplication([ - (r'/test/error', OldStyleHandlerWithError), - (r'/test/(.*)', OldStyleHandler), - webapp2.Route(r'/test2/', OldStyleHandler2), + (r'/test/error', NewStyleHandlerWithError), + (r'/test/(.*)', NewStyleHandler), + webapp2.Route(r'/test2/', NewStyleHandler), ]) -class TestWebapp1(test_base.BaseTestCase): - def test_old_app_new_handler(self): +class TestWebapp2(test_base.BaseTestCase): + def test_app_handler(self): req = webapp2.Request.blank('/test/foo') - rsp = req.get_response(app) + rsp = req.get_response(app2) self.assertEqual(rsp.status_int, 200) self.assertEqual(rsp.body, 'foo') req = webapp2.Request.blank('/test/bar') - rsp = req.get_response(app) + rsp = req.get_response(app2) self.assertEqual(rsp.status_int, 200) self.assertEqual(rsp.body, 'bar') - self.assertTrue(issubclass(OldStyleHandler, webapp.RequestHandler)) + self.assertTrue(issubclass(NewStyleHandler, webapp2.RequestHandler)) - def test_new_app_old_handler(self): + def test_handler_200(self): req = webapp2.Request.blank('/test/foo') rsp = req.get_response(app2) self.assertEqual(rsp.status_int, 200) @@ -82,33 +65,33 @@ def test_new_app_old_handler(self): self.assertEqual(rsp.status_int, 200) self.assertEqual(rsp.body, 'bar') - def test_new_app_old_handler_405(self): + def test_handler_405(self): req = webapp2.Request.blank('/test/foo') req.method = 'POST' rsp = req.get_response(app2) self.assertEqual(rsp.status_int, 405) - self.assertEqual(rsp.headers.get('Allow'), None) + self.assertEqual(rsp.headers.get('Allow'), 'GET') - def test_new_app_old_handler_501(self): + def test_handler_405_new_method(self): app2.allowed_methods = list(app2.allowed_methods) + ['NEW_METHOD'] req = webapp2.Request.blank('/test/foo') req.method = 'NEW_METHOD' rsp = req.get_response(app2) - self.assertEqual(rsp.status_int, 501) + self.assertEqual(rsp.status_int, 405) - def test_new_app_old_handler_501_2(self): + def test_handler_501(self): req = webapp2.Request.blank('/test/foo') req.method = 'WHATMETHODISTHIS' rsp = req.get_response(app2) self.assertEqual(rsp.status_int, 501) - def test_new_app_old_handler_with_error(self): + def test_handler_with_error(self): req = webapp2.Request.blank('/test/error') rsp = req.get_response(app2) self.assertEqual(rsp.status_int, 500) self.assertEqual(rsp.body, 'ValueError!') - def test_new_app_old_kwargs(self): + def test_kwargs(self): req = webapp2.Request.blank('/test2/foo') rsp = req.get_response(app2) self.assertEqual(rsp.status_int, 200) @@ -126,11 +109,11 @@ def test_unicode_cookie(self): # So we have to do it. quoted_value = urllib.quote(initial_value.encode('utf-8')) - rsp = webapp.Response() + rsp = webapp2.Response() rsp.headers['Set-Cookie'] = 'app=%s; Path=/' % quoted_value cookie = rsp.headers.get('Set-Cookie') - req = webapp.Request.blank('/', headers=[('Cookie', cookie)]) + req = webapp2.Request.blank('/', headers=[('Cookie', cookie)]) # The stored value is the same quoted value from before. stored_value = req.cookies.get('app')