##// END OF EJS Templates
cleanup: few fixes
cleanup: few fixes

File last commit:

r5088:8f6d1ed6 default
r5116:cc48fcd2 default
Show More
jsonalchemy.py
173 lines | 5.8 KiB | text/x-python | PythonLexer
dan
integrations: use mutable json settings using json alchemy MutableObject.
r447
copyrights: updated for 2023
r5088 # Copyright (C) 2010-2023 RhodeCode GmbH
dan
integrations: use mutable json settings using json alchemy MutableObject.
r447 #
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License, version 3
# (only), as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# This program is dual-licensed. If you wish to learn more about the
# RhodeCode Enterprise Edition, including its added features, Support services,
# and proprietary license terms, please see https://rhodecode.com/licenses/
import sqlalchemy
from sqlalchemy import UnicodeText
libs: major refactor for python3
r5085 from sqlalchemy.ext.mutable import Mutable, \
MutableList as MutationList, \
MutableDict as MutationDict
dan
integrations: use mutable json settings using json alchemy MutableObject.
r447
libs: major refactor for python3
r5085 from rhodecode.lib import ext_json
dan
integrations: use mutable json settings using json alchemy MutableObject.
r447
requirements: fixed pycrypto backport and sqlalchemy <2.0
r4918 class JsonRaw(str):
dan
integrations: use mutable json settings using json alchemy MutableObject.
r447 """
Allows interacting with a JSON types field using a raw string.
For example::
db_instance = JsonTable()
db_instance.enabled = True
db_instance.json_data = JsonRaw('{"a": 4}')
This will bypass serialization/checks, and allow storing
raw values
"""
pass
class JSONEncodedObj(sqlalchemy.types.TypeDecorator):
"""
Represents an immutable structure as a json-encoded string.
If default is, for example, a dict, then a NULL value in the
database will be exposed as an empty dict.
"""
impl = UnicodeText
safe = True
libs: major refactor for python3
r5085 enforce_str = True
dan
integrations: use mutable json settings using json alchemy MutableObject.
r447
def __init__(self, *args, **kwargs):
self.default = kwargs.pop('default', None)
self.safe = kwargs.pop('safe_json', self.safe)
libs: major refactor for python3
r5085 self.enforce_str = kwargs.pop('enforce_str', self.enforce_str)
dan
integrations: use mutable json settings using json alchemy MutableObject.
r447 self.dialect_map = kwargs.pop('dialect_map', {})
super(JSONEncodedObj, self).__init__(*args, **kwargs)
def load_dialect_impl(self, dialect):
if dialect.name in self.dialect_map:
return dialect.type_descriptor(self.dialect_map[dialect.name])
return dialect.type_descriptor(self.impl)
def process_bind_param(self, value, dialect):
if isinstance(value, JsonRaw):
value = value
elif value is not None:
libs: major refactor for python3
r5085 if self.enforce_str:
value = ext_json.str_json(value)
else:
value = ext_json.json.dumps(value)
dan
integrations: use mutable json settings using json alchemy MutableObject.
r447 return value
def process_result_value(self, value, dialect):
if self.default is not None and (not value or value == '""'):
return self.default()
if value is not None:
try:
libs: major refactor for python3
r5085 value = ext_json.json.loads(value)
except Exception:
jsonalchemy: fix a case when default is not set and parsinge exception happens....
r2396 if self.safe and self.default is not None:
dan
integrations: use mutable json settings using json alchemy MutableObject.
r447 return self.default()
else:
raise
return value
class MutationObj(Mutable):
libs: major refactor for python3
r5085
dan
integrations: use mutable json settings using json alchemy MutableObject.
r447 @classmethod
def coerce(cls, key, value):
if isinstance(value, dict) and not isinstance(value, MutationDict):
return MutationDict.coerce(key, value)
if isinstance(value, list) and not isinstance(value, MutationList):
return MutationList.coerce(key, value)
return value
jsonalchemy: add de_coerce to reverse serialization of list and dict elements from Mutation objects back to original state.
r2397 def de_coerce(self):
return self
dan
integrations: use mutable json settings using json alchemy MutableObject.
r447 @classmethod
def _listen_on_attribute(cls, attribute, coerce, parent_cls):
key = attribute.key
if parent_cls is not attribute.class_:
return
# rely on "propagate" here
parent_cls = attribute.class_
def load(state, *args):
val = state.dict.get(key, None)
if coerce:
val = cls.coerce(key, val)
state.dict[key] = val
if isinstance(val, cls):
val._parents[state.obj()] = key
def set(target, value, oldvalue, initiator):
if not isinstance(value, cls):
value = cls.coerce(key, value)
if isinstance(value, cls):
value._parents[target.obj()] = key
if isinstance(oldvalue, cls):
oldvalue._parents.pop(target.obj(), None)
return value
def pickle(state, state_dict):
val = state.dict.get(key, None)
if isinstance(val, cls):
if 'ext.mutable.values' not in state_dict:
state_dict['ext.mutable.values'] = []
state_dict['ext.mutable.values'].append(val)
def unpickle(state, state_dict):
if 'ext.mutable.values' in state_dict:
for val in state_dict['ext.mutable.values']:
val._parents[state.obj()] = key
sqlalchemy.event.listen(parent_cls, 'load', load, raw=True,
propagate=True)
sqlalchemy.event.listen(parent_cls, 'refresh', load, raw=True,
propagate=True)
sqlalchemy.event.listen(parent_cls, 'pickle', pickle, raw=True,
propagate=True)
sqlalchemy.event.listen(attribute, 'set', set, raw=True, retval=True,
propagate=True)
sqlalchemy.event.listen(parent_cls, 'unpickle', unpickle, raw=True,
propagate=True)
def JsonType(impl=None, **kwargs):
"""
Helper for using a mutation obj, it allows to use .with_variant easily.
example::
settings = Column('settings_json',
MutationObj.as_mutable(
JsonType(dialect_map=dict(mysql=UnicodeText(16384))))
"""
if impl == 'list':
return JSONEncodedObj(default=list, **kwargs)
elif impl == 'dict':
libs: major refactor for python3
r5085 return JSONEncodedObj(default=dict, **kwargs)
dan
integrations: use mutable json settings using json alchemy MutableObject.
r447 else:
return JSONEncodedObj(**kwargs)