Skip to content

Commit 3135fae

Browse files
committed
feat: add deepcopy implementation for BoundedAttributes
1 parent 87160d0 commit 3135fae

3 files changed

Lines changed: 99 additions & 0 deletions

File tree

opentelemetry-api/src/opentelemetry/attributes/__init__.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
import copy
1516
import logging
1617
import threading
1718
from collections import OrderedDict
@@ -318,5 +319,18 @@ def __iter__(self): # type: ignore
318319
def __len__(self) -> int:
319320
return len(self._dict)
320321

322+
def __deepcopy__(self, memo: dict) -> "BoundedAttributes":
323+
with self._lock:
324+
attributes = copy.deepcopy(self._dict, memo)
325+
copy_ = BoundedAttributes(
326+
self.maxlen,
327+
attributes,
328+
self._immutable,
329+
self.max_value_len,
330+
self._extended_attributes,
331+
)
332+
memo[id(self)] = copy_
333+
return copy_
334+
321335
def copy(self): # type: ignore
322336
return self._dict.copy() # type: ignore

opentelemetry-api/tests/attributes/test_attributes.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
# type: ignore
1616

17+
import copy
1718
import unittest
1819
from typing import MutableSequence
1920

@@ -320,3 +321,29 @@ def __str__(self):
320321
self.assertEqual(
321322
"<DummyWSGIRequest method=GET path=/example/>", cleaned_value
322323
)
324+
325+
def test_deepcopy(self):
326+
bdict = BoundedAttributes(4, self.base, immutable=False)
327+
bdict_copy = copy.deepcopy(bdict)
328+
329+
for key in bdict_copy:
330+
self.assertEqual(bdict_copy[key], bdict[key])
331+
332+
self.assertEqual(bdict_copy.dropped, bdict.dropped)
333+
self.assertEqual(bdict_copy.maxlen, bdict.maxlen)
334+
self.assertEqual(bdict_copy.max_value_len, bdict.max_value_len)
335+
336+
bdict_copy["name"] = "Bob"
337+
self.assertNotEqual(bdict_copy["name"], bdict["name"])
338+
339+
bdict["age"] = 99
340+
self.assertNotEqual(bdict["age"], bdict_copy["age"])
341+
342+
def test_deepcopy_preserves_immutability(self):
343+
bdict = BoundedAttributes(
344+
maxlen=4, attributes=self.base, immutable=True
345+
)
346+
bdict_copy = copy.deepcopy(bdict)
347+
348+
with self.assertRaises(TypeError):
349+
bdict_copy["invalid"] = "invalid"

opentelemetry-sdk/tests/trace/test_trace.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
# pylint: disable=too-many-lines
1616
# pylint: disable=no-member
1717

18+
import copy
1819
import shutil
1920
import subprocess
2021
import unittest
@@ -708,6 +709,63 @@ def test_link_dropped_attributes(self):
708709
)
709710
self.assertEqual(link2.dropped_attributes, 0)
710711

712+
def test_deepcopy(self):
713+
context = trace_api.SpanContext(
714+
trace_id=0x000000000000000000000000DEADBEEF,
715+
span_id=0x00000000DEADBEF0,
716+
is_remote=False,
717+
)
718+
attributes = BoundedAttributes(
719+
10, {"key1": "value1", "key2": 42}, immutable=False
720+
)
721+
events = [
722+
trace.Event("event1", {"ekey": "evalue"}),
723+
trace.Event("event2", {"ekey2": "evalue2"}),
724+
]
725+
links = [
726+
trace_api.Link(
727+
context=trace_api.INVALID_SPAN_CONTEXT,
728+
attributes={"lkey": "lvalue"},
729+
)
730+
]
731+
732+
span = trace.ReadableSpan(
733+
name="test-span",
734+
context=context,
735+
attributes=attributes,
736+
events=events,
737+
links=links,
738+
status=Status(StatusCode.OK),
739+
)
740+
741+
span_copy = copy.deepcopy(span)
742+
743+
self.assertEqual(span_copy.name, span.name)
744+
self.assertEqual(span_copy.status.status_code, span.status.status_code)
745+
self.assertEqual(span_copy.context.trace_id, span.context.trace_id)
746+
self.assertEqual(span_copy.context.span_id, span.context.span_id)
747+
748+
self.assertEqual(dict(span_copy.attributes), dict(span.attributes))
749+
attributes["key1"] = "mutated"
750+
self.assertNotEqual(
751+
span_copy.attributes["key1"], span.attributes["key1"]
752+
)
753+
754+
self.assertEqual(len(span_copy.events), len(span.events))
755+
events[0] = trace.Event("mutated-event", {"mutated": "value"})
756+
self.assertNotEqual(span_copy.events[0].name, events[0].name)
757+
self.assertEqual(span_copy.events[0].name, "event1")
758+
759+
self.assertEqual(len(span_copy.links), len(span.links))
760+
self.assertEqual(
761+
span_copy.links[0].attributes, span.links[0].attributes
762+
)
763+
links[0] = trace_api.Link(
764+
context=trace_api.INVALID_SPAN_CONTEXT,
765+
attributes={"mutated": "link"},
766+
)
767+
self.assertNotIn("mutated", span_copy.links[0].attributes)
768+
711769

712770
class DummyError(Exception):
713771
pass

0 commit comments

Comments
 (0)