@@ -170,7 +170,9 @@ def client_response_hook(span: Span, scope: dict[str, Any], message: dict[str, A
170
170
---
171
171
"""
172
172
173
- from typing import Collection
173
+ from __future__ import annotations
174
+
175
+ from typing import TYPE_CHECKING , Any , Collection , cast
174
176
175
177
from starlette import applications
176
178
from starlette .routing import Match
@@ -184,18 +186,29 @@ def client_response_hook(span: Span, scope: dict[str, Any], message: dict[str, A
184
186
from opentelemetry .instrumentation .instrumentor import BaseInstrumentor
185
187
from opentelemetry .instrumentation .starlette .package import _instruments
186
188
from opentelemetry .instrumentation .starlette .version import __version__
187
- from opentelemetry .metrics import get_meter
189
+ from opentelemetry .metrics import MeterProvider , get_meter
188
190
from opentelemetry .semconv .trace import SpanAttributes
189
- from opentelemetry .trace import get_tracer
191
+ from opentelemetry .trace import TracerProvider , get_tracer
190
192
from opentelemetry .util .http import get_excluded_urls
191
193
194
+ if TYPE_CHECKING :
195
+ from typing import TypedDict , Unpack
196
+
197
+ class InstrumentKwargs (TypedDict , total = False ):
198
+ tracer_provider : TracerProvider
199
+ meter_provider : MeterProvider
200
+ server_request_hook : ServerRequestHook
201
+ client_request_hook : ClientRequestHook
202
+ client_response_hook : ClientResponseHook
203
+
204
+
192
205
_excluded_urls = get_excluded_urls ("STARLETTE" )
193
206
194
207
195
208
class StarletteInstrumentor (BaseInstrumentor ):
196
- """An instrumentor for starlette
209
+ """An instrumentor for Starlette.
197
210
198
- See `BaseInstrumentor`
211
+ See `BaseInstrumentor`.
199
212
"""
200
213
201
214
_original_starlette = None
@@ -206,8 +219,8 @@ def instrument_app(
206
219
server_request_hook : ServerRequestHook = None ,
207
220
client_request_hook : ClientRequestHook = None ,
208
221
client_response_hook : ClientResponseHook = None ,
209
- meter_provider = None ,
210
- tracer_provider = None ,
222
+ meter_provider : MeterProvider | None = None ,
223
+ tracer_provider : TracerProvider | None = None ,
211
224
):
212
225
"""Instrument an uninstrumented Starlette application."""
213
226
tracer = get_tracer (
@@ -253,7 +266,7 @@ def uninstrument_app(app: applications.Starlette):
253
266
def instrumentation_dependencies (self ) -> Collection [str ]:
254
267
return _instruments
255
268
256
- def _instrument (self , ** kwargs ):
269
+ def _instrument (self , ** kwargs : Unpack [ InstrumentKwargs ] ):
257
270
self ._original_starlette = applications .Starlette
258
271
_InstrumentedStarlette ._tracer_provider = kwargs .get ("tracer_provider" )
259
272
_InstrumentedStarlette ._server_request_hook = kwargs .get (
@@ -269,7 +282,7 @@ def _instrument(self, **kwargs):
269
282
270
283
applications .Starlette = _InstrumentedStarlette
271
284
272
- def _uninstrument (self , ** kwargs ):
285
+ def _uninstrument (self , ** kwargs : Any ):
273
286
"""uninstrumenting all created apps by user"""
274
287
for instance in _InstrumentedStarlette ._instrumented_starlette_apps :
275
288
self .uninstrument_app (instance )
@@ -278,14 +291,14 @@ def _uninstrument(self, **kwargs):
278
291
279
292
280
293
class _InstrumentedStarlette (applications .Starlette ):
281
- _tracer_provider = None
282
- _meter_provider = None
294
+ _tracer_provider : TracerProvider | None = None
295
+ _meter_provider : MeterProvider | None = None
283
296
_server_request_hook : ServerRequestHook = None
284
297
_client_request_hook : ClientRequestHook = None
285
298
_client_response_hook : ClientResponseHook = None
286
- _instrumented_starlette_apps = set ()
299
+ _instrumented_starlette_apps : set [ applications . Starlette ] = set ()
287
300
288
- def __init__ (self , * args , ** kwargs ):
301
+ def __init__ (self , * args : Any , ** kwargs : Any ):
289
302
super ().__init__ (* args , ** kwargs )
290
303
tracer = get_tracer (
291
304
__name__ ,
@@ -318,21 +331,22 @@ def __del__(self):
318
331
_InstrumentedStarlette ._instrumented_starlette_apps .remove (self )
319
332
320
333
321
- def _get_route_details (scope ) :
334
+ def _get_route_details (scope : dict [ str , Any ]) -> str | None :
322
335
"""
323
- Function to retrieve Starlette route from scope.
336
+ Function to retrieve Starlette route from ASGI scope.
324
337
325
338
TODO: there is currently no way to retrieve http.route from
326
339
a starlette application from scope.
327
340
See: https://github.com/encode/starlette/pull/804
328
341
329
342
Args:
330
- scope: A Starlette scope
343
+ scope: The ASGI scope that contains the Starlette application in the "app" key.
344
+
331
345
Returns:
332
- A string containing the route or None
346
+ The path to the route if found, otherwise None.
333
347
"""
334
- app = scope ["app" ]
335
- route = None
348
+ app = cast ( applications . Starlette , scope ["app" ])
349
+ route : str | None = None
336
350
337
351
for starlette_route in app .routes :
338
352
match , _ = starlette_route .matches (scope )
@@ -344,18 +358,20 @@ def _get_route_details(scope):
344
358
return route
345
359
346
360
347
- def _get_default_span_details (scope ):
348
- """
349
- Callback to retrieve span name and attributes from scope.
361
+ def _get_default_span_details (
362
+ scope : dict [str , Any ],
363
+ ) -> tuple [str , dict [str , Any ]]:
364
+ """Callback to retrieve span name and attributes from ASGI scope.
350
365
351
366
Args:
352
- scope: A Starlette scope
367
+ scope: The ASGI scope that contains the Starlette application in the "app" key.
368
+
353
369
Returns:
354
- A tuple of span name and attributes
370
+ A tuple of span name and attributes.
355
371
"""
356
372
route = _get_route_details (scope )
357
- method = scope .get ("method" , "" )
358
- attributes = {}
373
+ method : str = scope .get ("method" , "" )
374
+ attributes : dict [ str , Any ] = {}
359
375
if route :
360
376
attributes [SpanAttributes .HTTP_ROUTE ] = route
361
377
if method and route : # http
0 commit comments