From 265d63565464b6b5b94a6932be70d40f5805bcc8 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Mon, 21 Nov 2016 16:38:35 +0100 Subject: [PATCH] feat: add JSON encoding of unexpected types This allows all the things to be JSON encoded. Potentially allowing to store all of kind of things in the metadata. The fallback conversion is a string representation. fix as suggested in PR fix try-except style add basic test for JSON encoder autopep --- qcodes/tests/test_helpers.py | 44 ++++++++++++++++++++++++++++++++---- qcodes/utils/helpers.py | 8 ++++++- qcodes/utils/validators.py | 2 +- 3 files changed, 48 insertions(+), 6 deletions(-) diff --git a/qcodes/tests/test_helpers.py b/qcodes/tests/test_helpers.py index 6b056153b3a7..c4d5cad333b1 100644 --- a/qcodes/tests/test_helpers.py +++ b/qcodes/tests/test_helpers.py @@ -1,15 +1,19 @@ -from unittest import TestCase -import time -from datetime import datetime import asyncio import json +import time + +from collections import OrderedDict +from datetime import datetime +from unittest import TestCase + + import numpy as np from qcodes.utils.helpers import (is_sequence, permissive_range, wait_secs, make_unique, DelegateAttributes, LogCapture, strip_attrs, full_class, named_repr, make_sweep, is_sequence_of, - compare_dictionaries) + compare_dictionaries, NumpyJSONEncoder) from qcodes.utils.deferred_operations import is_function @@ -550,6 +554,38 @@ def test_depth(self): with self.subTest(args=args): self.assertFalse(is_sequence_of(*args)) +# tests related to JSON encoding +class TestJSONencoder(TestCase): + + def testNumpyJSONEncoder(self): + e = NumpyJSONEncoder() + + # test basic python types + od = OrderedDict() + od['a'] = 0 + od['b'] = 1 + testinput=[10, float(10.), 'hello', od] + testoutput=['10', '10.0', '"hello"', '{"a": 0, "b": 1}'] + # int + for d, r in zip(testinput, testoutput): + v=e.encode(d) + if type(d) == dict: + self.assertDictEqual(v, r) + else: + self.assertEqual(v, r) + + + # test numpy array + x=np.array([1,0,0]) + v=e.encode(x) + self.assertEqual(v, '[1, 0, 0]') + + # test class + class dummy(object): + pass + # test that does not raise, do not care about + # return value + e.encode(dummy()) class TestCompareDictionaries(TestCase): def test_same(self): diff --git a/qcodes/utils/helpers.py b/qcodes/utils/helpers.py index 3afea8cf22bc..37f8c3588d2b 100644 --- a/qcodes/utils/helpers.py +++ b/qcodes/utils/helpers.py @@ -18,6 +18,7 @@ class NumpyJSONEncoder(json.JSONEncoder): """Return numpy types as standard types.""" # http://stackoverflow.com/questions/27050108/convert-numpy-type-to-python # http://stackoverflow.com/questions/9452775/converting-numpy-dtypes-to-native-python-types/11389998#11389998 + def default(self, obj): if isinstance(obj, np.integer): return int(obj) @@ -33,7 +34,12 @@ def default(self, obj): 'im': float(obj.imag) } else: - return super(NumpyJSONEncoder, self).default(obj) + try: + s = super(NumpyJSONEncoder, self).default(obj) + except TypeError: + # we cannot convert the object to JSON, just take a string + s = str(obj) + return s def tprint(string, dt=1, tag='default'): diff --git a/qcodes/utils/validators.py b/qcodes/utils/validators.py index 80405a2c06a5..560ec694833f 100644 --- a/qcodes/utils/validators.py +++ b/qcodes/utils/validators.py @@ -364,7 +364,7 @@ def validate(self, value, context=''): if (np.shape(value) != self._shape): raise ValueError( '{} does not have expected shape {}; {}'.format( - repr(value), self._shape, context)) + repr(value), self._shape, context)) # Only check if max is not inf as it can be expensive for large arrays if self._max_value != (float("inf")):