Skip to content

Commit ac4639c

Browse files
authored
chore(debugging): relax safe dict retrieval (#6024)
The existing safety check around the retrieval of `__dict__` are too strict and lead to many objects not being captured down to their fields. This change relaxes the condition on safety by ensuring that we can get an instance of `__dict__` that is of the _exact_ type `dict`. ## Risks The example that was found in the FastAPI is probably quite exotic as we don't expect actual objects to implement a `__dict__` attribute on a class with potential side-effects.
1 parent c24f467 commit ac4639c

File tree

2 files changed

+21
-14
lines changed

2 files changed

+21
-14
lines changed

ddtrace/debugging/safety.py

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,8 @@
55
from typing import Dict
66
from typing import Iterator
77
from typing import Tuple
8-
from typing import Type
98

109
from ddtrace.internal.safety import get_slots
11-
from ddtrace.internal.utils.cache import cached
1210

1311

1412
GetSetDescriptor = type(type.__dict__["__dict__"]) # type: ignore[index]
@@ -60,20 +58,16 @@ def safe_getitem(obj, index):
6058
raise TypeError("Type is not indexable collection " + str(type(obj)))
6159

6260

63-
@cached()
64-
def _has_safe_dict(_type):
65-
# type: (Type) -> bool
66-
try:
67-
return type(object.__getattribute__(_type, "__dict__").get("__dict__")) is GetSetDescriptor
68-
except AttributeError:
69-
return False
70-
71-
7261
def _safe_dict(o):
7362
# type: (Any) -> Dict[str, Any]
74-
if _has_safe_dict(type(o)):
75-
return object.__getattribute__(o, "__dict__")
76-
raise AttributeError("No safe __dict__ attribute")
63+
try:
64+
__dict__ = object.__getattribute__(o, "__dict__")
65+
if type(__dict__) is dict:
66+
return __dict__
67+
except Exception:
68+
pass # nosec
69+
70+
raise AttributeError("No safe __dict__")
7771

7872

7973
def get_fields(obj):

tests/debugging/test_safety.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import inspect
44

5+
import pytest
6+
57
from ddtrace.debugging import safety
68

79

@@ -75,3 +77,14 @@ def __init__(self):
7577

7678
assert safety.get_fields(A()) == {"a": "a"}
7779
assert safety.get_fields(B()) == {"a": "a", "b": "b"}
80+
81+
82+
def test_safe_dict():
83+
# Found in the FastAPI test suite
84+
class Foo(object):
85+
@property
86+
def __dict__(self):
87+
raise NotImplementedError()
88+
89+
with pytest.raises(AttributeError):
90+
safety._safe_dict(Foo())

0 commit comments

Comments
 (0)