Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/jsonpickle/util.py: 38%
168 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:20 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:20 +0000
1# Copyright (C) 2008 John Paulett (john -at- paulett.org)
2# Copyright (C) 2009-2018 David Aguilar (davvid -at- gmail.com)
3# All rights reserved.
4#
5# This software is licensed as described in the file COPYING, which
6# you should have received as part of this distribution.
8"""Helper functions for pickling and unpickling. Most functions assist in
9determining the type of an object.
10"""
11from __future__ import absolute_import, division, unicode_literals
13import base64
14import collections
15import inspect
16import io
17import operator
18import sys
19import time
20import types
22from . import compat, tags
23from .compat import abc_iterator, class_types, iterator_types, numeric_types
25SEQUENCES = (list, set, tuple)
26SEQUENCES_SET = {list, set, tuple}
27PRIMITIVES = {compat.ustr, bool, type(None)} | set(numeric_types)
28FUNCTION_TYPES = {
29 types.FunctionType,
30 types.MethodType,
31 types.LambdaType,
32 types.BuiltinFunctionType,
33 types.BuiltinMethodType,
34}
35NON_REDUCIBLE_TYPES = (
36 {
37 list,
38 dict,
39 set,
40 tuple,
41 object,
42 bytes,
43 }
44 | PRIMITIVES
45 | FUNCTION_TYPES
46)
47NON_CLASS_TYPES = {
48 list,
49 dict,
50 set,
51 tuple,
52 bytes,
53} | PRIMITIVES
56def is_type(obj):
57 """Returns True is obj is a reference to a type.
59 >>> is_type(1)
60 False
62 >>> is_type(object)
63 True
65 >>> class Klass: pass
66 >>> is_type(Klass)
67 True
68 """
69 # use "isinstance" and not "is" to allow for metaclasses
70 return isinstance(obj, class_types)
73def has_method(obj, name):
74 # false if attribute doesn't exist
75 if not hasattr(obj, name):
76 return False
77 func = getattr(obj, name)
79 # builtin descriptors like __getnewargs__
80 if isinstance(func, types.BuiltinMethodType):
81 return True
83 # note that FunctionType has a different meaning in py2/py3
84 if not isinstance(func, (types.MethodType, types.FunctionType)):
85 return False
87 # need to go through __dict__'s since in py3
88 # methods are essentially descriptors
90 # __class__ for old-style classes
91 base_type = obj if is_type(obj) else obj.__class__
92 original = None
93 # there is no .mro() for old-style classes
94 for subtype in inspect.getmro(base_type):
95 original = vars(subtype).get(name)
96 if original is not None:
97 break
99 # name not found in the mro
100 if original is None:
101 return False
103 # static methods are always fine
104 if isinstance(original, staticmethod):
105 return True
107 # at this point, the method has to be an instancemthod or a classmethod
108 if not hasattr(func, '__self__'):
109 return False
110 bound_to = getattr(func, '__self__')
112 # class methods
113 if isinstance(original, classmethod):
114 return issubclass(base_type, bound_to)
116 # bound methods
117 return isinstance(obj, type(bound_to))
120def is_object(obj):
121 """Returns True is obj is a reference to an object instance.
123 >>> is_object(1)
124 True
126 >>> is_object(object())
127 True
129 >>> is_object(lambda x: 1)
130 False
131 """
132 return isinstance(obj, object) and not isinstance(
133 obj, (type, types.FunctionType, types.BuiltinFunctionType)
134 )
137def is_not_class(obj):
138 """Determines if the object is not a class or a class instance.
139 Used for serializing properties.
140 """
141 return type(obj) in NON_CLASS_TYPES
144def is_primitive(obj):
145 """Helper method to see if the object is a basic data type. Unicode strings,
146 integers, longs, floats, booleans, and None are considered primitive
147 and will return True when passed into *is_primitive()*
149 >>> is_primitive(3)
150 True
151 >>> is_primitive([4,4])
152 False
153 """
154 return type(obj) in PRIMITIVES
157def is_enum(obj):
158 """Is the object an enum?"""
159 return 'enum' in sys.modules and isinstance(obj, sys.modules['enum'].Enum)
162def is_dictionary(obj):
163 """Helper method for testing if the object is a dictionary.
165 >>> is_dictionary({'key':'value'})
166 True
168 """
169 return type(obj) is dict
172def is_sequence(obj):
173 """Helper method to see if the object is a sequence (list, set, or tuple).
175 >>> is_sequence([4])
176 True
178 """
179 return type(obj) in SEQUENCES_SET
182def is_list(obj):
183 """Helper method to see if the object is a Python list.
185 >>> is_list([4])
186 True
187 """
188 return type(obj) is list
191def is_set(obj):
192 """Helper method to see if the object is a Python set.
194 >>> is_set(set())
195 True
196 """
197 return type(obj) is set
200def is_bytes(obj):
201 """Helper method to see if the object is a bytestring.
203 >>> is_bytes(b'foo')
204 True
205 """
206 return type(obj) is bytes
209def is_unicode(obj):
210 """Helper method to see if the object is a unicode string"""
211 return type(obj) is compat.ustr
214def is_tuple(obj):
215 """Helper method to see if the object is a Python tuple.
217 >>> is_tuple((1,))
218 True
219 """
220 return type(obj) is tuple
223def is_dictionary_subclass(obj):
224 """Returns True if *obj* is a subclass of the dict type. *obj* must be
225 a subclass and not the actual builtin dict.
227 >>> class Temp(dict): pass
228 >>> is_dictionary_subclass(Temp())
229 True
230 """
231 # TODO: add UserDict
232 return (
233 hasattr(obj, '__class__')
234 and issubclass(obj.__class__, dict)
235 and type(obj) is not dict
236 )
239def is_sequence_subclass(obj):
240 """Returns True if *obj* is a subclass of list, set or tuple.
242 *obj* must be a subclass and not the actual builtin, such
243 as list, set, tuple, etc..
245 >>> class Temp(list): pass
246 >>> is_sequence_subclass(Temp())
247 True
248 """
249 return (
250 hasattr(obj, '__class__')
251 and issubclass(obj.__class__, SEQUENCES)
252 and not is_sequence(obj)
253 )
256def is_noncomplex(obj):
257 """Returns True if *obj* is a special (weird) class, that is more complex
258 than primitive data types, but is not a full object. Including:
260 * :class:`~time.struct_time`
261 """
262 if type(obj) is time.struct_time:
263 return True
264 return False
267def is_function(obj):
268 """Returns true if passed a function
270 >>> is_function(lambda x: 1)
271 True
273 >>> is_function(locals)
274 True
276 >>> def method(): pass
277 >>> is_function(method)
278 True
280 >>> is_function(1)
281 False
282 """
283 return type(obj) in FUNCTION_TYPES
286def is_module_function(obj):
287 """Return True if `obj` is a module-global function
289 >>> import os
290 >>> is_module_function(os.path.exists)
291 True
293 >>> is_module_function(lambda: None)
294 False
296 """
298 return (
299 hasattr(obj, '__class__')
300 and isinstance(obj, (types.FunctionType, types.BuiltinFunctionType))
301 and hasattr(obj, '__module__')
302 and hasattr(obj, '__name__')
303 and obj.__name__ != '<lambda>'
304 )
307def is_module(obj):
308 """Returns True if passed a module
310 >>> import os
311 >>> is_module(os)
312 True
314 """
315 return isinstance(obj, types.ModuleType)
318def is_picklable(name, value):
319 """Return True if an object can be pickled
321 >>> import os
322 >>> is_picklable('os', os)
323 True
325 >>> def foo(): pass
326 >>> is_picklable('foo', foo)
327 True
329 >>> is_picklable('foo', lambda: None)
330 False
332 """
333 if name in tags.RESERVED:
334 return False
335 return is_module_function(value) or not is_function(value)
338def is_installed(module):
339 """Tests to see if ``module`` is available on the sys.path
341 >>> is_installed('sys')
342 True
343 >>> is_installed('hopefullythisisnotarealmodule')
344 False
346 """
347 try:
348 __import__(module)
349 return True
350 except ImportError:
351 return False
354def is_list_like(obj):
355 return hasattr(obj, '__getitem__') and hasattr(obj, 'append')
358def is_iterator(obj):
359 return isinstance(obj, abc_iterator) and not isinstance(obj, io.IOBase)
362def is_collections(obj):
363 try:
364 return type(obj).__module__ == 'collections'
365 except Exception:
366 return False
369def is_reducible_sequence_subclass(obj):
370 return hasattr(obj, '__class__') and issubclass(obj.__class__, SEQUENCES)
373def is_reducible(obj):
374 """
375 Returns false if of a type which have special casing,
376 and should not have their __reduce__ methods used
377 """
378 # defaultdicts may contain functions which we cannot serialise
379 if is_collections(obj) and not isinstance(obj, collections.defaultdict):
380 return True
381 # We turn off the formatting in order to double the speed of the function.
382 # Condensing it into one line seems to save the parser a lot of time.
383 # fmt: off
384 # pylint: disable=line-too-long
385 if type(obj) in NON_REDUCIBLE_TYPES or obj is object or is_dictionary_subclass(obj) or isinstance(obj, types.ModuleType) or is_reducible_sequence_subclass(obj) or is_list_like(obj) or isinstance(getattr(obj, '__slots__', None), iterator_types) or (is_type(obj) and obj.__module__ == 'datetime'): # noqa: E501
386 return False
387 # fmt: on
388 return True
391def in_dict(obj, key, default=False):
392 """
393 Returns true if key exists in obj.__dict__; false if not in.
394 If obj.__dict__ is absent, return default
395 """
396 return (key in obj.__dict__) if getattr(obj, '__dict__', None) else default
399def in_slots(obj, key, default=False):
400 """
401 Returns true if key exists in obj.__slots__; false if not in.
402 If obj.__slots__ is absent, return default
403 """
404 return (key in obj.__slots__) if getattr(obj, '__slots__', None) else default
407def has_reduce(obj):
408 """
409 Tests if __reduce__ or __reduce_ex__ exists in the object dict or
410 in the class dicts of every class in the MRO *except object*.
412 Returns a tuple of booleans (has_reduce, has_reduce_ex)
413 """
415 if not is_reducible(obj) or is_type(obj):
416 return (False, False)
418 # in this case, reduce works and is desired
419 # notwithstanding depending on default object
420 # reduce
421 if is_noncomplex(obj):
422 return (False, True)
424 has_reduce = False
425 has_reduce_ex = False
427 REDUCE = '__reduce__'
428 REDUCE_EX = '__reduce_ex__'
430 # For object instance
431 has_reduce = in_dict(obj, REDUCE) or in_slots(obj, REDUCE)
432 has_reduce_ex = in_dict(obj, REDUCE_EX) or in_slots(obj, REDUCE_EX)
434 # turn to the MRO
435 for base in type(obj).__mro__:
436 if is_reducible(base):
437 has_reduce = has_reduce or in_dict(base, REDUCE)
438 has_reduce_ex = has_reduce_ex or in_dict(base, REDUCE_EX)
439 if has_reduce and has_reduce_ex:
440 return (has_reduce, has_reduce_ex)
442 # for things that don't have a proper dict but can be
443 # getattred (rare, but includes some builtins)
444 cls = type(obj)
445 object_reduce = getattr(object, REDUCE)
446 object_reduce_ex = getattr(object, REDUCE_EX)
447 if not has_reduce:
448 has_reduce_cls = getattr(cls, REDUCE, False)
449 if has_reduce_cls is not object_reduce:
450 has_reduce = has_reduce_cls
452 if not has_reduce_ex:
453 has_reduce_ex_cls = getattr(cls, REDUCE_EX, False)
454 if has_reduce_ex_cls is not object_reduce_ex:
455 has_reduce_ex = has_reduce_ex_cls
457 return (has_reduce, has_reduce_ex)
460def translate_module_name(module):
461 """Rename builtin modules to a consistent module name.
463 Prefer the more modern naming.
465 This is used so that references to Python's `builtins` module can
466 be loaded in both Python 2 and 3. We remap to the "__builtin__"
467 name and unmap it when importing.
469 Map the Python2 `exceptions` module to `builtins` because
470 `builtins` is a superset and contains everything that is
471 available in `exceptions`, which makes the translation simpler.
473 See untranslate_module_name() for the reverse operation.
474 """
475 lookup = dict(__builtin__='builtins', exceptions='builtins')
476 return lookup.get(module, module)
479def _0_9_6_compat_untranslate(module):
480 """Provide compatibility for pickles created with jsonpickle 0.9.6 and
481 earlier, remapping `exceptions` and `__builtin__` to `builtins`.
482 """
483 lookup = dict(__builtin__='builtins', exceptions='builtins')
484 return lookup.get(module, module)
487def untranslate_module_name(module):
488 """Rename module names mention in JSON to names that we can import
490 This reverses the translation applied by translate_module_name() to
491 a module name available to the current version of Python.
493 """
494 return _0_9_6_compat_untranslate(module)
497def importable_name(cls):
498 """
499 >>> class Example(object):
500 ... pass
502 >>> ex = Example()
503 >>> importable_name(ex.__class__) == 'jsonpickle.util.Example'
504 True
505 >>> importable_name(type(25)) == 'builtins.int'
506 True
507 >>> importable_name(None.__class__) == 'builtins.NoneType'
508 True
509 >>> importable_name(False.__class__) == 'builtins.bool'
510 True
511 >>> importable_name(AttributeError) == 'builtins.AttributeError'
512 True
514 """
515 # Use the fully-qualified name if available (Python >= 3.3)
516 name = getattr(cls, '__qualname__', cls.__name__)
517 module = translate_module_name(cls.__module__)
518 if not module:
519 if hasattr(cls, '__self__'):
520 module = cls.__self__.__class__.__module__
521 return '{}.{}'.format(module, name)
524def b64encode(data):
525 """
526 Encode binary data to ascii text in base64. Data must be bytes.
527 """
528 return base64.b64encode(data).decode('ascii')
531def b64decode(payload):
532 """
533 Decode payload - must be ascii text.
534 """
535 return base64.b64decode(payload)
538def b85encode(data):
539 """
540 Encode binary data to ascii text in base85. Data must be bytes.
541 """
542 return base64.b85encode(data).decode('ascii')
545def b85decode(payload):
546 """
547 Decode payload - must be ascii text.
548 """
549 return base64.b85decode(payload)
552def itemgetter(obj, getter=operator.itemgetter(0)):
553 return compat.ustr(getter(obj))
556def items(obj):
557 """
558 TODO: Replace all calls to this with plain dict.items()
559 """
560 for k, v in obj.items():
561 yield k, v