From b849a2a1197265a43a4960f49d302f4eccaa3470 Mon Sep 17 00:00:00 2001 From: "Gabriele N. Tornetta" Date: Fri, 18 Oct 2024 13:51:47 +0100 Subject: [PATCH] chore(di): better support for builtin collections (#10967) We improve the support for the serialisation of some builtin collection types when rendering template messages. ## Checklist - [x] PR author has checked that all the criteria below are met - The PR description includes an overview of the change - The PR description articulates the motivation for the change - The change includes tests OR the PR description describes a testing strategy - The PR description notes risks associated with the change, if any - Newly-added code is easy to change - The change follows the [library release note guidelines](https://ddtrace.readthedocs.io/en/stable/releasenotes.html) - The change includes or references documentation updates if necessary - Backport labels are set (if [applicable](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting)) ## Reviewer Checklist - [ ] Reviewer has checked that all the criteria below are met - Title is accurate - All changes are related to the pull request's stated goal - Avoids breaking [API](https://ddtrace.readthedocs.io/en/stable/versioning.html#interfaces) changes - Testing strategy adequately addresses listed risks - Newly-added code is easy to change - Release note makes sense to a user of the library - If necessary, author has acknowledged and discussed the performance implications of this PR as reported in the benchmarks PR comment - Backport labels are set in a manner that is consistent with the [release branch maintenance policy](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting) --- ddtrace/debugging/_signal/utils.py | 31 ++++++++++++++---------------- tests/debugging/test_encoding.py | 12 ++++++++++++ 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/ddtrace/debugging/_signal/utils.py b/ddtrace/debugging/_signal/utils.py index ea24d1efc8f..b2e5d8e285b 100644 --- a/ddtrace/debugging/_signal/utils.py +++ b/ddtrace/debugging/_signal/utils.py @@ -1,3 +1,4 @@ +from collections import deque from itertools import islice from itertools import takewhile from types import FrameType @@ -73,19 +74,7 @@ def serialize( if not level: return repr(type(value)) - if type(value) not in BUILTIN_CONTAINER_TYPES: - return "%s(%s)" % ( - type(value).__name__, - ", ".join( - ( - "=".join((k, serialize(v, level - 1, maxsize, maxlen, maxfields))) - for k, v in islice(get_fields(value).items(), maxfields) - if not redact(k) - ) - ), - ) - - if type(value) is dict: + if type(value) in BUILTIN_MAPPING_TYPES: return "{%s}" % ", ".join( ( ": ".join( @@ -97,15 +86,23 @@ def serialize( for k, v in islice(value.items(), maxsize) ) ) - elif type(value) is list: + elif type(value) in {list, deque}: return _serialize_collection(value, "[]", level, maxsize, maxlen, maxfields) elif type(value) is tuple: return _serialize_collection(value, "()", level, maxsize, maxlen, maxfields) - elif type(value) is set: + elif type(value) in {set, frozenset}: return _serialize_collection(value, r"{}", level, maxsize, maxlen, maxfields) if value else "set()" - msg = f"Unhandled type: {type(value)}" - raise TypeError(msg) + return "%s(%s)" % ( + type(value).__name__, + ", ".join( + ( + "=".join((k, serialize(v, level - 1, maxsize, maxlen, maxfields))) + for k, v in islice(get_fields(value).items(), maxfields) + if not redact(k) + ) + ), + ) def capture_stack(top_frame: FrameType, max_height: int = 4096) -> List[dict]: diff --git a/tests/debugging/test_encoding.py b/tests/debugging/test_encoding.py index 258aecb582a..81239c639da 100644 --- a/tests/debugging/test_encoding.py +++ b/tests/debugging/test_encoding.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- +from collections import defaultdict import inspect import json import sys @@ -663,3 +664,14 @@ def test_capture_value_sequence_type(_type): ], "size": 1, } + + +@pytest.mark.parametrize( + "value,expected", + [ + (defaultdict(int, {"bar": 42}), "{'bar': 42}"), + (frozenset({"foo"}), "{'foo'}"), + ], +) +def test_serialize_builtins(value, expected): + assert utils.serialize(value) == expected