Skip to content
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

Support PEP 561 to opentelemetry-util-http #3127

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
([#3148](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3148))
- add support to Python 3.13
([#3134](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3134))
- `opentelemetry-util-http` Add `py.typed` file to enable PEP 561
([#3127](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3127))

### Fixed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from re import IGNORECASE as RE_IGNORECASE
from re import compile as re_compile
from re import search
from typing import Callable, Iterable, Optional
from typing import Callable, Iterable
from urllib.parse import urlparse, urlunparse

from opentelemetry.semconv.trace import SpanAttributes
Expand Down Expand Up @@ -121,18 +121,16 @@ def sanitize_header_values(
_root = r"OTEL_PYTHON_{}"


def get_traced_request_attrs(instrumentation):
def get_traced_request_attrs(instrumentation: str) -> list[str]:
traced_request_attrs = environ.get(
_root.format(f"{instrumentation}_TRACED_REQUEST_ATTRS"), []
_root.format(f"{instrumentation}_TRACED_REQUEST_ATTRS")
)

if traced_request_attrs:
traced_request_attrs = [
return [
traced_request_attr.strip()
for traced_request_attr in traced_request_attrs.split(",")
]

return traced_request_attrs
return []


def get_excluded_urls(instrumentation: str) -> ExcludeList:
Expand Down Expand Up @@ -193,7 +191,7 @@ def normalise_response_header_name(header: str) -> str:
return f"http.response.header.{key}"


def sanitize_method(method: Optional[str]) -> Optional[str]:
def sanitize_method(method: str | None) -> str | None:
if method is None:
return None
method = method.upper()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@
not create spans on its own.
"""

from __future__ import annotations

import contextlib
import http.client
import logging
import socket # pylint:disable=unused-import # Used for typing
import typing
from typing import Collection
from typing import Any, Callable, Collection, TypedDict, cast

import wrapt

Expand All @@ -36,20 +38,22 @@

logger = logging.getLogger(__name__)

R = typing.TypeVar("R")


class HttpClientInstrumentor(BaseInstrumentor):
def instrumentation_dependencies(self) -> Collection[str]:
return () # This instruments http.client from stdlib; no extra deps.

def _instrument(self, **kwargs):
def _instrument(self, **kwargs: Any):
"""Instruments the http.client module (not creating spans on its own)"""
_instrument()

def _uninstrument(self, **kwargs):
def _uninstrument(self, **kwargs: Any):
_uninstrument()


def _remove_nonrecording(spanlist: typing.List[Span]):
def _remove_nonrecording(spanlist: list[Span]) -> bool:
idx = len(spanlist) - 1
while idx >= 0:
if not spanlist[idx].is_recording():
Expand All @@ -67,7 +71,9 @@ def _remove_nonrecording(spanlist: typing.List[Span]):
return True


def trysetip(conn: http.client.HTTPConnection, loglevel=logging.DEBUG) -> bool:
def trysetip(
conn: http.client.HTTPConnection, loglevel: int = logging.DEBUG
) -> bool:
"""Tries to set the net.peer.ip semantic attribute on the current span from the given
HttpConnection.

Expand Down Expand Up @@ -110,14 +116,17 @@ def trysetip(conn: http.client.HTTPConnection, loglevel=logging.DEBUG) -> bool:


def _instrumented_connect(
wrapped, instance: http.client.HTTPConnection, args, kwargs
):
wrapped: Callable[..., R],
instance: http.client.HTTPConnection,
args: tuple[Any, ...],
kwargs: dict[str, Any],
) -> R:
result = wrapped(*args, **kwargs)
trysetip(instance, loglevel=logging.WARNING)
return result


def instrument_connect(module, name="connect"):
def instrument_connect(module: type[Any], name: str = "connect"):
"""Instrument additional connect() methods, e.g. for derived classes."""

wrapt.wrap_function_wrapper(
Expand All @@ -129,8 +138,11 @@ def instrument_connect(module, name="connect"):

def _instrument():
def instrumented_send(
wrapped, instance: http.client.HTTPConnection, args, kwargs
):
wrapped: Callable[..., R],
instance: http.client.HTTPConnection,
args: tuple[Any, ...],
kwargs: dict[str, Any],
) -> R:
done = trysetip(instance)
result = wrapped(*args, **kwargs)
if not done:
Expand All @@ -147,8 +159,12 @@ def instrumented_send(
# No need to instrument HTTPSConnection, as it calls super().connect()


def _getstate() -> typing.Optional[dict]:
return context.get_value(_STATE_KEY)
class _ConnectionState(TypedDict):
need_ip: list[Span]


def _getstate() -> _ConnectionState | None:
return cast(_ConnectionState, context.get_value(_STATE_KEY))


@contextlib.contextmanager
Expand All @@ -163,7 +179,7 @@ def set_ip_on_next_http_connection(span: Span):
finally:
context.detach(token)
else:
spans: typing.List[Span] = state["need_ip"]
spans = state["need_ip"]
spans.append(span)
try:
yield
Expand Down
Empty file.
Loading