@@ -169,8 +169,11 @@ def client_response_hook(span: Span, scope: dict[str, Any], message: dict[str, A
169169API
170170---
171171"""
172+ # pyright: reportPrivateUsage=false
172173
173- from typing import Collection
174+ from __future__ import annotations
175+
176+ from typing import TYPE_CHECKING , Any , Collection , cast
174177
175178from starlette import applications
176179from starlette .routing import Match
@@ -184,18 +187,29 @@ def client_response_hook(span: Span, scope: dict[str, Any], message: dict[str, A
184187from opentelemetry .instrumentation .instrumentor import BaseInstrumentor
185188from opentelemetry .instrumentation .starlette .package import _instruments
186189from opentelemetry .instrumentation .starlette .version import __version__
187- from opentelemetry .metrics import get_meter
190+ from opentelemetry .metrics import MeterProvider , get_meter
188191from opentelemetry .semconv .trace import SpanAttributes
189- from opentelemetry .trace import get_tracer
192+ from opentelemetry .trace import TracerProvider , get_tracer
190193from opentelemetry .util .http import get_excluded_urls
191194
195+ if TYPE_CHECKING :
196+ from typing import NotRequired , TypedDict , Unpack
197+
198+ class InstrumentKwargs (TypedDict ):
199+ tracer_provider : NotRequired [TracerProvider ]
200+ meter_provider : NotRequired [MeterProvider ]
201+ server_request_hook : NotRequired [ServerRequestHook ]
202+ client_request_hook : NotRequired [ClientRequestHook ]
203+ client_response_hook : NotRequired [ClientResponseHook ]
204+
205+
192206_excluded_urls = get_excluded_urls ("STARLETTE" )
193207
194208
195209class StarletteInstrumentor (BaseInstrumentor ):
196- """An instrumentor for starlette
210+ """An instrumentor for Starlette.
197211
198- See `BaseInstrumentor`
212+ See `BaseInstrumentor`.
199213 """
200214
201215 _original_starlette = None
@@ -206,8 +220,8 @@ def instrument_app(
206220 server_request_hook : ServerRequestHook = None ,
207221 client_request_hook : ClientRequestHook = None ,
208222 client_response_hook : ClientResponseHook = None ,
209- meter_provider = None ,
210- tracer_provider = None ,
223+ meter_provider : MeterProvider | None = None ,
224+ tracer_provider : TracerProvider | None = None ,
211225 ):
212226 """Instrument an uninstrumented Starlette application."""
213227 tracer = get_tracer (
@@ -242,34 +256,24 @@ def instrument_app(
242256
243257 @staticmethod
244258 def uninstrument_app (app : applications .Starlette ):
245- app .user_middleware = [
246- x
247- for x in app .user_middleware
248- if x .cls is not OpenTelemetryMiddleware
249- ]
259+ app .user_middleware = [x for x in app .user_middleware if x .cls is not OpenTelemetryMiddleware ]
250260 app .middleware_stack = app .build_middleware_stack ()
251261 app ._is_instrumented_by_opentelemetry = False
252262
253263 def instrumentation_dependencies (self ) -> Collection [str ]:
254264 return _instruments
255265
256- def _instrument (self , ** kwargs ):
266+ def _instrument (self , ** kwargs : Unpack [ InstrumentKwargs ] ):
257267 self ._original_starlette = applications .Starlette
258268 _InstrumentedStarlette ._tracer_provider = kwargs .get ("tracer_provider" )
259- _InstrumentedStarlette ._server_request_hook = kwargs .get (
260- "server_request_hook"
261- )
262- _InstrumentedStarlette ._client_request_hook = kwargs .get (
263- "client_request_hook"
264- )
265- _InstrumentedStarlette ._client_response_hook = kwargs .get (
266- "client_response_hook"
267- )
269+ _InstrumentedStarlette ._server_request_hook = kwargs .get ("server_request_hook" )
270+ _InstrumentedStarlette ._client_request_hook = kwargs .get ("client_request_hook" )
271+ _InstrumentedStarlette ._client_response_hook = kwargs .get ("client_response_hook" )
268272 _InstrumentedStarlette ._meter_provider = kwargs .get ("_meter_provider" )
269273
270274 applications .Starlette = _InstrumentedStarlette
271275
272- def _uninstrument (self , ** kwargs ):
276+ def _uninstrument (self , ** kwargs : Any ):
273277 """uninstrumenting all created apps by user"""
274278 for instance in _InstrumentedStarlette ._instrumented_starlette_apps :
275279 self .uninstrument_app (instance )
@@ -278,14 +282,14 @@ def _uninstrument(self, **kwargs):
278282
279283
280284class _InstrumentedStarlette (applications .Starlette ):
281- _tracer_provider = None
282- _meter_provider = None
285+ _tracer_provider : TracerProvider | None = None
286+ _meter_provider : MeterProvider | None = None
283287 _server_request_hook : ServerRequestHook = None
284288 _client_request_hook : ClientRequestHook = None
285289 _client_response_hook : ClientResponseHook = None
286- _instrumented_starlette_apps = set ()
290+ _instrumented_starlette_apps : set [ applications . Starlette ] = set ()
287291
288- def __init__ (self , * args , ** kwargs ):
292+ def __init__ (self , * args : Any , ** kwargs : Any ):
289293 super ().__init__ (* args , ** kwargs )
290294 tracer = get_tracer (
291295 __name__ ,
@@ -318,21 +322,22 @@ def __del__(self):
318322 _InstrumentedStarlette ._instrumented_starlette_apps .remove (self )
319323
320324
321- def _get_route_details (scope ) :
325+ def _get_route_details (scope : dict [ str , Any ]) -> str | None :
322326 """
323- Function to retrieve Starlette route from scope.
327+ Function to retrieve Starlette route from ASGI scope.
324328
325329 TODO: there is currently no way to retrieve http.route from
326330 a starlette application from scope.
327331 See: https://github.com/encode/starlette/pull/804
328332
329333 Args:
330- scope: A Starlette scope
334+ scope: The ASGI scope that contains the Starlette application in the "app" key.
335+
331336 Returns:
332- A string containing the route or None
337+ The path to the route if found, otherwise None.
333338 """
334- app = scope ["app" ]
335- route = None
339+ app = cast ( applications . Starlette , scope ["app" ])
340+ route : str | None = None
336341
337342 for starlette_route in app .routes :
338343 match , _ = starlette_route .matches (scope )
@@ -344,18 +349,18 @@ def _get_route_details(scope):
344349 return route
345350
346351
347- def _get_default_span_details (scope ):
348- """
349- Callback to retrieve span name and attributes from scope.
352+ def _get_default_span_details (scope : dict [str , Any ]) -> tuple [str , dict [str , Any ]]:
353+ """Callback to retrieve span name and attributes from ASGI scope.
350354
351355 Args:
352- scope: A Starlette scope
356+ scope: The ASGI scope that contains the Starlette application in the "app" key.
357+
353358 Returns:
354- A tuple of span name and attributes
359+ A tuple of span name and attributes.
355360 """
356361 route = _get_route_details (scope )
357- method = scope .get ("method" , "" )
358- attributes = {}
362+ method : str = scope .get ("method" , "" )
363+ attributes : dict [ str , Any ] = {}
359364 if route :
360365 attributes [SpanAttributes .HTTP_ROUTE ] = route
361366 if method and route : # http
0 commit comments