@@ -184,9 +184,11 @@ def response_hook(
184184"""
185185
186186import collections .abc
187+ import inspect
187188import io
188189import typing
189190from dataclasses import dataclass
191+ from inspect import BoundArguments
190192from timeit import default_timer
191193from typing import Collection
192194
@@ -287,12 +289,6 @@ class RequestInfo:
287289 ]
288290]
289291
290- _URL_OPEN_ARG_TO_INDEX_MAPPING = {
291- "method" : 0 ,
292- "url" : 1 ,
293- "body" : 2 ,
294- }
295-
296292
297293class URLLib3Instrumentor (BaseInstrumentor ):
298294 def instrumentation_dependencies (self ) -> Collection [str ]:
@@ -428,6 +424,7 @@ def _get_span_name(method: str) -> str:
428424 return method
429425
430426
427+ # pylint: disable=too-many-locals
431428def _instrument (
432429 tracer : Tracer ,
433430 duration_histogram_old : Histogram ,
@@ -445,18 +442,31 @@ def _instrument(
445442 captured_response_headers : typing .Optional [list [str ]] = None ,
446443 sensitive_headers : typing .Optional [list [str ]] = None ,
447444):
448- # pylint: disable=too-many-locals
445+ urlopen_signature = inspect .signature (
446+ urllib3 .connectionpool .HTTPConnectionPool .urlopen
447+ )
448+
449449 def instrumented_urlopen (wrapped , instance , args , kwargs ):
450450 if not is_http_instrumentation_enabled ():
451451 return wrapped (* args , ** kwargs )
452452
453- url = _get_url (instance , args , kwargs , url_filter )
453+ try :
454+ bound_args = urlopen_signature .bind (instance , * args , ** kwargs )
455+ except TypeError :
456+ return wrapped (* args , ** kwargs )
457+
458+ bound_args .apply_defaults ()
459+
460+ method = bound_args .arguments .get ("method" ).upper ()
461+ headers = bound_args .arguments .get ("headers" )
462+ body = bound_args .arguments .get ("body" )
463+ url = _get_url (instance , bound_args , url_filter )
464+
454465 if excluded_urls and excluded_urls .url_disabled (url ):
455466 return wrapped (* args , ** kwargs )
456467
457- method = _get_url_open_arg ("method" , args , kwargs ).upper ()
458- headers = _prepare_headers (kwargs )
459- body = _get_url_open_arg ("body" , args , kwargs )
468+ # avoid modifying original headers on inject
469+ headers = headers .copy () if headers is not None else {}
460470
461471 span_name = _get_span_name (method )
462472 span_attributes = {}
@@ -496,10 +506,12 @@ def instrumented_urlopen(wrapped, instance, args, kwargs):
496506 ),
497507 )
498508 inject (headers )
509+ bound_args .arguments ["headers" ] = headers
510+
499511 # TODO: add error handling to also set exception `error.type` in new semconv
500512 with suppress_http_instrumentation ():
501513 start_time = default_timer ()
502- response = wrapped (* args , ** kwargs )
514+ response = wrapped (* bound_args . args [ 1 :] , ** bound_args . kwargs )
503515 duration_s = default_timer () - start_time
504516 # set http status code based on semconv
505517 metric_attributes = {}
@@ -554,23 +566,12 @@ def instrumented_urlopen(wrapped, instance, args, kwargs):
554566 )
555567
556568
557- def _get_url_open_arg (name : str , args : typing .List , kwargs : typing .Mapping ):
558- arg_idx = _URL_OPEN_ARG_TO_INDEX_MAPPING .get (name )
559- if arg_idx is not None :
560- try :
561- return args [arg_idx ]
562- except IndexError :
563- pass
564- return kwargs .get (name )
565-
566-
567569def _get_url (
568570 instance : urllib3 .connectionpool .HTTPConnectionPool ,
569- args : typing .List ,
570- kwargs : typing .Mapping ,
571+ bound_args : BoundArguments ,
571572 url_filter : _UrlFilterT ,
572573) -> str :
573- url_or_path = _get_url_open_arg ("url" , args , kwargs )
574+ url_or_path = bound_args . arguments . get ("url" )
574575 if not url_or_path .startswith ("/" ):
575576 url = url_or_path
576577 else :
@@ -587,6 +588,7 @@ def _get_url(
587588def _get_body_size (body : object ) -> typing .Optional [int ]:
588589 if body is None :
589590 return 0
591+ # pylint: disable-next=no-member
590592 if isinstance (body , collections .abc .Sized ):
591593 return len (body )
592594 if isinstance (body , io .BytesIO ):
@@ -604,16 +606,6 @@ def _should_append_port(scheme: str, port: typing.Optional[int]) -> bool:
604606 return True
605607
606608
607- def _prepare_headers (urlopen_kwargs : typing .Dict ) -> typing .Dict :
608- headers = urlopen_kwargs .get ("headers" )
609-
610- # avoid modifying original headers on inject
611- headers = headers .copy () if headers is not None else {}
612- urlopen_kwargs ["headers" ] = headers
613-
614- return headers
615-
616-
617609def _set_status_code_attribute (
618610 span : Span ,
619611 status_code : int ,
0 commit comments