Skip to content

Fix deep copy related issue #46

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Feb 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
import copy
from typing import List, Sequence, TypeVar

from typing_extensions import override
Expand Down Expand Up @@ -103,11 +102,14 @@ def copy_attributes_with_local_root(attributes: BoundedAttributes) -> BoundedAtt
)


# TODO: AwsMetricAttributesSpanExporter depends on internal ReadableSpan method _attributes.
# This is a bit risky but is required for our implementation.
# The risk is that the implementation of _attributes changes in the future.
# We need tests that thoroughly test this behaviour to make sure it does not change upstream.
def wrap_span_with_attributes(span: ReadableSpan, attributes: BoundedAttributes) -> ReadableSpan:
# To make sure we create a new span without influence original span's Attributes
# We have to create a deepcopy for it
new_span = copy.deepcopy(span)
original_attributes: AttributesT = new_span.attributes
original_attributes: AttributesT = span.attributes
update_attributes: types.Attributes = {}
# Copy all attribute in span into update_attributes
for key, value in original_attributes.items():
Expand All @@ -117,12 +119,12 @@ def wrap_span_with_attributes(span: ReadableSpan, attributes: BoundedAttributes)
update_attributes[key] = value

if isinstance(original_attributes, BoundedAttributes):
new_span._attributes = BoundedAttributes(
span._attributes = BoundedAttributes(
maxlen=original_attributes.maxlen,
attributes=update_attributes,
immutable=original_attributes._immutable,
max_value_len=original_attributes.max_value_len,
)
else:
new_span._attributes = update_attributes
return new_span
span._attributes = update_attributes
return span
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
import copy
from typing import Any
from unittest import TestCase
from unittest.mock import MagicMock, Mock, call
from unittest.mock import MagicMock, call

from amazon.opentelemetry.distro._aws_attribute_keys import AWS_CONSUMER_PARENT_SPAN_KIND, AWS_SPAN_KIND
from amazon.opentelemetry.distro._aws_metric_attribute_generator import _AwsMetricAttributeGenerator
Expand All @@ -16,11 +15,10 @@
from amazon.opentelemetry.distro.metric_attribute_generator import DEPENDENCY_METRIC, SERVICE_METRIC
from opentelemetry.attributes import BoundedAttributes
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace import Event, ReadableSpan
from opentelemetry.sdk.trace import ReadableSpan
from opentelemetry.sdk.trace.export import SpanExporter
from opentelemetry.sdk.util.instrumentation import InstrumentationScope
from opentelemetry.semconv.trace import MessagingOperationValues, SpanAttributes
from opentelemetry.trace import Link, SpanContext, SpanKind, Status
from opentelemetry.trace import SpanContext, SpanKind
from opentelemetry.util.types import Attributes

_CONTAINS_ATTRIBUTES: bool = True
Expand Down Expand Up @@ -158,54 +156,6 @@ def test_overridden_attributes(self):
self.assertEqual(exported_span._attributes["key2"], "old value2")
self.assertEqual(exported_span._attributes["key3"], "new value3")

def test_export_delegating_span_data_behaviour(self):
span_attributes: Attributes = self._build_span_attributes(_CONTAINS_ATTRIBUTES)
span_data_mock: ReadableSpan = self._build_readable_span_mock_without_deepcopy_support(span_attributes)
metric_attributes: Attributes = self._build_metric_attributes(_CONTAINS_ATTRIBUTES)
self._configure_mock_for_export(span_data_mock, metric_attributes)

self.aws_metric_attributes_span_exporter.export([span_data_mock])
self.delegate_mock.assert_has_calls([call.export([span_data_mock])])
exported_spans: Attributes = self.delegate_mock.export.call_args[0][0]
self.assertEqual(len(exported_spans), 1)

exported_span: ReadableSpan = exported_spans[0]

span_context_mock: SpanContext = MagicMock()
span_data_mock.get_span_context.return_value = span_context_mock
self.assertEqual(exported_span.get_span_context(), span_context_mock)

parent_span_context_mock: SpanContext = MagicMock()
span_data_mock._parent = parent_span_context_mock
self.assertEqual(exported_span._parent, parent_span_context_mock)

span_data_mock.set_attribute("_resource", self.test_resource)
self.assertEqual(exported_span._resource, self.test_resource)

test_instrumentation_scope_info: InstrumentationScope = MagicMock()
span_data_mock.set_attribute("_instrumentation_scope", test_instrumentation_scope_info)
self.assertEqual(exported_span._instrumentation_scope, test_instrumentation_scope_info)

test_name: str = "name"
span_data_mock.set_attribute("_name", test_name)
self.assertEqual(exported_span._name, test_name)

kind_mock: SpanKind = Mock()
span_data_mock.set_attribute("_kind", kind_mock)
self.assertEqual(exported_span._kind, kind_mock)

events_mock: [Event] = [Mock()]
span_data_mock.set_attribute("_events", events_mock)
self.assertEqual(exported_span._events, events_mock)

links_mock: [Link] = [Mock()]
span_data_mock.set_attribute("_links", links_mock)
self.assertEqual(exported_span._links, links_mock)

status_mock: Status = Mock()
span_data_mock.set_attribute("_status", status_mock)
self.assertEqual(exported_span._status, status_mock)

def test_export_delegation_with_two_metrics(self):
span_attributes: Attributes = self._build_span_attributes(_CONTAINS_ATTRIBUTES)

Expand Down Expand Up @@ -375,22 +325,3 @@ def _build_readable_span_mock(self, span_attributes: Attributes) -> ReadableSpan
mock_span_data._parent = None
mock_span_data.attributes = mock_span_data._attributes
return mock_span_data

def _build_readable_span_mock_without_deepcopy_support(self, span_attributes: Attributes) -> ReadableSpan:
class NoDeepCopyMock(MagicMock):
def __init__(self, *args: Any, **kw: Any):
super().__init__(*args, **kw)
self._attributes = span_attributes
self._kind = SpanKind.SERVER
self._parent = None
self.attributes = self._attributes

def set_attribute(self, name, value):
setattr(self, name, value)

def __deepcopy__(self, memo):
return self

mock_span_data: ReadableSpan = NoDeepCopyMock()

return mock_span_data