xref: /aosp_15_r20/external/autotest/utils/frozen_chromite/lib/structured.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
1# -*- coding: utf-8 -*-
2# Copyright 2017 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""A convenience class for objects which can be converted to JSON."""
7
8from __future__ import print_function
9
10_BUILTINS = (str, int, float, bool, type(None))
11
12
13class Structured(object):
14  """An object with a approved set of public properties (.VISIBLE_KEYS)"""
15
16  def _Keys(self):
17    seen = set()
18    for cls in type(self).mro():
19      for k in getattr(cls, 'VISIBLE_KEYS', ()):
20        if k not in seen:
21          yield k
22          seen.add(k)
23
24  def ToDict(self):
25    return ToStructure(self)
26
27
28def ToStructure(value):
29  """Makes an object JSON-encodable.
30
31  Args:
32    value: An object which can be converted to a JSON-encodable object.
33
34  Returns:
35    An object which is legal to pass into json.dumps(obj), namely
36    type JSONEncodable = (
37        str | int | float | NoneType | dict<str, JSONEncodable> |
38        list<JSONEncodable>
39
40  Raises:
41    StackOverflow if the object has circular references (parent -> child ->
42    parent).
43  """
44  if isinstance(value, _BUILTINS):
45    return value
46
47  elif hasattr(value, '_Keys'):
48    ret = {}
49    for k in value._Keys():  # pylint: disable=protected-access
50      v = ToStructure(getattr(value, k, None))
51      if v is not None:
52        ret[k] = v
53    return ret
54
55  elif isinstance(value, dict):
56    ret = {}
57    for k, v in value.items():
58      v = ToStructure(v)
59      if v is not None:
60        ret[k] = v
61    return ret
62
63  else:
64    try:
65      iterator = iter(value)
66    except TypeError:
67      return None
68
69    ret = []
70    for element in iterator:
71      v = ToStructure(element)
72      if v is not None:
73        ret.append(v)
74
75    return ret