1 2# 3# spyne - Copyright (C) Spyne contributors. 4# 5# This library is free software; you can redistribute it and/or 6# modify it under the terms of the GNU Lesser General Public 7# License as published by the Free Software Foundation; either 8# version 2.1 of the License, or (at your option) any later version. 9# 10# This library is distributed in the hope that it will be useful, 11# but WITHOUT ANY WARRANTY; without even the implied warranty of 12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13# Lesser General Public License for more details. 14# 15# You should have received a copy of the GNU Lesser General Public 16# License along with this library; if not, write to the Free Software 17# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 18# 19 20""" 21Some code dump from some time ago. 22 23If you're using this for anything serious, you're insane. 24""" 25 26from __future__ import absolute_import 27 28import logging 29logger = logging.getLogger(__name__) 30 31from inspect import isclass 32 33from spyne import rpc, Any, AnyDict, NATIVE_MAP, M, Array, ComplexModelBase, \ 34 UnsignedInteger32, PushBase, Iterable, ModelBase, File, Service, \ 35 ResourceNotFoundError, Unicode 36 37from spyne.const import MAX_ARRAY_ELEMENT_NUM, MAX_DICT_ELEMENT_NUM, \ 38 MAX_STRING_FIELD_LENGTH, MAX_FIELD_NUM 39 40try: 41 from spyne.store.relational.document import FileData 42 from sqlalchemy.orm.exc import DetachedInstanceError 43except ImportError: 44 # these are used just for isinstance checks. so we just set it to an 45 # anonymous value 46 FileData = type('__hidden', (object, ), {}) 47 DetachedInstanceError = type('__hidden', (Exception, ), {}) 48 49from spyne.util import memoize, six 50 51EXCEPTION_ADDRESS = None 52 53 54try: 55 from colorama.ansi import Fore 56 from colorama.ansi import Style 57 RED = Fore.RED + Style.BRIGHT 58 GREEN = Fore.GREEN + Style.BRIGHT 59 RESET = Style.RESET_ALL 60 61except ImportError: 62 RED = "" 63 GREEN = "" 64 RESET = "" 65 66 67class ReaderService(Service): 68 pass 69 70 71class WriterService(Service): 72 pass 73 74 75def log_repr(obj, cls=None, given_len=None, parent=None, from_array=False, 76 tags=None, prot=None): 77 """Use this function if you want to serialize a ComplexModelBase instance to 78 logs. It will: 79 80 * Limit size of the String types 81 * Limit size of Array types 82 * Not try to iterate on iterators, push data, etc. 83 """ 84 85 if tags is None: 86 tags = set() 87 88 if obj is None: 89 return 'None' 90 91 objcls = None 92 if hasattr(obj, '__class__'): 93 objcls = obj.__class__ 94 95 if objcls in (list, tuple): 96 objcls = Array(Any) 97 98 elif objcls is dict: 99 objcls = AnyDict 100 101 elif objcls in NATIVE_MAP: 102 objcls = NATIVE_MAP[objcls] 103 104 if objcls is not None and (cls is None or issubclass(objcls, cls)): 105 cls = objcls 106 107 cls_attrs = None 108 logged = None 109 110 if hasattr(cls, 'Attributes'): 111 if prot is None: 112 cls_attrs = cls.Attributes 113 else: 114 cls_attrs = prot.get_cls_attrs(cls) 115 116 logged = cls_attrs.logged 117 if not logged: 118 return "%s(...)" % cls.get_type_name() 119 120 if logged == '...': 121 return "(...)" 122 123 if issubclass(cls, File) and isinstance(obj, File.Value): 124 cls = obj.__class__ 125 126 if cls_attrs.logged == 'len': 127 l = '?' 128 try: 129 if isinstance(obj, (list, tuple)): 130 l = str(sum([len(o) for o in obj])) 131 132 else: 133 l = str(len(obj)) 134 except (TypeError, ValueError): 135 if given_len is not None: 136 l = str(given_len) 137 138 return "<len=%s>" % l 139 140 if callable(cls_attrs.logged): 141 try: 142 return cls_attrs.logged(obj) 143 except Exception as e: 144 logger.error("Exception %r in log_repr transformer ignored", e) 145 logger.exception(e) 146 pass 147 148 if issubclass(cls, AnyDict): 149 retval = [] 150 151 if isinstance(obj, dict): 152 if logged == 'full': 153 for i, (k, v) in enumerate(obj.items()): 154 retval.append('%r: %r' % (k, v)) 155 156 elif logged == 'keys': 157 for i, k in enumerate(obj.keys()): 158 if i >= MAX_DICT_ELEMENT_NUM: 159 retval.append("(...)") 160 break 161 162 retval.append('%r: (...)' % (k,)) 163 164 elif logged == 'values': 165 for i, v in enumerate(obj.values()): 166 if i >= MAX_DICT_ELEMENT_NUM: 167 retval.append("(...)") 168 break 169 170 retval.append('(...): %s' % (log_repr(v, tags=tags),)) 171 172 elif logged == 'keys-full': 173 for k in obj.keys(): 174 retval.append('%r: (...)' % (k,)) 175 176 elif logged == 'values-full': 177 for v in obj.values(): 178 retval.append('(...): %r' % (v,)) 179 180 elif logged is True: # default behaviour 181 for i, (k, v) in enumerate(obj.items()): 182 if i >= MAX_DICT_ELEMENT_NUM: 183 retval.append("(...)") 184 break 185 186 retval.append('%r: %s' % (k, 187 log_repr(v, parent=k, tags=tags))) 188 else: 189 raise ValueError("Invalid value logged=%r", logged) 190 191 return "{%s}" % ', '.join(retval) 192 193 else: 194 if logged in ('full', 'keys-full', 'values-full'): 195 retval = [repr(s) for s in obj] 196 197 else: 198 for i, v in enumerate(obj): 199 if i >= MAX_DICT_ELEMENT_NUM: 200 retval.append("(...)") 201 break 202 203 retval.append(log_repr(v, tags=tags)) 204 205 return "[%s]" % ', '.join(retval) 206 207 if (issubclass(cls, Array) or (cls_attrs.max_occurs > 1)) and not from_array: 208 if id(obj) in tags: 209 return "%s(...)" % obj.__class__.__name__ 210 211 tags.add(id(obj)) 212 213 retval = [] 214 215 subcls = cls 216 if issubclass(cls, Array): 217 subcls, = cls._type_info.values() 218 219 if isinstance(obj, PushBase): 220 return '[<PushData>]' 221 222 if logged is None: 223 logged = cls_attrs.logged 224 225 for i, o in enumerate(obj): 226 if logged != 'full' and i >= MAX_ARRAY_ELEMENT_NUM: 227 retval.append("(...)") 228 break 229 230 retval.append(log_repr(o, subcls, from_array=True, tags=tags)) 231 232 return "[%s]" % (', '.join(retval)) 233 234 if issubclass(cls, ComplexModelBase): 235 if id(obj) in tags: 236 return "%s(...)" % obj.__class__.__name__ 237 238 tags.add(id(obj)) 239 240 retval = [] 241 i = 0 242 243 for k, t in cls.get_flat_type_info(cls).items(): 244 if i >= MAX_FIELD_NUM: 245 retval.append("(...)") 246 break 247 248 if not t.Attributes.logged: 249 continue 250 251 if logged == '...': 252 retval.append("%s=(...)" % k) 253 continue 254 255 try: 256 v = getattr(obj, k, None) 257 except (AttributeError, KeyError, DetachedInstanceError): 258 v = None 259 260 # HACK!: sometimes non-db attributes restored from database don't 261 # get properly reinitialized. 262 if isclass(v) and issubclass(v, ModelBase): 263 continue 264 265 polymap = t.Attributes.polymap 266 if polymap is not None: 267 t = polymap.get(v.__class__, t) 268 269 if v is not None: 270 retval.append("%s=%s" % (k, log_repr(v, t, parent=k, tags=tags))) 271 i += 1 272 273 return "%s(%s)" % (cls.get_type_name(), ', '.join(retval)) 274 275 if issubclass(cls, Unicode) and isinstance(obj, six.string_types): 276 if len(obj) > MAX_STRING_FIELD_LENGTH: 277 return '%r(...)' % obj[:MAX_STRING_FIELD_LENGTH] 278 279 return repr(obj) 280 281 if issubclass(cls, File) and isinstance(obj, FileData): 282 return log_repr(obj, FileData, tags=tags) 283 284 retval = repr(obj) 285 286 if len(retval) > MAX_STRING_FIELD_LENGTH: 287 retval = retval[:MAX_STRING_FIELD_LENGTH] + "(...)" 288 289 return retval 290 291 292def TReaderService(T, T_name): 293 class ReaderService(ReaderService): 294 @rpc(M(UnsignedInteger32), _returns=T, 295 _in_message_name='get_%s' % T_name, 296 _in_variable_names={'obj_id': "%s_id" % T_name}) 297 def get(ctx, obj_id): 298 return ctx.udc.session.query(T).filter_by(id=obj_id).one() 299 300 @rpc(_returns=Iterable(T), 301 _in_message_name='get_all_%s' % T_name) 302 def get_all(ctx): 303 return ctx.udc.session.query(T).order_by(T.id) 304 305 return ReaderService 306 307 308def TWriterService(T, T_name, put_not_found='raise'): 309 assert put_not_found in ('raise', 'fix') 310 311 if put_not_found == 'raise': 312 def put_not_found(obj): 313 raise ResourceNotFoundError('%s.id=%d' % (T_name, obj.id)) 314 315 elif put_not_found == 'fix': 316 def put_not_found(obj): 317 obj.id = None 318 319 class WriterService(WriterService): 320 @rpc(M(T), _returns=UnsignedInteger32, 321 _in_message_name='put_%s' % T_name, 322 _in_variable_names={'obj': T_name}) 323 def put(ctx, obj): 324 if obj.id is None: 325 ctx.udc.session.add(obj) 326 ctx.udc.session.flush() # so that we get the obj.id value 327 328 else: 329 if ctx.udc.session.query(T).get(obj.id) is None: 330 # this is to prevent the client from setting the primary key 331 # of a new object instead of the database's own primary-key 332 # generator. 333 # Instead of raising an exception, you can also choose to 334 # ignore the primary key set by the client by silently doing 335 # obj.id = None in order to have the database assign the 336 # primary key the traditional way. 337 put_not_found(obj.id) 338 339 else: 340 ctx.udc.session.merge(obj) 341 342 return obj.id 343 344 @rpc(M(UnsignedInteger32), 345 _in_message_name='del_%s' % T_name, 346 _in_variable_names={'obj_id': '%s_id' % T_name}) 347 def del_(ctx, obj_id): 348 count = ctx.udc.session.query(T).filter_by(id=obj_id).count() 349 if count == 0: 350 raise ResourceNotFoundError(obj_id) 351 352 ctx.udc.session.query(T).filter_by(id=obj_id).delete() 353 354 return WriterService 355