-
Notifications
You must be signed in to change notification settings - Fork 117
Open
Labels
enhancementNew feature or requestNew feature or request
Description
Goal: Instrument individual reactive computations (calcs, effects, outputs) with descriptive labels and source attribution.
Status: ✅ Complete
PR #2149 implements all Phase 4 functionality with additional improvements from code review.
Completed Tasks
- ✅ Created
shiny/otel/_labels.pywith:generate_reactive_label(func, label_type, namespace, modifier)- Generate descriptive labels- Extract function name, handle anonymous functions
- Add module namespace prefix (e.g.,
"reactive my-module:myValue") - Support modifiers like
"cache","event"
- ✅ Extended
shiny/otel/_attributes.pywith:extract_source_ref(func)- Extract source location usinginspectmodule- Return dict with
code.filepath,code.lineno,code.function - Improvement: Separate try/catch blocks maximize attribute extraction
- ✅ Modified
Calc_.update_value()to wrap execution in span with label"reactive <name>"- Improvement: Lazy name generation eliminates code duplication
- ✅ Modified
Effect_._run()to wrap execution in span with label"observe <name>"- Improvement: Lazy name generation eliminates code duplication
- ✅ Modified
Outputs.__call__to wrap renderer execution in span with label"output <name>"- Improvement: Lazy callables for both name and attributes
- ✅ Store OTel attributes (source refs) on reactive objects at creation time
- ✅ Enhanced
with_otel_span_async()to accept callable name parameter - ✅ Moved OTel imports to top-level in
_reactives.pyand all test files for optimal performance - ✅ Added internal test helpers to
shiny/otel/_core.py:reset_tracing_state(*, tracing_enabled=None)- Reset cached tracing statepatch_tracing_state(*, tracing_enabled)- Context manager for temporary patching- Both use keyword-only arguments, not exported in public API
- Replaced 22+ occurrences across 4 test files
Acceptance Criteria
- ✅ Reactive computations create spans when
SHINY_OTEL_COLLECT >= reactivity - ✅ Spans have descriptive labels (function names, not just "reactive")
- ✅ Source location attributes (
code.filepath,code.lineno) included when available - ✅ Module namespaces reflected in span names
- ✅ Async context propagates correctly (child spans nest under reactive.update)
- ✅ Spans include parent reactive.update span context
- ✅ Bonus: Code duplication eliminated through lazy evaluation (~150 lines reduced)
- ✅ Bonus: Strong typing with no suppressions (except known OTel SDK limitation)
- ✅ Bonus: Test code improved with helper utilities for better encapsulation
Implementation Improvements
-
Callable Name Parameter - Added support for
name: str | Callable[[], str]in span wrappers- Enables lazy evaluation of expensive name generation
- Only called if collection is enabled
- Eliminates if/else duplication
-
Top-Level Imports - Moved OTel imports to module level
- Removes repeated inline import overhead during execution
- Cleaner code structure in both production and test code
-
Separate Try/Catch -
extract_source_ref()uses separate blocks- Maximizes attribute extraction even if some operations fail
- More robust error handling
-
Test Helper Utilities - Internal functions for test isolation
reset_tracing_state(*, tracing_enabled=None)- Replaces direct state manipulationpatch_tracing_state(*, tracing_enabled)- Replacesunittest.mock.patch- Better encapsulation, cleaner test code, easier refactoring
-
Code Reduction - ~150 lines eliminated:
- Calc: 67% reduction (40 → 12 lines)
- Effect: 50% reduction (90 → 45 lines)
- Output: 48% reduction (25 → 13 lines)
Files Created
- ✅
shiny/otel/_labels.py - ✅
tests/pytest/test_otel_reactive_execution.py(16 tests, all passing)
Files Modified
- ✅
shiny/otel/_core.py(added test helpers) - ✅
shiny/otel/_attributes.py - ✅
shiny/otel/_span_wrappers.py(added callable name support) - ✅
shiny/reactive/_reactives.py - ✅
shiny/session/_session.py - ✅
tests/pytest/test_otel_foundation.py(updated to use test helpers) - ✅
tests/pytest/test_otel_session.py(updated to use test helpers) - ✅
tests/pytest/test_otel_reactive_flush.py(updated to use test helpers)
Test Results
- ✅ All 408 tests pass (407 passed + 1 skipped)
- ✅ Pyright: 0 errors, 0 warnings
- ✅ Flake8: Clean
- ✅ Black: Formatted
- ✅ isort: Sorted
Metadata
Metadata
Assignees
Labels
enhancementNew feature or requestNew feature or request