Skip to content

Commit 3d9de97

Browse files
Fix typing in ReadableSpan (#3528)
Co-authored-by: Srikanth Chekuri <[email protected]>
1 parent 0a70dc3 commit 3d9de97

File tree

2 files changed

+74
-84
lines changed

2 files changed

+74
-84
lines changed

opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py

+69-80
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import threading
2222
import traceback
2323
import typing
24-
from collections import OrderedDict
2524
from contextlib import contextmanager
2625
from os import environ
2726
from time import time_ns
@@ -31,6 +30,7 @@
3130
Callable,
3231
Dict,
3332
Iterator,
33+
List,
3434
Optional,
3535
Sequence,
3636
Tuple,
@@ -353,19 +353,19 @@ class ReadableSpan:
353353

354354
def __init__(
355355
self,
356-
name: str = None,
357-
context: trace_api.SpanContext = None,
356+
name: str,
357+
context: Optional[trace_api.SpanContext] = None,
358358
parent: Optional[trace_api.SpanContext] = None,
359-
resource: Resource = None,
359+
resource: Optional[Resource] = None,
360360
attributes: types.Attributes = None,
361361
events: Sequence[Event] = (),
362362
links: Sequence[trace_api.Link] = (),
363363
kind: trace_api.SpanKind = trace_api.SpanKind.INTERNAL,
364-
instrumentation_info: InstrumentationInfo = None,
364+
instrumentation_info: Optional[InstrumentationInfo] = None,
365365
status: Status = Status(StatusCode.UNSET),
366366
start_time: Optional[int] = None,
367367
end_time: Optional[int] = None,
368-
instrumentation_scope: InstrumentationScope = None,
368+
instrumentation_scope: Optional[InstrumentationScope] = None,
369369
) -> None:
370370
self._name = name
371371
self._context = context
@@ -386,19 +386,19 @@ def __init__(
386386

387387
@property
388388
def dropped_attributes(self) -> int:
389-
if self._attributes:
389+
if isinstance(self._attributes, BoundedAttributes):
390390
return self._attributes.dropped
391391
return 0
392392

393393
@property
394394
def dropped_events(self) -> int:
395-
if self._events:
395+
if isinstance(self._events, BoundedList):
396396
return self._events.dropped
397397
return 0
398398

399399
@property
400400
def dropped_links(self) -> int:
401-
if self._links:
401+
if isinstance(self._links, BoundedList):
402402
return self._links.dropped
403403
return 0
404404

@@ -435,7 +435,7 @@ def status(self) -> trace_api.Status:
435435

436436
@property
437437
def attributes(self) -> types.Attributes:
438-
return MappingProxyType(self._attributes)
438+
return MappingProxyType(self._attributes or {})
439439

440440
@property
441441
def events(self) -> Sequence[Event]:
@@ -453,23 +453,17 @@ def resource(self) -> Resource:
453453
@deprecated(
454454
version="1.11.1", reason="You should use instrumentation_scope"
455455
)
456-
def instrumentation_info(self) -> InstrumentationInfo:
456+
def instrumentation_info(self) -> Optional[InstrumentationInfo]:
457457
return self._instrumentation_info
458458

459459
@property
460-
def instrumentation_scope(self) -> InstrumentationScope:
460+
def instrumentation_scope(self) -> Optional[InstrumentationScope]:
461461
return self._instrumentation_scope
462462

463-
def to_json(self, indent=4):
463+
def to_json(self, indent: int = 4):
464464
parent_id = None
465465
if self.parent is not None:
466-
if isinstance(self.parent, Span):
467-
ctx = self.parent.context
468-
parent_id = f"0x{trace_api.format_span_id(ctx.span_id)}"
469-
elif isinstance(self.parent, SpanContext):
470-
parent_id = (
471-
f"0x{trace_api.format_span_id(self.parent.span_id)}"
472-
)
466+
parent_id = f"0x{trace_api.format_span_id(self.parent.span_id)}"
473467

474468
start_time = None
475469
if self._start_time:
@@ -479,77 +473,72 @@ def to_json(self, indent=4):
479473
if self._end_time:
480474
end_time = util.ns_to_iso_str(self._end_time)
481475

482-
if self._status is not None:
483-
status = OrderedDict()
484-
status["status_code"] = str(self._status.status_code.name)
485-
if self._status.description:
486-
status["description"] = self._status.description
487-
488-
f_span = OrderedDict()
489-
490-
f_span["name"] = self._name
491-
f_span["context"] = self._format_context(self._context)
492-
f_span["kind"] = str(self.kind)
493-
f_span["parent_id"] = parent_id
494-
f_span["start_time"] = start_time
495-
f_span["end_time"] = end_time
496-
if self._status is not None:
497-
f_span["status"] = status
498-
f_span["attributes"] = self._format_attributes(self._attributes)
499-
f_span["events"] = self._format_events(self._events)
500-
f_span["links"] = self._format_links(self._links)
501-
f_span["resource"] = json.loads(self.resource.to_json())
476+
status = {
477+
"status_code": str(self._status.status_code.name),
478+
}
479+
if self._status.description:
480+
status["description"] = self._status.description
481+
482+
f_span = {
483+
"name": self._name,
484+
"context": self._format_context(self._context)
485+
if self._context
486+
else None,
487+
"kind": str(self.kind),
488+
"parent_id": parent_id,
489+
"start_time": start_time,
490+
"end_time": end_time,
491+
"status": status,
492+
"attributes": self._format_attributes(self._attributes),
493+
"events": self._format_events(self._events),
494+
"links": self._format_links(self._links),
495+
"resource": json.loads(self.resource.to_json()),
496+
}
502497

503498
return json.dumps(f_span, indent=indent)
504499

505500
@staticmethod
506-
def _format_context(context):
507-
x_ctx = OrderedDict()
508-
x_ctx["trace_id"] = f"0x{trace_api.format_trace_id(context.trace_id)}"
509-
x_ctx["span_id"] = f"0x{trace_api.format_span_id(context.span_id)}"
510-
x_ctx["trace_state"] = repr(context.trace_state)
511-
return x_ctx
501+
def _format_context(context: SpanContext) -> Dict[str, str]:
502+
return {
503+
"trace_id": f"0x{trace_api.format_trace_id(context.trace_id)}",
504+
"span_id": f"0x{trace_api.format_span_id(context.span_id)}",
505+
"trace_state": repr(context.trace_state),
506+
}
512507

513508
@staticmethod
514-
def _format_attributes(attributes):
515-
if isinstance(attributes, BoundedAttributes):
516-
return attributes._dict # pylint: disable=protected-access
517-
if isinstance(attributes, MappingProxyType):
518-
return attributes.copy()
509+
def _format_attributes(
510+
attributes: types.Attributes,
511+
) -> Optional[Dict[str, Any]]:
512+
if attributes is not None and not isinstance(attributes, dict):
513+
return dict(attributes)
519514
return attributes
520515

521516
@staticmethod
522-
def _format_events(events):
523-
f_events = []
524-
for event in events:
525-
f_event = OrderedDict()
526-
f_event["name"] = event.name
527-
f_event["timestamp"] = util.ns_to_iso_str(event.timestamp)
528-
f_event[
529-
"attributes"
530-
] = Span._format_attributes( # pylint: disable=protected-access
531-
event.attributes
532-
)
533-
f_events.append(f_event)
534-
return f_events
517+
def _format_events(events: Sequence[Event]) -> List[Dict[str, Any]]:
518+
return [
519+
{
520+
"name": event.name,
521+
"timestamp": util.ns_to_iso_str(event.timestamp),
522+
"attributes": Span._format_attributes( # pylint: disable=protected-access
523+
event.attributes
524+
),
525+
}
526+
for event in events
527+
]
535528

536529
@staticmethod
537-
def _format_links(links):
538-
f_links = []
539-
for link in links:
540-
f_link = OrderedDict()
541-
f_link[
542-
"context"
543-
] = Span._format_context( # pylint: disable=protected-access
544-
link.context
545-
)
546-
f_link[
547-
"attributes"
548-
] = Span._format_attributes( # pylint: disable=protected-access
549-
link.attributes
550-
)
551-
f_links.append(f_link)
552-
return f_links
530+
def _format_links(links: Sequence[trace_api.Link]) -> List[Dict[str, Any]]:
531+
return [
532+
{
533+
"context": Span._format_context( # pylint: disable=protected-access
534+
link.context
535+
),
536+
"attributes": Span._format_attributes( # pylint: disable=protected-access
537+
link.attributes
538+
),
539+
}
540+
for link in links
541+
]
553542

554543

555544
class SpanLimits:

opentelemetry-sdk/tests/trace/test_trace.py

+5-4
Original file line numberDiff line numberDiff line change
@@ -605,24 +605,25 @@ def test_surplus_span_attributes(self):
605605

606606
class TestReadableSpan(unittest.TestCase):
607607
def test_links(self):
608-
span = trace.ReadableSpan()
608+
span = trace.ReadableSpan("test")
609609
self.assertEqual(span.links, ())
610610

611611
span = trace.ReadableSpan(
612+
"test",
612613
links=[trace_api.Link(context=trace_api.INVALID_SPAN_CONTEXT)] * 2,
613614
)
614615
self.assertEqual(len(span.links), 2)
615616
for link in span.links:
616617
self.assertFalse(link.context.is_valid)
617618

618619
def test_events(self):
619-
span = trace.ReadableSpan()
620+
span = trace.ReadableSpan("test")
620621
self.assertEqual(span.events, ())
621622
events = [
622623
trace.Event("foo1", {"bar1": "baz1"}),
623624
trace.Event("foo2", {"bar2": "baz2"}),
624625
]
625-
span = trace.ReadableSpan(events=events)
626+
span = trace.ReadableSpan("test", events=events)
626627
self.assertEqual(span.events, tuple(events))
627628

628629

@@ -1376,7 +1377,7 @@ def test_to_json(self):
13761377
)
13771378
parent = trace._Span("parent-name", context, resource=Resource({}))
13781379
span = trace._Span(
1379-
"span-name", context, resource=Resource({}), parent=parent
1380+
"span-name", context, resource=Resource({}), parent=parent.context
13801381
)
13811382

13821383
self.assertEqual(

0 commit comments

Comments
 (0)