diff --git a/hack/ci_assets/function/main.py b/hack/ci_assets/function/main.py index 9e4fb96..dceb1cf 100644 --- a/hack/ci_assets/function/main.py +++ b/hack/ci_assets/function/main.py @@ -20,6 +20,9 @@ def handler(context: nuclio_sdk.Context, event: nuclio_sdk.Event): + headers = { + ensure_str(header): ensure_str(value) for header, value in event.headers.items() + } context.logger.debug_with( "Received request", event=json.dumps( @@ -27,10 +30,7 @@ def handler(context: nuclio_sdk.Context, event: nuclio_sdk.Event): "id": event.id, "eventType": event.trigger.kind, "contentType": event.content_type, - "headers": { - ensure_str(header): ensure_str(value) - for header, value in event.headers.items() - }, + "headers": headers, "timestamp": event.timestamp.isoformat("T") + "Z", "path": event.path, "url": event.url, @@ -46,7 +46,7 @@ def handler(context: nuclio_sdk.Context, event: nuclio_sdk.Event): ), ) return context.Response( - headers=event.headers, + headers=headers, body={ "sdk_version": get_sdk_version(), }, diff --git a/nuclio_sdk/json_encoder.py b/nuclio_sdk/json_encoder.py index 38b8791..294f04b 100644 --- a/nuclio_sdk/json_encoder.py +++ b/nuclio_sdk/json_encoder.py @@ -13,10 +13,73 @@ # limitations under the License. import json +import json.encoder # JSON encoder that can encode custom stuff class Encoder(json.JSONEncoder): + def __init__(self, **kwargs): + super(Encoder, self).__init__(**kwargs) + self.nan_str = "NaN" + + def iterencode(self, o, _one_shot=False): + """ + Encode the given object and yield each string + representation as available. + + For example:: + + for chunk in JSONEncoder().iterencode(bigobject): + mysocket.write(chunk) + + """ + if self.check_circular: + markers = {} + else: + markers = None + if self.ensure_ascii: + _encoder = json.encoder.encode_basestring_ascii + else: + _encoder = json.encoder.encode_basestring + + def floatstr( + o, + allow_nan=self.allow_nan, + nan_str=self.nan_str, + _repr=float.__repr__, + _inf=json.encoder.INFINITY, + _neginf=-json.encoder.INFINITY, + ): + if o != o: + text = '"{0}"'.format(nan_str) + elif o == _inf: + text = '"Infinity"' + elif o == _neginf: + text = '"-Infinity"' + else: + return _repr(o) + + if not allow_nan: + raise ValueError( + "Out of range float values are not JSON compliant: " + repr(o) + ) + + return text + + _iterencode = json.encoder._make_iterencode( + markers, + self.default, + _encoder, + self.indent, + floatstr, + self.key_separator, + self.item_separator, + self.sort_keys, + self.skipkeys, + _one_shot, + ) + return _iterencode(o, 0) + def default(self, obj): # EAFP all the way. Leave the unused "exc" in for debugging ease diff --git a/nuclio_sdk/test/test_logger.py b/nuclio_sdk/test/test_logger.py index 8622efb..01a2c67 100644 --- a/nuclio_sdk/test/test_logger.py +++ b/nuclio_sdk/test/test_logger.py @@ -65,6 +65,30 @@ def test_log_with_date(self): self._io.getvalue(), ) + def test_log_nan(self): + self._logger.info_with(self._testMethodName, nan=float("NaN")) + self.assertIn(self._testMethodName, self._io.getvalue()) + self.assertIn( + '"with": {"nan": "NaN"}', + self._io.getvalue(), + ) + + def test_log_infinity(self): + self._logger.info_with(self._testMethodName, nan=float("Infinity")) + self.assertIn(self._testMethodName, self._io.getvalue()) + self.assertIn( + '"with": {"nan": "Infinity"}', + self._io.getvalue(), + ) + + def test_log_minus_infinity(self): + self._logger.info_with(self._testMethodName, nan=float("-Infinity")) + self.assertIn(self._testMethodName, self._io.getvalue()) + self.assertIn( + '"with": {"nan": "-Infinity"}', + self._io.getvalue(), + ) + def test_fail_to_log(self): """ Do not fail logging when an object is not log-able