From 90288a013446ce15434fee196c932709213b4c14 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Sat, 14 Apr 2018 11:42:31 +0300 Subject: [PATCH 1/3] fix sessions in cookies for python3 --- gluon/globals.py | 2 +- gluon/utils.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/gluon/globals.py b/gluon/globals.py index 767a9b0b..95e310c6 100644 --- a/gluon/globals.py +++ b/gluon/globals.py @@ -1167,7 +1167,7 @@ class Session(Storage): compression_level=compression_level) rcookies = response.cookies rcookies.pop(name, None) - rcookies[name] = value + rcookies[name] = value.decode('utf8') rcookies[name]['path'] = '/' expires = response.session_cookie_expires if isinstance(expires, datetime.datetime): diff --git a/gluon/utils.py b/gluon/utils.py index 3b26a51b..3b93ea18 100644 --- a/gluon/utils.py +++ b/gluon/utils.py @@ -207,6 +207,8 @@ def secure_dumps(data, encryption_key, hash_key=None, compression_level=None): def secure_loads(data, encryption_key, hash_key=None, compression_level=None): + if not isinstance(data, bytes): + data = bytes(data, 'utf8') components = data.count(b':') if components == 1: return secure_loads_deprecated(data, encryption_key, hash_key, compression_level) From 086bfb58512162437d11e6693a4060c181bc04ff Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Sat, 14 Apr 2018 14:33:37 +0300 Subject: [PATCH 2/3] fix redis sessions and redis for python3 --- gluon/contrib/redis_cache.py | 4 ++-- gluon/contrib/redis_session.py | 12 +++++++----- gluon/contrib/redis_utils.py | 4 ++-- gluon/globals.py | 6 ++++-- 4 files changed, 15 insertions(+), 11 deletions(-) diff --git a/gluon/contrib/redis_cache.py b/gluon/contrib/redis_cache.py index 05aa53fc..110360eb 100644 --- a/gluon/contrib/redis_cache.py +++ b/gluon/contrib/redis_cache.py @@ -10,7 +10,7 @@ except: import time import re import logging -import thread +from threading import Lock import random from gluon import current from gluon.cache import CacheAbstract @@ -19,7 +19,7 @@ from gluon.contrib.redis_utils import register_release_lock, RConnectionError logger = logging.getLogger("web2py.cache.redis") -locker = thread.allocate_lock() +locker = Lock() def RedisCache(redis_conn=None, debug=False, with_lock=False, fail_gracefully=False, db=None): diff --git a/gluon/contrib/redis_session.py b/gluon/contrib/redis_session.py index 1273e6de..b26b6b21 100644 --- a/gluon/contrib/redis_session.py +++ b/gluon/contrib/redis_session.py @@ -8,7 +8,7 @@ Redis-backed sessions """ import logging -import thread +from threading import Lock from gluon import current from gluon.storage import Storage from gluon.contrib.redis_utils import acquire_lock, release_lock @@ -16,7 +16,7 @@ from gluon.contrib.redis_utils import register_release_lock logger = logging.getLogger("web2py.session.redis") -locker = thread.allocate_lock() +locker = Lock() def RedisSession(redis_conn, session_expiry=False, with_lock=False, db=None): @@ -43,7 +43,7 @@ def RedisSession(redis_conn, session_expiry=False, with_lock=False, db=None): try: instance_name = 'redis_instance_' + current.request.application if not hasattr(RedisSession, instance_name): - setattr(RedisSession, instance_name, + setattr(RedisSession, instance_name, RedisClient(redis_conn, session_expiry=session_expiry, with_lock=with_lock)) return getattr(RedisSession, instance_name) finally: @@ -185,8 +185,10 @@ class MockQuery(object): if rtn: if self.unique_key: # make sure the id and unique_key are correct - if rtn['unique_key'] == self.unique_key: - rtn['update_record'] = self.update # update record support + if not isinstance(self.unique_key, bytes): + self.unique_key = bytes(self.unique_key, 'utf8') + if rtn[b'unique_key'] == self.unique_key: + rtn[b'update_record'] = self.update # update record support else: rtn = None return [Storage(rtn)] if rtn else [] diff --git a/gluon/contrib/redis_utils.py b/gluon/contrib/redis_utils.py index 217ca952..8dc7b2d3 100644 --- a/gluon/contrib/redis_utils.py +++ b/gluon/contrib/redis_utils.py @@ -11,7 +11,7 @@ to ensure compatibility with another - similar - library """ import logging -import thread +from threading import Lock import time from gluon import current @@ -26,7 +26,7 @@ except ImportError: raise RuntimeError('Needs redis library to work') -locker = thread.allocate_lock() +locker = Lock() def RConn(*args, **vars): diff --git a/gluon/globals.py b/gluon/globals.py index 95e310c6..edf0e504 100644 --- a/gluon/globals.py +++ b/gluon/globals.py @@ -967,7 +967,7 @@ class Session(Storage): if row: # rows[0].update_record(locked=True) # Unpickle the data - session_data = pickle.loads(row.session_data) + session_data = pickle.loads(row[b'session_data']) self.update(session_data) response.session_new = False else: @@ -1049,7 +1049,9 @@ class Session(Storage): if record_id.isdigit() and long(record_id) > 0: new_unique_key = web2py_uuid() row = table(record_id) - if row and row.unique_key == unique_key: + if not isinstance(unique_key, bytes): + unique_key = bytes(unique_key, 'utf8') + if row and row[b'unique_key'] == unique_key: table._db(table.id == record_id).update(unique_key=new_unique_key) else: record_id = None From 0900a3ddb94c10cfb509726277798c2a71a25dbb Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Sat, 14 Apr 2018 14:58:53 +0300 Subject: [PATCH 3/3] discovered and use _compat to_bytes and to_native functions --- gluon/contrib/redis_session.py | 5 ++--- gluon/globals.py | 22 ++++++++++------------ gluon/utils.py | 3 +-- 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/gluon/contrib/redis_session.py b/gluon/contrib/redis_session.py index b26b6b21..e5771719 100644 --- a/gluon/contrib/redis_session.py +++ b/gluon/contrib/redis_session.py @@ -13,6 +13,7 @@ from gluon import current from gluon.storage import Storage from gluon.contrib.redis_utils import acquire_lock, release_lock from gluon.contrib.redis_utils import register_release_lock +from gluon._compat import to_bytes logger = logging.getLogger("web2py.session.redis") @@ -185,9 +186,7 @@ class MockQuery(object): if rtn: if self.unique_key: # make sure the id and unique_key are correct - if not isinstance(self.unique_key, bytes): - self.unique_key = bytes(self.unique_key, 'utf8') - if rtn[b'unique_key'] == self.unique_key: + if rtn[b'unique_key'] == to_bytes(self.unique_key): rtn[b'update_record'] = self.update # update record support else: rtn = None diff --git a/gluon/globals.py b/gluon/globals.py index edf0e504..c54d4635 100644 --- a/gluon/globals.py +++ b/gluon/globals.py @@ -14,7 +14,7 @@ Contains the classes for the global used variables: """ from gluon._compat import pickle, StringIO, copyreg, Cookie, urlparse, PY2, iteritems, to_unicode, to_native, \ - unicodeT, long, hashlib_md5, urllib_quote + to_bytes, unicodeT, long, hashlib_md5, urllib_quote from gluon.storage import Storage, List from gluon.streamer import streamer, stream_file_or_304_or_206, DEFAULT_CHUNK_SIZE from gluon.contenttype import contenttype @@ -225,7 +225,7 @@ class Request(Storage): # instead of load. # This line can be simplified to json_vars = json_parser.load(body) # if and when we drop support for python versions under 3.6 - json_vars = json_parser.loads(to_native(body.read())) + json_vars = json_parser.loads(to_native(body.read())) except: # incoherent request bodies can still be parsed "ad-hoc" json_vars = {} @@ -344,8 +344,8 @@ class Request(Storage): user_agent = Storage(user_agent) user_agent.is_mobile = 'Mobile' in http_user_agent user_agent.is_tablet = 'Tablet' in http_user_agent - session._user_agent = user_agent - + session._user_agent = user_agent + return user_agent def requires_https(self): @@ -485,12 +485,12 @@ class Response(Storage): # # We will only minify and concat adjacent internal files as there's # no way to know if changing the order with which the files are apppended - # will break things since the order matters in both CSS and JS and + # will break things since the order matters in both CSS and JS and # internal files may be interleaved with external ones. files = [] # For the adjacent list we're going to use storage List to both distinguish # from the regular list and so we can add attributes - internal = List() + internal = List() internal.has_js = False internal.has_css = False done = set() # to remove duplicates @@ -513,7 +513,7 @@ class Response(Storage): if item.endswith('.js'): internal.has_js = True if item.endswith('.css'): - internal.has_css = True + internal.has_css = True if internal: files.append(internal) @@ -537,7 +537,7 @@ class Response(Storage): time_expire) else: files[i] = call_minify() - + def static_map(s, item): if isinstance(item, str): f = item.lower().split('?')[0] @@ -1049,9 +1049,7 @@ class Session(Storage): if record_id.isdigit() and long(record_id) > 0: new_unique_key = web2py_uuid() row = table(record_id) - if not isinstance(unique_key, bytes): - unique_key = bytes(unique_key, 'utf8') - if row and row[b'unique_key'] == unique_key: + if row and row[b'unique_key'] == to_bytes(unique_key): table._db(table.id == record_id).update(unique_key=new_unique_key) else: record_id = None @@ -1169,7 +1167,7 @@ class Session(Storage): compression_level=compression_level) rcookies = response.cookies rcookies.pop(name, None) - rcookies[name] = value.decode('utf8') + rcookies[name] = to_native(value) rcookies[name]['path'] = '/' expires = response.session_cookie_expires if isinstance(expires, datetime.datetime): diff --git a/gluon/utils.py b/gluon/utils.py index 3b93ea18..ea738047 100644 --- a/gluon/utils.py +++ b/gluon/utils.py @@ -207,8 +207,7 @@ def secure_dumps(data, encryption_key, hash_key=None, compression_level=None): def secure_loads(data, encryption_key, hash_key=None, compression_level=None): - if not isinstance(data, bytes): - data = bytes(data, 'utf8') + data = to_bytes(data) components = data.count(b':') if components == 1: return secure_loads_deprecated(data, encryption_key, hash_key, compression_level)