prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  ddtrace.Pin() for multiple grpc channels doesn't work
  Thanks for taking the time for reporting an issue!
  
  Before reporting an issue on dd-trace-py, please be sure to provide all
  necessary information.
  
  If you're hitting a bug, make sure that you're using the latest version of this
  library.
  
  ### Which version of dd-trace-py are you using?
  0.38.2
  I didn't find anything related to this issue in the release notes of the releases after this version.
  
  ### Which version of the libraries are you using?
  datadog==0.36.0
  
  ### How can we reproduce your problem?
  Approach 1:
  servers is a list of grpc server addresses
  ```
  for server in servers:
      channel = grpc.insecure_channel(server)
      Pin.override(channel, service=server)
      # Do something with the channel
  ```
  
  Since `Pin.override(grpc.Channel, service=server)` worked with one server, I also tried the following to see how it looks
  Approach 2:
  servers is a list of grpc server addresses
  ```
  for server in servers:
      Pin.override(grpc.Channel, service=server)
      channel = grpc.insecure_channel(server)
      # Do something with the channel
  ```
  
  ### What is the result that you get?
  In Approach 1, Pin.override did not set the service name correctly. Everywhere in Datadog, I could see it as `grpc-client` which is the default value.
  In Approach 2, since I I don't pass the channels corresponding to each server, all servers are overriden by Pin to the final server (probably because it's the last one in the loop)
  
  ### What is the result that you expected?
  ddtrace.Pin() onto multiple grpc channels should work and I should be able to see the correct `service` in Datadog APM traces and Service Map
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `ddtrace/contrib/grpc/patch.py`
  Content:
  ```
  1 import os
  2 
  3 import grpc
  4 
  5 from ddtrace.vendor.wrapt import wrap_function_wrapper as _w
  6 from ddtrace import config, Pin
  7 
  8 from ...utils.wrappers import unwrap as _u
  9 
  10 from . import constants
  11 from .client_interceptor import create_client_interceptor, intercept_channel
  12 from .server_interceptor import create_server_interceptor
  13 
  14 
  15 config._add('grpc_server', dict(
  16     service_name=config._get_service(default=constants.GRPC_SERVICE_SERVER),
  17     distributed_tracing_enabled=True,
  18 ))
  19 
  20 
  21 # Precedence for the service name:
  22 # 1) DD_GRPC_SERVICE if defined; or
  23 # 2) For compatibility, the globally set service + "-grpc-client"; or
  24 # 3) The fall-back "grpc-client"
  25 if "DD_GRPC_SERVICE" in os.environ:
  26     service = os.getenv("DD_GRPC_SERVICE")
  27 elif config._get_service():
  28     service = "{}-{}".format(config._get_service(), constants.GRPC_SERVICE_CLIENT)
  29 else:
  30     service = constants.GRPC_SERVICE_CLIENT
  31 
  32 
  33 # TODO[tbutt]: keeping name for client config unchanged to maintain backwards
  34 # compatibility but should change in future
  35 config._add('grpc', dict(
  36     service_name=service,
  37     distributed_tracing_enabled=True,
  38 ))
  39 
  40 
  41 def patch():
  42     _patch_client()
  43     _patch_server()
  44 
  45 
  46 def unpatch():
  47     _unpatch_client()
  48     _unpatch_server()
  49 
  50 
  51 def _patch_client():
  52     if getattr(constants.GRPC_PIN_MODULE_CLIENT, '__datadog_patch', False):
  53         return
  54     setattr(constants.GRPC_PIN_MODULE_CLIENT, '__datadog_patch', True)
  55 
  56     Pin().onto(constants.GRPC_PIN_MODULE_CLIENT)
  57 
  58     _w('grpc', 'insecure_channel', _client_channel_interceptor)
  59     _w('grpc', 'secure_channel', _client_channel_interceptor)
  60     _w('grpc', 'intercept_channel', intercept_channel)
  61 
  62 
  63 def _unpatch_client():
  64     if not getattr(constants.GRPC_PIN_MODULE_CLIENT, '__datadog_patch', False):
  65         return
  66     setattr(constants.GRPC_PIN_MODULE_CLIENT, '__datadog_patch', False)
  67 
  68     pin = Pin.get_from(constants.GRPC_PIN_MODULE_CLIENT)
  69     if pin:
  70         pin.remove_from(constants.GRPC_PIN_MODULE_CLIENT)
  71 
  72     _u(grpc, 'secure_channel')
  73     _u(grpc, 'insecure_channel')
  74 
  75 
  76 def _patch_server():
  77     if getattr(constants.GRPC_PIN_MODULE_SERVER, '__datadog_patch', False):
  78         return
  79     setattr(constants.GRPC_PIN_MODULE_SERVER, '__datadog_patch', True)
  80 
  81     Pin().onto(constants.GRPC_PIN_MODULE_SERVER)
  82 
  83     _w('grpc', 'server', _server_constructor_interceptor)
  84 
  85 
  86 def _unpatch_server():
  87     if not getattr(constants.GRPC_PIN_MODULE_SERVER, '__datadog_patch', False):
  88         return
  89     setattr(constants.GRPC_PIN_MODULE_SERVER, '__datadog_patch', False)
  90 
  91     pin = Pin.get_from(constants.GRPC_PIN_MODULE_SERVER)
  92     if pin:
  93         pin.remove_from(constants.GRPC_PIN_MODULE_SERVER)
  94 
  95     _u(grpc, 'server')
  96 
  97 
  98 def _client_channel_interceptor(wrapped, instance, args, kwargs):
  99     channel = wrapped(*args, **kwargs)
  100 
  101     pin = Pin.get_from(constants.GRPC_PIN_MODULE_CLIENT)
  102     if not pin or not pin.enabled():
  103         return channel
  104 
  105     (host, port) = _parse_target_from_arguments(args, kwargs)
  106 
  107     interceptor_function = create_client_interceptor(pin, host, port)
  108     return grpc.intercept_channel(channel, interceptor_function)
  109 
  110 
  111 def _server_constructor_interceptor(wrapped, instance, args, kwargs):
  112     # DEV: we clone the pin on the grpc module and configure it for the server
  113     # interceptor
  114 
  115     pin = Pin.get_from(constants.GRPC_PIN_MODULE_SERVER)
  116     if not pin or not pin.enabled():
  117         return wrapped(*args, **kwargs)
  118 
  119     interceptor = create_server_interceptor(pin)
  120 
  121     # DEV: Inject our tracing interceptor first in the list of interceptors
  122     if 'interceptors' in kwargs:
  123         kwargs['interceptors'] = (interceptor,) + tuple(kwargs['interceptors'])
  124     else:
  125         kwargs['interceptors'] = (interceptor,)
  126 
  127     return wrapped(*args, **kwargs)
  128 
  129 
  130 def _parse_target_from_arguments(args, kwargs):
  131     if 'target' in kwargs:
  132         target = kwargs['target']
  133     else:
  134         target = args[0]
  135 
  136     split = target.rsplit(':', 2)
  137 
  138     return (split[0], split[1] if len(split) > 1 else None)
  139
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/ddtrace/contrib/grpc/patch.py b/ddtrace/contrib/grpc/patch.py
  --- a/ddtrace/contrib/grpc/patch.py
  +++ b/ddtrace/contrib/grpc/patch.py
  @@ -98,7 +98,7 @@
   def _client_channel_interceptor(wrapped, instance, args, kwargs):
       channel = wrapped(*args, **kwargs)
   
  -    pin = Pin.get_from(constants.GRPC_PIN_MODULE_CLIENT)
  +    pin = Pin.get_from(channel)
       if not pin or not pin.enabled():
           return channel
  
verification_info: {"golden_diff": "diff --git a/ddtrace/contrib/grpc/patch.py b/ddtrace/contrib/grpc/patch.py\n--- a/ddtrace/contrib/grpc/patch.py\n+++ b/ddtrace/contrib/grpc/patch.py\n@@ -98,7 +98,7 @@\n def _client_channel_interceptor(wrapped, instance, args, kwargs):\n     channel = wrapped(*args, **kwargs)\n \n-    pin = Pin.get_from(constants.GRPC_PIN_MODULE_CLIENT)\n+    pin = Pin.get_from(channel)\n     if not pin or not pin.enabled():\n         return channel\n", "issue": "ddtrace.Pin() for multiple grpc channels doesn't work\nThanks for taking the time for reporting an issue!\r\n\r\nBefore reporting an issue on dd-trace-py, please be sure to provide all\r\nnecessary information.\r\n\r\nIf you're hitting a bug, make sure that you're using the latest version of this\r\nlibrary.\r\n\r\n### Which version of dd-trace-py are you using?\r\n0.38.2\r\nI didn't find anything related to this issue in the release notes of the releases after this version.\r\n\r\n### Which version of the libraries are you using?\r\ndatadog==0.36.0\r\n\r\n### How can we reproduce your problem?\r\nApproach 1:\r\nservers is a list of grpc server addresses\r\n```\r\nfor server in servers:\r\n    channel = grpc.insecure_channel(server)\r\n    Pin.override(channel, service=server)\r\n    # Do something with the channel\r\n```\r\n\r\nSince `Pin.override(grpc.Channel, service=server)` worked with one server, I also tried the following to see how it looks\r\nApproach 2:\r\nservers is a list of grpc server addresses\r\n```\r\nfor server in servers:\r\n    Pin.override(grpc.Channel, service=server)\r\n    channel = grpc.insecure_channel(server)\r\n    # Do something with the channel\r\n```\r\n\r\n### What is the result that you get?\r\nIn Approach 1, Pin.override did not set the service name correctly. Everywhere in Datadog, I could see it as `grpc-client` which is the default value.\r\nIn Approach 2, since I I don't pass the channels corresponding to each server, all servers are overriden by Pin to the final server (probably because it's the last one in the loop)\r\n\r\n### What is the result that you expected?\r\nddtrace.Pin() onto multiple grpc channels should work and I should be able to see the correct `service` in Datadog APM traces and Service Map\n", "before_files": [{"content": "import os\n\nimport grpc\n\nfrom ddtrace.vendor.wrapt import wrap_function_wrapper as _w\nfrom ddtrace import config, Pin\n\nfrom ...utils.wrappers import unwrap as _u\n\nfrom . import constants\nfrom .client_interceptor import create_client_interceptor, intercept_channel\nfrom .server_interceptor import create_server_interceptor\n\n\nconfig._add('grpc_server', dict(\n    service_name=config._get_service(default=constants.GRPC_SERVICE_SERVER),\n    distributed_tracing_enabled=True,\n))\n\n\n# Precedence for the service name:\n# 1) DD_GRPC_SERVICE if defined; or\n# 2) For compatibility, the globally set service + \"-grpc-client\"; or\n# 3) The fall-back \"grpc-client\"\nif \"DD_GRPC_SERVICE\" in os.environ:\n    service = os.getenv(\"DD_GRPC_SERVICE\")\nelif config._get_service():\n    service = \"{}-{}\".format(config._get_service(), constants.GRPC_SERVICE_CLIENT)\nelse:\n    service = constants.GRPC_SERVICE_CLIENT\n\n\n# TODO[tbutt]: keeping name for client config unchanged to maintain backwards\n# compatibility but should change in future\nconfig._add('grpc', dict(\n    service_name=service,\n    distributed_tracing_enabled=True,\n))\n\n\ndef patch():\n    _patch_client()\n    _patch_server()\n\n\ndef unpatch():\n    _unpatch_client()\n    _unpatch_server()\n\n\ndef _patch_client():\n    if getattr(constants.GRPC_PIN_MODULE_CLIENT, '__datadog_patch', False):\n        return\n    setattr(constants.GRPC_PIN_MODULE_CLIENT, '__datadog_patch', True)\n\n    Pin().onto(constants.GRPC_PIN_MODULE_CLIENT)\n\n    _w('grpc', 'insecure_channel', _client_channel_interceptor)\n    _w('grpc', 'secure_channel', _client_channel_interceptor)\n    _w('grpc', 'intercept_channel', intercept_channel)\n\n\ndef _unpatch_client():\n    if not getattr(constants.GRPC_PIN_MODULE_CLIENT, '__datadog_patch', False):\n        return\n    setattr(constants.GRPC_PIN_MODULE_CLIENT, '__datadog_patch', False)\n\n    pin = Pin.get_from(constants.GRPC_PIN_MODULE_CLIENT)\n    if pin:\n        pin.remove_from(constants.GRPC_PIN_MODULE_CLIENT)\n\n    _u(grpc, 'secure_channel')\n    _u(grpc, 'insecure_channel')\n\n\ndef _patch_server():\n    if getattr(constants.GRPC_PIN_MODULE_SERVER, '__datadog_patch', False):\n        return\n    setattr(constants.GRPC_PIN_MODULE_SERVER, '__datadog_patch', True)\n\n    Pin().onto(constants.GRPC_PIN_MODULE_SERVER)\n\n    _w('grpc', 'server', _server_constructor_interceptor)\n\n\ndef _unpatch_server():\n    if not getattr(constants.GRPC_PIN_MODULE_SERVER, '__datadog_patch', False):\n        return\n    setattr(constants.GRPC_PIN_MODULE_SERVER, '__datadog_patch', False)\n\n    pin = Pin.get_from(constants.GRPC_PIN_MODULE_SERVER)\n    if pin:\n        pin.remove_from(constants.GRPC_PIN_MODULE_SERVER)\n\n    _u(grpc, 'server')\n\n\ndef _client_channel_interceptor(wrapped, instance, args, kwargs):\n    channel = wrapped(*args, **kwargs)\n\n    pin = Pin.get_from(constants.GRPC_PIN_MODULE_CLIENT)\n    if not pin or not pin.enabled():\n        return channel\n\n    (host, port) = _parse_target_from_arguments(args, kwargs)\n\n    interceptor_function = create_client_interceptor(pin, host, port)\n    return grpc.intercept_channel(channel, interceptor_function)\n\n\ndef _server_constructor_interceptor(wrapped, instance, args, kwargs):\n    # DEV: we clone the pin on the grpc module and configure it for the server\n    # interceptor\n\n    pin = Pin.get_from(constants.GRPC_PIN_MODULE_SERVER)\n    if not pin or not pin.enabled():\n        return wrapped(*args, **kwargs)\n\n    interceptor = create_server_interceptor(pin)\n\n    # DEV: Inject our tracing interceptor first in the list of interceptors\n    if 'interceptors' in kwargs:\n        kwargs['interceptors'] = (interceptor,) + tuple(kwargs['interceptors'])\n    else:\n        kwargs['interceptors'] = (interceptor,)\n\n    return wrapped(*args, **kwargs)\n\n\ndef _parse_target_from_arguments(args, kwargs):\n    if 'target' in kwargs:\n        target = kwargs['target']\n    else:\n        target = args[0]\n\n    split = target.rsplit(':', 2)\n\n    return (split[0], split[1] if len(split) > 1 else None)\n", "path": "ddtrace/contrib/grpc/patch.py"}], "after_files": [{"content": "import os\n\nimport grpc\n\nfrom ddtrace.vendor.wrapt import wrap_function_wrapper as _w\nfrom ddtrace import config, Pin\n\nfrom ...utils.wrappers import unwrap as _u\n\nfrom . import constants\nfrom .client_interceptor import create_client_interceptor, intercept_channel\nfrom .server_interceptor import create_server_interceptor\n\n\nconfig._add('grpc_server', dict(\n    service_name=config._get_service(default=constants.GRPC_SERVICE_SERVER),\n    distributed_tracing_enabled=True,\n))\n\n\n# Precedence for the service name:\n# 1) DD_GRPC_SERVICE if defined; or\n# 2) For compatibility, the globally set service + \"-grpc-client\"; or\n# 3) The fall-back \"grpc-client\"\nif \"DD_GRPC_SERVICE\" in os.environ:\n    service = os.getenv(\"DD_GRPC_SERVICE\")\nelif config._get_service():\n    service = \"{}-{}\".format(config._get_service(), constants.GRPC_SERVICE_CLIENT)\nelse:\n    service = constants.GRPC_SERVICE_CLIENT\n\n\n# TODO[tbutt]: keeping name for client config unchanged to maintain backwards\n# compatibility but should change in future\nconfig._add('grpc', dict(\n    service_name=service,\n    distributed_tracing_enabled=True,\n))\n\n\ndef patch():\n    _patch_client()\n    _patch_server()\n\n\ndef unpatch():\n    _unpatch_client()\n    _unpatch_server()\n\n\ndef _patch_client():\n    if getattr(constants.GRPC_PIN_MODULE_CLIENT, '__datadog_patch', False):\n        return\n    setattr(constants.GRPC_PIN_MODULE_CLIENT, '__datadog_patch', True)\n\n    Pin().onto(constants.GRPC_PIN_MODULE_CLIENT)\n\n    _w('grpc', 'insecure_channel', _client_channel_interceptor)\n    _w('grpc', 'secure_channel', _client_channel_interceptor)\n    _w('grpc', 'intercept_channel', intercept_channel)\n\n\ndef _unpatch_client():\n    if not getattr(constants.GRPC_PIN_MODULE_CLIENT, '__datadog_patch', False):\n        return\n    setattr(constants.GRPC_PIN_MODULE_CLIENT, '__datadog_patch', False)\n\n    pin = Pin.get_from(constants.GRPC_PIN_MODULE_CLIENT)\n    if pin:\n        pin.remove_from(constants.GRPC_PIN_MODULE_CLIENT)\n\n    _u(grpc, 'secure_channel')\n    _u(grpc, 'insecure_channel')\n\n\ndef _patch_server():\n    if getattr(constants.GRPC_PIN_MODULE_SERVER, '__datadog_patch', False):\n        return\n    setattr(constants.GRPC_PIN_MODULE_SERVER, '__datadog_patch', True)\n\n    Pin().onto(constants.GRPC_PIN_MODULE_SERVER)\n\n    _w('grpc', 'server', _server_constructor_interceptor)\n\n\ndef _unpatch_server():\n    if not getattr(constants.GRPC_PIN_MODULE_SERVER, '__datadog_patch', False):\n        return\n    setattr(constants.GRPC_PIN_MODULE_SERVER, '__datadog_patch', False)\n\n    pin = Pin.get_from(constants.GRPC_PIN_MODULE_SERVER)\n    if pin:\n        pin.remove_from(constants.GRPC_PIN_MODULE_SERVER)\n\n    _u(grpc, 'server')\n\n\ndef _client_channel_interceptor(wrapped, instance, args, kwargs):\n    channel = wrapped(*args, **kwargs)\n\n    pin = Pin.get_from(channel)\n    if not pin or not pin.enabled():\n        return channel\n\n    (host, port) = _parse_target_from_arguments(args, kwargs)\n\n    interceptor_function = create_client_interceptor(pin, host, port)\n    return grpc.intercept_channel(channel, interceptor_function)\n\n\ndef _server_constructor_interceptor(wrapped, instance, args, kwargs):\n    # DEV: we clone the pin on the grpc module and configure it for the server\n    # interceptor\n\n    pin = Pin.get_from(constants.GRPC_PIN_MODULE_SERVER)\n    if not pin or not pin.enabled():\n        return wrapped(*args, **kwargs)\n\n    interceptor = create_server_interceptor(pin)\n\n    # DEV: Inject our tracing interceptor first in the list of interceptors\n    if 'interceptors' in kwargs:\n        kwargs['interceptors'] = (interceptor,) + tuple(kwargs['interceptors'])\n    else:\n        kwargs['interceptors'] = (interceptor,)\n\n    return wrapped(*args, **kwargs)\n\n\ndef _parse_target_from_arguments(args, kwargs):\n    if 'target' in kwargs:\n        target = kwargs['target']\n    else:\n        target = args[0]\n\n    split = target.rsplit(':', 2)\n\n    return (split[0], split[1] if len(split) > 1 else None)\n", "path": "ddtrace/contrib/grpc/patch.py"}]}