Skip to content
This repository was archived by the owner on Mar 4, 2026. It is now read-only.

Commit 47d3f92

Browse files
authored
Merge pull request #7 from UiPath/fix/add_attributes
fix: add span attributes
2 parents e0c1b78 + 83b0a1d commit 47d3f92

10 files changed

Lines changed: 32 additions & 53 deletions

File tree

pyproject.toml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "uipath-core"
3-
version = "0.0.3"
3+
version = "0.0.4"
44
description = "UiPath Core abstractions"
55
readme = { file = "README.md", content-type = "text/markdown" }
66
requires-python = ">=3.11"
@@ -12,7 +12,6 @@ dependencies = [
1212
classifiers = [
1313
"Intended Audience :: Developers",
1414
"Topic :: Software Development :: Build Tools",
15-
"Programming Language :: Python :: 3.10",
1615
"Programming Language :: Python :: 3.11",
1716
"Programming Language :: Python :: 3.12",
1817
"Programming Language :: Python :: 3.13",
@@ -25,7 +24,7 @@ maintainers = [
2524

2625
[project.urls]
2726
Homepage = "https://uipath.com"
28-
Repository = "https://github.com/UiPath/uipath-runtime-python"
27+
Repository = "https://github.com/UiPath/uipath-core-python"
2928
Documentation = "https://uipath.github.io/uipath-python/"
3029

3130
[build-system]

src/uipath/core/tracing/__init__.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,12 @@
44
with OpenTelemetry tracing, including custom processors for UiPath execution tracking.
55
"""
66

7-
from uipath.core.tracing.context import UiPathTraceContext
87
from uipath.core.tracing.decorators import traced
98
from uipath.core.tracing.span_utils import UiPathSpanUtils
109
from uipath.core.tracing.trace_manager import UiPathTraceManager
1110

1211
__all__ = [
1312
"traced",
14-
"UiPathTraceContext",
1513
"UiPathSpanUtils",
1614
"UiPathTraceManager",
1715
]

src/uipath/core/tracing/_utils.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from dataclasses import asdict, is_dataclass
77
from datetime import datetime, timezone
88
from enum import Enum
9-
from typing import Any, Dict, Mapping
9+
from typing import Any, Mapping
1010
from zoneinfo import ZoneInfo
1111

1212
from pydantic import BaseModel
@@ -15,15 +15,15 @@
1515
def get_supported_params(
1616
tracer_impl: Callable[..., Any],
1717
params: Mapping[str, Any],
18-
) -> Dict[str, Any]:
18+
) -> dict[str, Any]:
1919
"""Extract the parameters supported by the tracer implementation."""
2020
try:
2121
sig = inspect.signature(tracer_impl)
2222
except (TypeError, ValueError):
2323
# If we can't inspect, pass all parameters and let the function handle it
2424
return dict(params)
2525

26-
supported: Dict[str, Any] = {}
26+
supported: dict[str, Any] = {}
2727
for name, value in params.items():
2828
if value is not None and name in sig.parameters:
2929
supported[name] = value
@@ -90,7 +90,7 @@ def format_object_for_trace_json(
9090

9191
def format_args_for_trace(
9292
signature: inspect.Signature, *args: Any, **kwargs: Any
93-
) -> Dict[str, Any]:
93+
) -> dict[str, Any]:
9494
try:
9595
"""Return a dictionary of inputs from the function signature."""
9696
# Create a parameter mapping by partially binding the arguments

src/uipath/core/tracing/context.py

Lines changed: 0 additions & 23 deletions
This file was deleted.

src/uipath/core/tracing/exporters.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""Custom OpenTelemetry Span Exporter for UiPath Runtime executions."""
22

33
from collections import defaultdict
4-
from typing import Dict, List, Optional, Sequence
4+
from typing import Dict, Optional, Sequence
55

66
from opentelemetry.sdk.trace import ReadableSpan
77
from opentelemetry.sdk.trace.export import SpanExporter, SpanExportResult
@@ -12,7 +12,7 @@ class UiPathRuntimeExecutionSpanExporter(SpanExporter):
1212

1313
def __init__(self):
1414
"""Initialize the exporter."""
15-
self._spans: Dict[str, List[ReadableSpan]] = defaultdict(list)
15+
self._spans: Dict[str, list[ReadableSpan]] = defaultdict(list)
1616

1717
def export(self, spans: Sequence[ReadableSpan]) -> SpanExportResult:
1818
"""Export spans, grouping them by execution id."""
@@ -24,7 +24,7 @@ def export(self, spans: Sequence[ReadableSpan]) -> SpanExportResult:
2424

2525
return SpanExportResult.SUCCESS
2626

27-
def get_spans(self, execution_id: str) -> List[ReadableSpan]:
27+
def get_spans(self, execution_id: str) -> list[ReadableSpan]:
2828
"""Retrieve spans for a given execution id."""
2929
return self._spans.get(execution_id, [])
3030

src/uipath/core/tracing/span_utils.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""Utilities for managing UiPath spans."""
22

33
import logging
4-
from typing import Callable, Dict, List, Optional
4+
from typing import Callable, Optional
55

66
from opentelemetry import context, trace
77
from opentelemetry.trace import Span, set_span_in_context
@@ -15,8 +15,8 @@ class SpanRegistry:
1515
MAX_HIERARCHY_DEPTH = 1000 # Hard limit for hierarchy traversal
1616

1717
def __init__(self):
18-
self._spans: Dict[int, Span] = {} # span_id -> span
19-
self._parent_map: Dict[int, Optional[int]] = {} # span_id -> parent_id
18+
self._spans: dict[int, Span] = {} # span_id -> span
19+
self._parent_map: dict[int, Optional[int]] = {} # span_id -> parent_id
2020

2121
def register_span(self, span: Span) -> None:
2222
"""Register a span and its parent relationship."""
@@ -116,7 +116,7 @@ class UiPathSpanUtils:
116116
"""Static utility class to manage tracing implementations and decorated functions."""
117117

118118
_current_span_provider: Optional[Callable[[], Optional[Span]]] = None
119-
_current_span_ancestors_provider: Optional[Callable[[], List[Span]]] = None
119+
_current_span_ancestors_provider: Optional[Callable[[], list[Span]]] = None
120120

121121
@staticmethod
122122
def register_current_span_provider(
@@ -279,7 +279,7 @@ def get_external_current_span() -> Optional[Span]:
279279
return None
280280

281281
@staticmethod
282-
def get_ancestor_spans() -> List[Span]:
282+
def get_ancestor_spans() -> list[Span]:
283283
"""Get the ancestor spans from the registered provider, if any."""
284284
if UiPathSpanUtils._current_span_ancestors_provider is not None:
285285
try:
@@ -290,7 +290,7 @@ def get_ancestor_spans() -> List[Span]:
290290

291291
@staticmethod
292292
def register_current_span_ancestors_provider(
293-
current_span_ancestors_provider: Optional[Callable[[], List[Span]]],
293+
current_span_ancestors_provider: Optional[Callable[[], list[Span]]],
294294
):
295295
"""Register a custom current span ancestors provider function.
296296

src/uipath/core/tracing/trace_manager.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""Tracing manager for handling tracer implementations and function registry."""
22

33
import contextlib
4-
from typing import Any, Generator, List
4+
from typing import Any, Generator, Optional
55

66
from opentelemetry import trace
77
from opentelemetry.sdk.trace import ReadableSpan, SpanProcessor, TracerProvider
@@ -30,7 +30,7 @@ def __init__(self):
3030
"An incompatible Otel TracerProvider was instantiated. Please check runtime configuration."
3131
)
3232
self.tracer_provider: TracerProvider = current_provider
33-
self.tracer_span_processors: List[SpanProcessor] = []
33+
self.tracer_span_processors: list[SpanProcessor] = []
3434
self.execution_span_exporter = UiPathRuntimeExecutionSpanExporter()
3535
self.add_span_exporter(self.execution_span_exporter)
3636

@@ -52,20 +52,25 @@ def add_span_exporter(
5252
def get_execution_spans(
5353
self,
5454
execution_id: str,
55-
) -> List[ReadableSpan]:
55+
) -> list[ReadableSpan]:
5656
"""Retrieve spans for a given execution id."""
5757
return self.execution_span_exporter.get_spans(execution_id)
5858

5959
@contextlib.contextmanager
6060
def start_execution_span(
61-
self, root_span: str, execution_id: str
61+
self,
62+
root_span: str,
63+
execution_id: str,
64+
attributes: Optional[dict[str, str]] = None,
6265
) -> Generator[_AgnosticContextManager[Any] | Any, Any, None]:
6366
"""Start an execution span."""
6467
try:
6568
tracer = trace.get_tracer("uipath-runtime")
6669
span_attributes: dict[str, Any] = {}
6770
if execution_id:
6871
span_attributes["execution.id"] = execution_id
72+
if attributes:
73+
span_attributes.update(attributes)
6974
with tracer.start_as_current_span(
7075
root_span, attributes=span_attributes
7176
) as span:

tests/tracing/test_serialization.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,15 +118,15 @@ def process_timestamp(timestamp: datetime):
118118

119119
def test_traced_with_complex_return_value(span_capture: SpanCapture):
120120
"""Test tracing with complex return value."""
121-
from typing import Any, Dict
121+
from typing import Any
122122

123123
from pydantic import BaseModel
124124

125125
from uipath.core.tracing.decorators import traced
126126

127127
class Result(BaseModel):
128128
success: bool
129-
data: Dict[str, Any]
129+
data: dict[str, Any]
130130

131131
@traced(name="get_result")
132132
def get_result():

tests/tracing/test_traced.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from asyncio import sleep
33
from dataclasses import asdict, dataclass
44
from enum import Enum
5-
from typing import Any, Dict, List, Sequence
5+
from typing import Any, Sequence
66

77
import pytest
88
from opentelemetry import trace
@@ -30,7 +30,7 @@ def export(self, spans: Sequence[ReadableSpan]) -> SpanExportResult:
3030
self.spans.extend(spans)
3131
return SpanExportResult.SUCCESS
3232

33-
def get_exported_spans(self) -> List[ReadableSpan]:
33+
def get_exported_spans(self) -> list[ReadableSpan]:
3434
return self.spans
3535

3636
def clear_exported_spans(self) -> None:
@@ -239,7 +239,7 @@ async def async_operation(message):
239239
assert output["processed"] is True # Added by processor
240240

241241

242-
def mask_credit_card(inputs: Dict[str, Any]) -> Dict[str, Any]:
242+
def mask_credit_card(inputs: dict[str, Any]) -> dict[str, Any]:
243243
"""Process inputs to mask credit card information."""
244244
processed = inputs.copy()
245245
if "card_number" in processed:
@@ -251,7 +251,7 @@ def mask_credit_card(inputs: Dict[str, Any]) -> Dict[str, Any]:
251251
return processed
252252

253253

254-
def anonymize_single_user_data(output_dict: Dict[str, Any]) -> Dict[str, Any]:
254+
def anonymize_single_user_data(output_dict: dict[str, Any]) -> dict[str, Any]:
255255
"""Process a single dictionary to anonymize user information."""
256256
processed = output_dict.copy()
257257
if "user_info" in processed and isinstance(processed["user_info"], dict):
@@ -669,7 +669,7 @@ class OutputFormat(BaseModel):
669669
confidence: float = 0.95
670670

671671
@traced()
672-
async def llm_chat_completions(messages: List[Any], response_format=None):
672+
async def llm_chat_completions(messages: list[Any], response_format=None):
673673
"""Simulate LLM function with BaseModel class as response_format."""
674674
if response_format:
675675
mock_content = '{"result": "hi!", "confidence": 0.95}'

uv.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)