Skip to content

Add distributed tracing module for New Relic integration#180

Open
dpastranak wants to merge 6 commits intomainfrom
distributed-tracing
Open

Add distributed tracing module for New Relic integration#180
dpastranak wants to merge 6 commits intomainfrom
distributed-tracing

Conversation

@dpastranak
Copy link

@dpastranak dpastranak commented Jan 26, 2026

Introduces agave.core.tracing with utilities to propagate and accept distributed trace headers across HTTP calls and queue messages:

  • get_trace_headers(): extract current trace headers
  • accept_trace_headers(): continue trace from incoming headers
  • add_custom_attribute(): add attributes to current trace
  • @accept_trace_from_queue: decorator for queue consumers
  • @inject_trace_headers: decorator for HTTP client calls
  • @trace_attributes: decorator to add custom trace attributes

Also adds 'tracing' optional dependency (newrelic>=7.0.0,<12.0.0) and bumps version to 1.5.3.

Summary by CodeRabbit

  • New Features

    • Added New Relic–based distributed tracing: automatic trace header handling, decorators/utilities to inject/accept headers, attach custom attributes, and a background-task context; supports sync and async flows and optional queue header propagation.
  • Tests

    • Added comprehensive test coverage for tracing behaviors (sync/async, header propagation, attribute extraction, background tasks).
  • Chores

    • Bumped version to 1.5.3.dev01 and introduced an optional "tracing" extras dependency for New Relic.
  • Examples

    • Added an example demonstrating tracing, queue handling, and background-task usage.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Jan 26, 2026

Walkthrough

Adds a new agave/core/tracing.py module implementing New Relic distributed tracing integration (public API, decorators, helpers, and a background_task context manager), test coverage in tests/core/test_tracing.py, an examples/tasks/tracing_example.py example, a tracing extras requirement in setup.py, newrelic added to requirements-test.txt, and a version bump in agave/version.py to "1.5.3.dev01". No other modules were modified.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.63% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add distributed tracing module for New Relic integration' accurately describes the primary change: introduction of a new tracing module with New Relic integration. It is clear, specific, and directly reflects the main changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link

codecov bot commented Jan 26, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (e7aa7f5) to head (2ebb1ef).

Additional details and impacted files
@@            Coverage Diff             @@
##              main      #180    +/-   ##
==========================================
  Coverage   100.00%   100.00%            
==========================================
  Files           19        20     +1     
  Lines          711       814   +103     
  Branches        74        88    +14     
==========================================
+ Hits           711       814   +103     
Flag Coverage Δ
unittests 100.00% <100.00%> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@agave/core/tracing.py`:
- Line 106: The function signature for trace_attributes uses the Python 3.10
union syntax (Callable | str) which breaks 3.9; update the signature in
trace_attributes to use typing.Union (e.g., Union[Callable, str]) and add the
appropriate import (from typing import Callable, Union) at the module top, or
alternatively enable postponed evaluation by adding from __future__ import
annotations at the top of the file and keep the current signature—pick one
approach and apply it consistently for trace_attributes.
- Around line 17-29: The type annotation in accept_trace_headers uses Python
3.10+ PEP 604 syntax (dict | None) which breaks on 3.9; update the signature to
use typing.Optional (import Optional from typing) or enable postponed evaluation
(from __future__ import annotations) and replace "headers: dict | None" with
"headers: Optional[dict]" so accept_trace_headers retains the same behavior
while being compatible with Python 3.9.

In `@tests/core/test_tracing.py`:
- Around line 103-114: Update test function signatures that use the Python 3.10
union type syntax (e.g., in test_inject_trace_headers_async_keyword_arg and
other tests using inject_trace_headers/my_func/get_trace_headers) so they are
compatible with Python 3.9: replace occurrences of "dict | None" with
"Optional[dict]" and add "from typing import Optional" to the test module,
applying the same change to all affected test functions (lines referenced in the
review: the functions around the test_inject_trace_headers_async_keyword_arg and
the other listed tests).
🧹 Nitpick comments (2)
agave/core/tracing.py (2)

37-60: Consider adding async support for queue consumers.

Unlike inject_trace_headers and trace_attributes, this decorator only handles sync functions. If an async function is decorated, it won't be properly awaited. Consider adding async support for consistency:

♻️ Proposed enhancement
 def accept_trace_from_queue(func: Callable) -> Callable:
     ...
+    if inspect.iscoroutinefunction(func):
+        `@wraps`(func)
+        async def async_wrapper(*args, **kwargs):
+            trace_headers = kwargs.pop(TRACE_HEADERS_KEY, None)
+            if trace_headers:
+                accept_trace_headers(trace_headers, transport_type="Queue")
+            return await func(*args, **kwargs)
+        return async_wrapper
+
     `@wraps`(func)
     def wrapper(*args, **kwargs):
         trace_headers = kwargs.pop(TRACE_HEADERS_KEY, None)
         if trace_headers:
             accept_trace_headers(trace_headers, transport_type="Queue")
         return func(*args, **kwargs)
 
     return wrapper

174-186: Consider logging extraction failures at debug level for observability.

The silent except Exception: pass makes debugging difficult when attribute extraction fails unexpectedly. While the intent to not disrupt the primary logic is valid, adding debug-level logging would aid troubleshooting without impacting performance.

♻️ Optional improvement
+import logging
+
+logger = logging.getLogger(__name__)
+
 def _add_attributes(kwargs: dict, extractors: dict) -> None:
     ...
         except Exception:
-            pass  # Silent exception
+            logger.debug(
+                "Failed to extract trace attribute %s",
+                attr_name,
+                exc_info=True,
+            )
             # we don't want to fail if unable to extract an attribute

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@agave/core/tracing.py`:
- Around line 1-6: The file imports newrelic.agent at module import time;
instead, add a lazy loader function (e.g., _get_newrelic()) that tries to import
newrelic.agent and on ImportError raises a clear RuntimeError explaining tracing
requires the "tracing" extra, then replace all direct references to
newrelic.agent in this module (e.g., inside any decorator or helper functions in
agave/core/tracing.py) to call _get_newrelic() and use the returned module so
users who don't have New Relic installed can import agave.core without failing
until a tracing function is actually invoked.
🧹 Nitpick comments (2)
agave/core/tracing.py (2)

79-87: Validate param_name exists or **kwargs is accepted.

If param_name isn’t in the target signature and the function doesn’t accept **kwargs, the wrapper will fail later with an unexpected TypeError. Consider failing fast with a clear error at decoration time.

♻️ Proposed fix
     def decorator(func: Callable) -> Callable:
         sig = inspect.signature(func)
+        if param_name not in sig.parameters and not any(
+            p.kind == inspect.Parameter.VAR_KEYWORD
+            for p in sig.parameters.values()
+        ):
+            raise ValueError(
+                f"inject_trace_headers param_name '{param_name}' is not in "
+                f"{func.__name__} signature and the function does not accept **kwargs."
+            )

180-191: Avoid fully silent exception swallowing.

Swallowing all exceptions makes debugging attribute extraction failures hard. Consider at least logging at debug level (or narrowing the exception set) to preserve observability.

♻️ Proposed fix (debug logging)
+import logging
@@
+logger = logging.getLogger(__name__)
@@
-        except Exception:
-            pass  # Silent exception
+        except Exception as exc:
+            logger.debug(
+                "Trace attribute extraction failed for %s",
+                attr_name,
+                exc_info=exc,
+            )

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@agave/core/tracing.py`:
- Around line 39-62: The decorator accept_trace_from_queue only supports
synchronous functions; detect async functions (use inspect.iscoroutinefunction
or asyncio.iscoroutinefunction on func) and return an async wrapper that pops
TRACE_HEADERS_KEY from kwargs, calls accept_trace_headers(...,
transport_type="Queue") if present, then awaits func(*args, **kwargs); for sync
funcs keep the existing wrapper behavior. Ensure both wrappers are decorated
with functools.wraps(func) and reference TRACE_HEADERS_KEY and
accept_trace_headers exactly as in the diff so the fix preserves behavior and
signatures.
🧹 Nitpick comments (1)
agave/core/tracing.py (1)

190-191: Consider logging exceptions instead of silently swallowing them.

While the intent to not fail on extraction errors is correct, completely silent failures make debugging difficult. Consider logging at DEBUG level to aid troubleshooting without impacting production behavior.

♻️ Proposed refactor
+import logging
+
+logger = logging.getLogger(__name__)
+
 # In _add_attributes:
         except Exception:
-            pass  # Silent exception
-            # we don't want to fail if unable to extract an attribute
+            logger.debug(
+                "Failed to extract trace attribute '%s'",
+                attr_name,
+                exc_info=True,
+            )

Introduces agave.core.tracing with utilities to propagate and accept
distributed trace headers across HTTP calls and queue messages:

- get_trace_headers(): extract current trace headers
- accept_trace_headers(): continue trace from incoming headers
- add_custom_attribute(): add attributes to current trace
- @accept_trace_from_queue: decorator for queue consumers
- @inject_trace_headers: decorator for HTTP client calls
- @trace_attributes: decorator to add custom trace attributes

Also adds tracing optional dependency (newrelic>=7.0.0,<12.0.0)
and bumps version to 1.5.3.
- Update accept_trace_from_queue decorator to handle both sync and async functions
- Add tests for async queue handler support
Copy link
Contributor

@gmorales96 gmorales96 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Algunos puntos

pytest-vcr==1.0.2
pytest-asyncio==0.18.*
typing_extensions==4.12.2
newrelic==11.2.0 No newline at end of file
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agregar una linea al final del archivo

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

listo

from functools import wraps
from typing import Any, Callable, Optional

import newrelic.agent
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Creo que esto puede fallar, dado que newrelic es opcional, puedes hacer unas pruebas? Quizá podemos hacer algo como aquí:

try:
from aiobotocore.session import get_session
from types_aiobotocore_sqs import SQSClient
except ImportError:
raise ImportError(
"You must install agave with [asyncio_aws_tools] option.\n"
"You can install it with: pip install agave[asyncio_aws_tools]"
)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

listo

yield


def get_trace_headers() -> dict:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aquí podemos ser más específicos dict[str, str]

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lsito

@@ -0,0 +1,467 @@
from __future__ import annotations
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Que opinas si para mantener el estilo actual del proyecto ya que tiene soporte para 3.9 usemos Optional?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

listo

Comment on lines 1 to 10
from .tracing import (
TRACE_HEADERS_KEY,
accept_trace_from_queue,
accept_trace_headers,
add_custom_attribute,
background_task,
get_trace_headers,
inject_trace_headers,
trace_attributes,
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sugiero quitarlos de aqui.
Para el modulo agave.core no dependa de newrelic

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lsito

- Make newrelic import explicit with clear error message for missing dependency
- Remove tracing re-exports from agave.core to avoid forcing newrelic installation
- Use Optional and Union for Python 3.9 compatibility instead of PEP 604 syntax and future module
- Add specific type hints dict[str, str] for trace headers
- Add trailing newline to requirements-test.txt
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@agave/core/tracing.py`:
- Around line 6-12: The ImportError caught when trying to import newrelic.agent
in agave/core/tracing.py must be re-raised while preserving the original
exception context; update the except ImportError handler that currently raises a
new ImportError to chain the original exception using "raise ... from e" (i.e.,
capture the caught exception variable and re-raise the new ImportError with
"from" the original) so the ImportError cause is preserved for debugging.

return dict(headers_list)


def accept_trace_headers(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Esta función no es necesaria.
Realmente solo hace una linea y el transport_type lo usas como Queue

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

En este caso la función también incluye la validación if not headers: return que evita errores cuando los headers son None. Además, el transport_type por default es "HTTP" y se puede usar en contexto HHTP no solo como Queue.
Sin esta función cada que se use tendría que repetirse la validación también. Creo que la función aporta valor.

Comment on lines +34 to +37
def get_trace_headers() -> dict[str, str]:
headers_list: list = []
newrelic.agent.insert_distributed_trace_headers(headers_list)
return dict(headers_list)
Copy link
Member

@rogelioLpz rogelioLpz Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Esta funcion es un poco extraña, inserta una lista vacía y el resultado siempre es el mismo.
Creo que es mejor quitar la funcion y hacer directo el insert_distributed_trace_headers

Copy link
Author

@dpastranak dpastranak Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

es que más bien el API de newrelic está extraña. Pide pasar una lista vacía que muta internamente donde regresa los headers y esta función encapsula esta complejidad.

sin esta función se tendría que hacer

headers_list = []
newrelic.agent.insert_distributed_trace_headers(headers_list)
# Después de llamar, headers_list ya tiene datos:
# [('newrelic', 'eyJ2IjpbMCwxXS...'), ('traceparent', '00-abc123...')]
headers = dict(headers_list) # lo paso a un diccionario para pasarlo entre servicio. 

con la función solo se hace
headers = get_trace_headers()

o por qué dices que siempre regresa lo mismo?

Creo que justo esta función hace la interfaz más clara y simple.

Comment on lines +18 to +24
def test_get_trace_headers_returns_dict_with_headers():
with patch("newrelic.agent") as mock_agent:
mock_agent.insert_distributed_trace_headers.side_effect = (
lambda h: h.extend([("traceparent", "abc"), ("tracestate", "xyz")])
)
result = get_trace_headers()
assert result == {"traceparent": "abc", "tracestate": "xyz"}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

En general los test aunque cumplen la covertura, sería mejor tener test con los examples.

@celery_sqs.task
@accept_trace_from_queue
def example_task(...):
   # extract trace headers
   return {...headers}

Y el test debe ejecutar la task y evaluar el resultado

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

listo, agregué dos test más

- Remove docstrings from tracing module now examples serves as docs
- Add examples/tasks/tracing_example.py with usage examples
- Update accept_trace_from_queue to extract headers from body dict
- Add integration tests for tracing with SQS tasks
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@examples/tasks/tracing_example.py`:
- Around line 1-4: Add "from __future__ import annotations" at the top of the
module to enable postponed evaluation of annotations so PEP 604 union types like
"dict | None" used later (e.g., in the tracing_example.py annotations and any
function signatures such as those involving accept_trace_from_queue) work on
Python 3.9; insert this single import as the very first line before other
imports (above BaseModel and agave.core.tracing imports) so runtime parsing of
"dict | None" is deferred.
🧹 Nitpick comments (1)
agave/core/tracing.py (1)

156-169: Consider logging extraction failures for debugging.

The silent except Exception: pass is intentional to avoid failing traces due to attribute extraction errors, which is a reasonable design choice. However, adding a debug-level log would help diagnose issues without impacting production.

♻️ Optional: Add debug logging
+import logging
+
+logger = logging.getLogger(__name__)
+
 def _add_attributes(kwargs: dict, extractors: dict) -> None:
     for attr_name, extractor in extractors.items():
         try:
             if callable(extractor):
                 value = extractor(kwargs)
             elif isinstance(extractor, str):
                 value = _get_nested_value(kwargs, extractor)
             else:
                 value = None
 
             add_custom_attribute(attr_name, value)
-        except Exception:
-            pass  # Silent exception
-            # we don't want to fail if unable to extract an attribute
+        except Exception:
+            logger.debug(
+                "Failed to extract trace attribute %s", attr_name, exc_info=True
+            )

Comment on lines +1 to +4
from pydantic import BaseModel

from agave.core.tracing import (
accept_trace_from_queue,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add from __future__ import annotations for Python 3.9 compatibility.

Line 37 uses dict | None (PEP 604 union syntax) which requires Python 3.10+ at runtime. Adding the future import enables postponed annotation evaluation, making this compatible with Python 3.9.

🐛 Proposed fix
+from __future__ import annotations
+
 from pydantic import BaseModel
 
 from agave.core.tracing import (
🤖 Prompt for AI Agents
In `@examples/tasks/tracing_example.py` around lines 1 - 4, Add "from __future__
import annotations" at the top of the module to enable postponed evaluation of
annotations so PEP 604 union types like "dict | None" used later (e.g., in the
tracing_example.py annotations and any function signatures such as those
involving accept_trace_from_queue) work on Python 3.9; insert this single import
as the very first line before other imports (above BaseModel and
agave.core.tracing imports) so runtime parsing of "dict | None" is deferred.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a New Relic–based distributed tracing module and integrates it with Agave’s task queues, plus an example and tests. It introduces utilities and decorators for propagating and accepting distributed trace headers across HTTP calls and SQS-based tasks, along with an optional tracing extra and version bump.

Changes:

  • Added agave.core.tracing with helpers (get_trace_headers, accept_trace_headers, add_custom_attribute) and decorators (accept_trace_from_queue, inject_trace_headers, trace_attributes) plus a background_task context manager.
  • Introduced a new tracing extras group for newrelic and adjusted test requirements to include the New Relic agent.
  • Added comprehensive tests for tracing behavior and an example task module demonstrating tracing and queue integration, and bumped the library version to 1.5.3.dev01.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
agave/core/tracing.py Implements New Relic tracing primitives (header extraction/acceptance, custom attributes, queue integration, and background task context) used by both HTTP and SQS flows.
tests/core/test_tracing.py Adds unit and integration-style tests covering sync/async decorators, attribute extraction, SQS task integration, and background task behavior; a few tests currently use an invalid patch target and will fail.
setup.py Declares a new tracing optional dependency (newrelic>=7.0.0,<12.0.0) and already targets Python >=3.9.
requirements-test.txt Pins newrelic==11.2.0 so the tracing tests can run in CI.
examples/tasks/tracing_example.py Provides an example of using the new tracing utilities with SQS tasks and background tasks; one type annotation currently uses syntax that is invalid on Python 3.9.
agave/version.py Bumps the library version to 1.5.3.dev01, which slightly differs from the PR description text.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@@ -1 +1 @@
__version__ = '1.5.2'
__version__ = "1.5.3.dev01"
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR description mentions bumping the version to 1.5.3, but the code sets __version__ to "1.5.3.dev01". Please either update the version string here or adjust the PR description so they are consistent.

Copilot uses AI. Check for mistakes.
Comment on lines +425 to +427
def test_background_task_creates_transaction():
"""Test that background_task creates a New Relic BackgroundTask."""
with patch("agave.core.tracing.newrelic.agent") as mock_agent:
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The patch target "agave.core.tracing.newrelic.agent" is not a valid module path for unittest.mock.patch (it tries to import a module named agave.core.tracing.newrelic, which does not exist), so this test will raise ModuleNotFoundError instead of patching New Relic. To patch the newrelic.agent object actually used by agave.core.tracing, you should target the real module path (for example, "newrelic.agent" as in the other tests in this file).

Copilot uses AI. Check for mistakes.
Comment on lines +442 to +444
def test_background_task_accepts_trace_headers():
"""Test that background_task accepts trace headers inside transaction."""
with patch("agave.core.tracing.newrelic.agent") as mock_agent:
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As in the previous background_task test, the patch target "agave.core.tracing.newrelic.agent" is not a real module and will cause unittest.mock.patch to fail with ModuleNotFoundError, so this test will never exercise the background_task logic. Please switch the target to the actual newrelic.agent module used by the tracing code (e.g. "newrelic.agent") so the New Relic agent calls are correctly mocked.

Copilot uses AI. Check for mistakes.
Comment on lines +459 to +461
def test_background_task_does_not_accept_when_no_headers():
"""Test that background_task skips accept when no trace headers."""
with patch("agave.core.tracing.newrelic.agent") as mock_agent:
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test also patches "agave.core.tracing.newrelic.agent", which is not a valid module path, so patch will fail before the test runs and background_task will not be exercised. Align this with the other tests by patching the real newrelic.agent module instead (for example, using "newrelic.agent" as the patch target).

Copilot uses AI. Check for mistakes.
Comment on lines +37 to +38
method: str, endpoint: str, trace_headers: dict | None = None
) -> None:
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The type annotation trace_headers: dict | None = None uses the PEP 604 union operator, which is only valid syntax in Python 3.10+, but this project declares python_requires='>=3.9' in setup.py. To keep the examples compatible with Python 3.9, please use Optional[dict] (with the appropriate import) or dict[str, Any] | None only after dropping 3.9 support.

Copilot uses AI. Check for mistakes.
@felipao-mx
Copy link
Contributor

se aprueba sin ver! ❤️

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants