summaryrefslogtreecommitdiffstats
path: root/nova/utils.py
diff options
context:
space:
mode:
authorSandy Walsh <sandy.walsh@rackspace.com>2011-08-02 11:23:11 +0000
committerTarmac <>2011-08-02 11:23:11 +0000
commitf05628dff7aebd15e3f3530295ece3372bf2dbec (patch)
tree45877b5b999db41f2012cb3d336fe57001fe0e58 /nova/utils.py
parentefdd1bb019ac431d7d7a1923ff8580de1bb34217 (diff)
parent51f4d4c2e0c7d9f066b328014aa955b150b62c3a (diff)
downloadnova-f05628dff7aebd15e3f3530295ece3372bf2dbec.tar.gz
nova-f05628dff7aebd15e3f3530295ece3372bf2dbec.tar.xz
nova-f05628dff7aebd15e3f3530295ece3372bf2dbec.zip
While we currently trap JSON encoding exceptions and bail out, for error notification it's more important that *some* form of the message gets out. So, we take complex notification payloads and convert them to something we know can be expressed in JSON.
Diffstat (limited to 'nova/utils.py')
-rw-r--r--nova/utils.py74
1 files changed, 55 insertions, 19 deletions
diff --git a/nova/utils.py b/nova/utils.py
index 737903f81..4ea623cc1 100644
--- a/nova/utils.py
+++ b/nova/utils.py
@@ -513,25 +513,61 @@ def utf8(value):
return value
-def to_primitive(value):
- if type(value) is type([]) or type(value) is type((None,)):
- o = []
- for v in value:
- o.append(to_primitive(v))
- return o
- elif type(value) is type({}):
- o = {}
- for k, v in value.iteritems():
- o[k] = to_primitive(v)
- return o
- elif isinstance(value, datetime.datetime):
- return str(value)
- elif hasattr(value, 'iteritems'):
- return to_primitive(dict(value.iteritems()))
- elif hasattr(value, '__iter__'):
- return to_primitive(list(value))
- else:
- return value
+def to_primitive(value, convert_instances=False, level=0):
+ """Convert a complex object into primitives.
+
+ Handy for JSON serialization. We can optionally handle instances,
+ but since this is a recursive function, we could have cyclical
+ data structures.
+
+ To handle cyclical data structures we could track the actual objects
+ visited in a set, but not all objects are hashable. Instead we just
+ track the depth of the object inspections and don't go too deep.
+
+ Therefore, convert_instances=True is lossy ... be aware.
+
+ """
+ if inspect.isclass(value):
+ return unicode(value)
+
+ if level > 3:
+ return []
+
+ # The try block may not be necessary after the class check above,
+ # but just in case ...
+ try:
+ if type(value) is type([]) or type(value) is type((None,)):
+ o = []
+ for v in value:
+ o.append(to_primitive(v, convert_instances=convert_instances,
+ level=level))
+ return o
+ elif type(value) is type({}):
+ o = {}
+ for k, v in value.iteritems():
+ o[k] = to_primitive(v, convert_instances=convert_instances,
+ level=level)
+ return o
+ elif isinstance(value, datetime.datetime):
+ return str(value)
+ elif hasattr(value, 'iteritems'):
+ return to_primitive(dict(value.iteritems()),
+ convert_instances=convert_instances,
+ level=level)
+ elif hasattr(value, '__iter__'):
+ return to_primitive(list(value), level)
+ elif convert_instances and hasattr(value, '__dict__'):
+ # Likely an instance of something. Watch for cycles.
+ # Ignore class member vars.
+ return to_primitive(value.__dict__,
+ convert_instances=convert_instances,
+ level=level + 1)
+ else:
+ return value
+ except TypeError, e:
+ # Class objects are tricky since they may define something like
+ # __iter__ defined but it isn't callable as list().
+ return unicode(value)
def dumps(value):