Skip to content

Commit

Permalink
Merge pull request #1639 from naylor-b/serialize
Browse files Browse the repository at this point in the history
Fix for unserializable object failure when running n2
  • Loading branch information
swryan committed Aug 20, 2020
2 parents 5886b4b + 72343b3 commit 676e377
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 8 deletions.
4 changes: 0 additions & 4 deletions openmdao/core/tests/test_group.py
Expand Up @@ -26,10 +26,6 @@
except ImportError:
PETScVector = None

try:
from openmdao.vectors.petsc_vector import PETScVector
except ImportError:
PETScVector = None

arr_order_1x1 = np.array([1, 2, 3, 4])
arr_2x4 = np.array([[0, 1, 2, 3], [10, 11, 12, 13]])
Expand Down
28 changes: 28 additions & 0 deletions openmdao/core/tests/test_serialize.py
@@ -0,0 +1,28 @@
import unittest

import openmdao.api as om

class BadOptionComp(om.ExplicitComponent):

def initialize(self):
self.options.declare('bad', recordable=False)

def setup(self):
self.add_input('x')
self.add_output('y')


class SerializeTestCase(unittest.TestCase):
def test_serialize_n2(self):
p = om.Problem()

p.model.add_subsystem('foo', BadOptionComp(bad=object()))

p.setup()
p.final_setup()

om.n2(p, show_browser=False)


if __name__ == '__main__':
unittest.main()
4 changes: 2 additions & 2 deletions openmdao/recorders/sqlite_recorder.py
Expand Up @@ -19,7 +19,7 @@
from openmdao.utils.mpi import MPI
from openmdao.utils.record_util import dict_to_structured_array
from openmdao.utils.options_dictionary import OptionsDictionary
from openmdao.utils.general_utils import simple_warning, make_serializable
from openmdao.utils.general_utils import simple_warning, make_serializable, default_noraise
from openmdao.core.driver import Driver
from openmdao.core.system import System
from openmdao.core.problem import Problem
Expand Down Expand Up @@ -612,7 +612,7 @@ def record_viewer_data(self, model_viewer_data, key='Driver'):
The unique ID to use for this data in the table.
"""
if self.connection:
json_data = json.dumps(model_viewer_data, default=make_serializable)
json_data = json.dumps(model_viewer_data, default=default_noraise)

# Note: recorded to 'driver_metadata' table for legacy/compatibility reasons.
try:
Expand Down
32 changes: 32 additions & 0 deletions openmdao/utils/general_utils.py
Expand Up @@ -830,6 +830,8 @@ def make_serializable(o):
"""
Recursively convert numpy types to native types for JSON serialization.
This function should NOT be passed into json.dump or json.dumps as the 'default' arg.
Parameters
----------
o : object
Expand All @@ -852,6 +854,36 @@ def make_serializable(o):
return o


def default_noraise(o):
"""
Try to convert some extra types during JSON serialization.
This is intended to be passed to json.dump or json.dumps as the 'default' arg. It will
attempt to convert values if possible, but if no conversion works, will return
'unserializable object (<type>)' instead of raising a TypeError.
Parameters
----------
o : object
the object to be converted
Returns
-------
object
The converted object.
"""
if isinstance(o, _container_classes):
return [make_serializable(item) for item in o]
elif isinstance(o, np.ndarray):
return o.tolist()
elif isinstance(o, np.number):
return o.item()
elif hasattr(o, '__dict__'):
return o.__class__.__name__
else:
return f"unserializable object ({type(o).__name__})"


def make_set(str_data, name=None):
"""
Construct a set containing the specified character strings.
Expand Down
4 changes: 2 additions & 2 deletions openmdao/visualization/n2_viewer/n2_viewer.py
Expand Up @@ -24,7 +24,7 @@
from openmdao.recorders.case_reader import CaseReader
from openmdao.solvers.nonlinear.newton import NewtonSolver
from openmdao.utils.class_util import overrides_method
from openmdao.utils.general_utils import simple_warning, make_serializable
from openmdao.utils.general_utils import simple_warning, default_noraise
from openmdao.utils.mpi import MPI
from openmdao.visualization.html_utils import read_files, write_script, DiagramWriter
from openmdao.utils.general_utils import warn_deprecation
Expand Down Expand Up @@ -416,7 +416,7 @@ def n2(data_source, outfile='n2.html', show_browser=True, embeddable=False,
warn_deprecation("'use_declare_partial_info' is now the"
" default and the option is ignored.")

raw_data = json.dumps(model_data, default=make_serializable).encode('utf8')
raw_data = json.dumps(model_data, default=default_noraise).encode('utf8')
b64_data = str(base64.b64encode(zlib.compress(raw_data)).decode("ascii"))
model_data = 'var compressedModel = "%s";' % b64_data

Expand Down

0 comments on commit 676e377

Please sign in to comment.