We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
opentelemetry-instrumentation-grpc fails if client channel is used as a context manager
**Describe your environment**

Verified on python 3.8.6 with opentelemetry 0.17b0

**Steps to reproduce**

Use a gRPC channel as a context manager.

For instance, this happens if you add instrumentation to the [gRPC helloworld example client](https://github.com/grpc/grpc/tree/026046d84301999936a759c1779669c04c6ffaaa/examples/python/helloworld):

```py
# Copyright 2015 gRPC authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""The Python implementation of the GRPC helloworld.Greeter client."""

from __future__ import print_function
import logging

import grpc

from opentelemetry import trace
from opentelemetry.instrumentation.grpc import GrpcInstrumentorClient
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import (
    ConsoleSpanExporter,
    SimpleExportSpanProcessor,
)

import helloworld_pb2
import helloworld_pb2_grpc

trace.set_tracer_provider(TracerProvider())
trace.get_tracer_provider().add_span_processor(
    SimpleExportSpanProcessor(ConsoleSpanExporter())
)

GrpcInstrumentorClient().instrument()


def run():
    # NOTE(gRPC Python Team): .close() is possible on a channel and should be
    # used in circumstances in which the with statement does not fit the needs
    # of the code.
    with grpc.insecure_channel('localhost:50051') as channel:
        stub = helloworld_pb2_grpc.GreeterStub(channel)
        response = stub.SayHello(helloworld_pb2.HelloRequest(name='you'))
        print("Greeter client received: " + response.message)


if __name__ == '__main__':
    logging.basicConfig()
    run()
```

**What is the expected behavior?**

The channel should be usable as normal, except now with instrumentation

**What is the actual behavior?**

```
❯ python greeter_client.py
Traceback (most recent call last):
  File "greeter_client.py", line 52, in <module>
    run()
  File "greeter_client.py", line 44, in run
    with grpc.insecure_channel('localhost:50051') as channel:
  File "/home/isobel/workspace/tmp/otlp/grpc/lib/python3.8/site-packages/opentelemetry/instrumentation/grpc/grpcext/_interceptor.py", line 235, in __enter__
    raise NotImplementedError()
NotImplementedError
```

**Additional context**

LMK and I can make a PR

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/grpcext/_interceptor.py`
Content:
```
1 # Copyright The OpenTelemetry Authors
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #     http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 
15 # pylint:disable=relative-beyond-top-level
16 # pylint:disable=arguments-differ
17 # pylint:disable=no-member
18 # pylint:disable=signature-differs
19 
20 """Implementation of gRPC Python interceptors."""
21 
22 
23 import collections
24 
25 import grpc
26 
27 from opentelemetry.instrumentation.grpc import grpcext
28 
29 
30 class _UnaryClientInfo(
31     collections.namedtuple("_UnaryClientInfo", ("full_method", "timeout"))
32 ):
33     pass
34 
35 
36 class _StreamClientInfo(
37     collections.namedtuple(
38         "_StreamClientInfo",
39         ("full_method", "is_client_stream", "is_server_stream", "timeout"),
40     )
41 ):
42     pass
43 
44 
45 class _InterceptorUnaryUnaryMultiCallable(grpc.UnaryUnaryMultiCallable):
46     def __init__(self, method, base_callable, interceptor):
47         self._method = method
48         self._base_callable = base_callable
49         self._interceptor = interceptor
50 
51     def __call__(self, request, timeout=None, metadata=None, credentials=None):
52         def invoker(request, metadata):
53             return self._base_callable(request, timeout, metadata, credentials)
54 
55         client_info = _UnaryClientInfo(self._method, timeout)
56         return self._interceptor.intercept_unary(
57             request, metadata, client_info, invoker
58         )
59 
60     def with_call(
61         self, request, timeout=None, metadata=None, credentials=None
62     ):
63         def invoker(request, metadata):
64             return self._base_callable.with_call(
65                 request, timeout, metadata, credentials
66             )
67 
68         client_info = _UnaryClientInfo(self._method, timeout)
69         return self._interceptor.intercept_unary(
70             request, metadata, client_info, invoker
71         )
72 
73     def future(self, request, timeout=None, metadata=None, credentials=None):
74         def invoker(request, metadata):
75             return self._base_callable.future(
76                 request, timeout, metadata, credentials
77             )
78 
79         client_info = _UnaryClientInfo(self._method, timeout)
80         return self._interceptor.intercept_unary(
81             request, metadata, client_info, invoker
82         )
83 
84 
85 class _InterceptorUnaryStreamMultiCallable(grpc.UnaryStreamMultiCallable):
86     def __init__(self, method, base_callable, interceptor):
87         self._method = method
88         self._base_callable = base_callable
89         self._interceptor = interceptor
90 
91     def __call__(self, request, timeout=None, metadata=None, credentials=None):
92         def invoker(request, metadata):
93             return self._base_callable(request, timeout, metadata, credentials)
94 
95         client_info = _StreamClientInfo(self._method, False, True, timeout)
96         return self._interceptor.intercept_stream(
97             request, metadata, client_info, invoker
98         )
99 
100 
101 class _InterceptorStreamUnaryMultiCallable(grpc.StreamUnaryMultiCallable):
102     def __init__(self, method, base_callable, interceptor):
103         self._method = method
104         self._base_callable = base_callable
105         self._interceptor = interceptor
106 
107     def __call__(
108         self, request_iterator, timeout=None, metadata=None, credentials=None
109     ):
110         def invoker(request_iterator, metadata):
111             return self._base_callable(
112                 request_iterator, timeout, metadata, credentials
113             )
114 
115         client_info = _StreamClientInfo(self._method, True, False, timeout)
116         return self._interceptor.intercept_stream(
117             request_iterator, metadata, client_info, invoker
118         )
119 
120     def with_call(
121         self, request_iterator, timeout=None, metadata=None, credentials=None
122     ):
123         def invoker(request_iterator, metadata):
124             return self._base_callable.with_call(
125                 request_iterator, timeout, metadata, credentials
126             )
127 
128         client_info = _StreamClientInfo(self._method, True, False, timeout)
129         return self._interceptor.intercept_stream(
130             request_iterator, metadata, client_info, invoker
131         )
132 
133     def future(
134         self, request_iterator, timeout=None, metadata=None, credentials=None
135     ):
136         def invoker(request_iterator, metadata):
137             return self._base_callable.future(
138                 request_iterator, timeout, metadata, credentials
139             )
140 
141         client_info = _StreamClientInfo(self._method, True, False, timeout)
142         return self._interceptor.intercept_stream(
143             request_iterator, metadata, client_info, invoker
144         )
145 
146 
147 class _InterceptorStreamStreamMultiCallable(grpc.StreamStreamMultiCallable):
148     def __init__(self, method, base_callable, interceptor):
149         self._method = method
150         self._base_callable = base_callable
151         self._interceptor = interceptor
152 
153     def __call__(
154         self, request_iterator, timeout=None, metadata=None, credentials=None
155     ):
156         def invoker(request_iterator, metadata):
157             return self._base_callable(
158                 request_iterator, timeout, metadata, credentials
159             )
160 
161         client_info = _StreamClientInfo(self._method, True, True, timeout)
162         return self._interceptor.intercept_stream(
163             request_iterator, metadata, client_info, invoker
164         )
165 
166 
167 class _InterceptorChannel(grpc.Channel):
168     def __init__(self, channel, interceptor):
169         self._channel = channel
170         self._interceptor = interceptor
171 
172     def subscribe(self, *args, **kwargs):
173         self._channel.subscribe(*args, **kwargs)
174 
175     def unsubscribe(self, *args, **kwargs):
176         self._channel.unsubscribe(*args, **kwargs)
177 
178     def unary_unary(
179         self, method, request_serializer=None, response_deserializer=None
180     ):
181         base_callable = self._channel.unary_unary(
182             method, request_serializer, response_deserializer
183         )
184         if isinstance(self._interceptor, grpcext.UnaryClientInterceptor):
185             return _InterceptorUnaryUnaryMultiCallable(
186                 method, base_callable, self._interceptor
187             )
188         return base_callable
189 
190     def unary_stream(
191         self, method, request_serializer=None, response_deserializer=None
192     ):
193         base_callable = self._channel.unary_stream(
194             method, request_serializer, response_deserializer
195         )
196         if isinstance(self._interceptor, grpcext.StreamClientInterceptor):
197             return _InterceptorUnaryStreamMultiCallable(
198                 method, base_callable, self._interceptor
199             )
200         return base_callable
201 
202     def stream_unary(
203         self, method, request_serializer=None, response_deserializer=None
204     ):
205         base_callable = self._channel.stream_unary(
206             method, request_serializer, response_deserializer
207         )
208         if isinstance(self._interceptor, grpcext.StreamClientInterceptor):
209             return _InterceptorStreamUnaryMultiCallable(
210                 method, base_callable, self._interceptor
211             )
212         return base_callable
213 
214     def stream_stream(
215         self, method, request_serializer=None, response_deserializer=None
216     ):
217         base_callable = self._channel.stream_stream(
218             method, request_serializer, response_deserializer
219         )
220         if isinstance(self._interceptor, grpcext.StreamClientInterceptor):
221             return _InterceptorStreamStreamMultiCallable(
222                 method, base_callable, self._interceptor
223             )
224         return base_callable
225 
226     def close(self):
227         if not hasattr(self._channel, "close"):
228             raise RuntimeError(
229                 "close() is not supported with the installed version of grpcio"
230             )
231         self._channel.close()
232 
233     def __enter__(self):
234         """Enters the runtime context related to the channel object."""
235         raise NotImplementedError()
236 
237     def __exit__(self, exc_type, exc_val, exc_tb):
238         """Exits the runtime context related to the channel object."""
239         raise NotImplementedError()
240 
241 
242 def intercept_channel(channel, *interceptors):
243     result = channel
244     for interceptor in interceptors:
245         if not isinstance(
246             interceptor, grpcext.UnaryClientInterceptor
247         ) and not isinstance(interceptor, grpcext.StreamClientInterceptor):
248             raise TypeError(
249                 "interceptor must be either a "
250                 "grpcext.UnaryClientInterceptor or a "
251                 "grpcext.StreamClientInterceptor"
252             )
253         result = _InterceptorChannel(result, interceptor)
254     return result
255
```
--- 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()

```

diff --git a/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/grpcext/_interceptor.py b/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/grpcext/_interceptor.py
--- a/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/grpcext/_interceptor.py
+++ b/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/grpcext/_interceptor.py
@@ -232,11 +232,11 @@
 
     def __enter__(self):
         """Enters the runtime context related to the channel object."""
-        raise NotImplementedError()
+        return self
 
     def __exit__(self, exc_type, exc_val, exc_tb):
         """Exits the runtime context related to the channel object."""
-        raise NotImplementedError()
+        self.close()
 
 
 def intercept_channel(channel, *interceptors):

{"golden_diff": "diff --git a/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/grpcext/_interceptor.py b/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/grpcext/_interceptor.py\n--- a/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/grpcext/_interceptor.py\n+++ b/instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/grpcext/_interceptor.py\n@@ -232,11 +232,11 @@\n \n     def __enter__(self):\n         \"\"\"Enters the runtime context related to the channel object.\"\"\"\n-        raise NotImplementedError()\n+        return self\n \n     def __exit__(self, exc_type, exc_val, exc_tb):\n         \"\"\"Exits the runtime context related to the channel object.\"\"\"\n-        raise NotImplementedError()\n+        self.close()\n \n \n def intercept_channel(channel, *interceptors):\n", "issue": "opentelemetry-instrumentation-grpc fails if client channel is used as a context manager\n**Describe your environment**\r\n\r\nVerified on python 3.8.6 with opentelemetry 0.17b0\r\n\r\n**Steps to reproduce**\r\n\r\nUse a gRPC channel as a context manager.\r\n\r\nFor instance, this happens if you add instrumentation to the [gRPC helloworld example client](https://github.com/grpc/grpc/tree/026046d84301999936a759c1779669c04c6ffaaa/examples/python/helloworld):\r\n\r\n```py\r\n# Copyright 2015 gRPC authors.\r\n#\r\n# Licensed under the Apache License, Version 2.0 (the \"License\");\r\n# you may not use this file except in compliance with the License.\r\n# You may obtain a copy of the License at\r\n#\r\n#     http://www.apache.org/licenses/LICENSE-2.0\r\n#\r\n# Unless required by applicable law or agreed to in writing, software\r\n# distributed under the License is distributed on an \"AS IS\" BASIS,\r\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n# See the License for the specific language governing permissions and\r\n# limitations under the License.\r\n\"\"\"The Python implementation of the GRPC helloworld.Greeter client.\"\"\"\r\n\r\nfrom __future__ import print_function\r\nimport logging\r\n\r\nimport grpc\r\n\r\nfrom opentelemetry import trace\r\nfrom opentelemetry.instrumentation.grpc import GrpcInstrumentorClient\r\nfrom opentelemetry.sdk.trace import TracerProvider\r\nfrom opentelemetry.sdk.trace.export import (\r\n    ConsoleSpanExporter,\r\n    SimpleExportSpanProcessor,\r\n)\r\n\r\nimport helloworld_pb2\r\nimport helloworld_pb2_grpc\r\n\r\ntrace.set_tracer_provider(TracerProvider())\r\ntrace.get_tracer_provider().add_span_processor(\r\n    SimpleExportSpanProcessor(ConsoleSpanExporter())\r\n)\r\n\r\nGrpcInstrumentorClient().instrument()\r\n\r\n\r\ndef run():\r\n    # NOTE(gRPC Python Team): .close() is possible on a channel and should be\r\n    # used in circumstances in which the with statement does not fit the needs\r\n    # of the code.\r\n    with grpc.insecure_channel('localhost:50051') as channel:\r\n        stub = helloworld_pb2_grpc.GreeterStub(channel)\r\n        response = stub.SayHello(helloworld_pb2.HelloRequest(name='you'))\r\n        print(\"Greeter client received: \" + response.message)\r\n\r\n\r\nif __name__ == '__main__':\r\n    logging.basicConfig()\r\n    run()\r\n```\r\n\r\n**What is the expected behavior?**\r\n\r\nThe channel should be usable as normal, except now with instrumentation\r\n\r\n**What is the actual behavior?**\r\n\r\n```\r\n\u276f python greeter_client.py\r\nTraceback (most recent call last):\r\n  File \"greeter_client.py\", line 52, in <module>\r\n    run()\r\n  File \"greeter_client.py\", line 44, in run\r\n    with grpc.insecure_channel('localhost:50051') as channel:\r\n  File \"/home/isobel/workspace/tmp/otlp/grpc/lib/python3.8/site-packages/opentelemetry/instrumentation/grpc/grpcext/_interceptor.py\", line 235, in __enter__\r\n    raise NotImplementedError()\r\nNotImplementedError\r\n```\r\n\r\n**Additional context**\r\n\r\nLMK and I can make a PR\n", "before_files": [{"content": "# Copyright The OpenTelemetry Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# pylint:disable=relative-beyond-top-level\n# pylint:disable=arguments-differ\n# pylint:disable=no-member\n# pylint:disable=signature-differs\n\n\"\"\"Implementation of gRPC Python interceptors.\"\"\"\n\n\nimport collections\n\nimport grpc\n\nfrom opentelemetry.instrumentation.grpc import grpcext\n\n\nclass _UnaryClientInfo(\n    collections.namedtuple(\"_UnaryClientInfo\", (\"full_method\", \"timeout\"))\n):\n    pass\n\n\nclass _StreamClientInfo(\n    collections.namedtuple(\n        \"_StreamClientInfo\",\n        (\"full_method\", \"is_client_stream\", \"is_server_stream\", \"timeout\"),\n    )\n):\n    pass\n\n\nclass _InterceptorUnaryUnaryMultiCallable(grpc.UnaryUnaryMultiCallable):\n    def __init__(self, method, base_callable, interceptor):\n        self._method = method\n        self._base_callable = base_callable\n        self._interceptor = interceptor\n\n    def __call__(self, request, timeout=None, metadata=None, credentials=None):\n        def invoker(request, metadata):\n            return self._base_callable(request, timeout, metadata, credentials)\n\n        client_info = _UnaryClientInfo(self._method, timeout)\n        return self._interceptor.intercept_unary(\n            request, metadata, client_info, invoker\n        )\n\n    def with_call(\n        self, request, timeout=None, metadata=None, credentials=None\n    ):\n        def invoker(request, metadata):\n            return self._base_callable.with_call(\n                request, timeout, metadata, credentials\n            )\n\n        client_info = _UnaryClientInfo(self._method, timeout)\n        return self._interceptor.intercept_unary(\n            request, metadata, client_info, invoker\n        )\n\n    def future(self, request, timeout=None, metadata=None, credentials=None):\n        def invoker(request, metadata):\n            return self._base_callable.future(\n                request, timeout, metadata, credentials\n            )\n\n        client_info = _UnaryClientInfo(self._method, timeout)\n        return self._interceptor.intercept_unary(\n            request, metadata, client_info, invoker\n        )\n\n\nclass _InterceptorUnaryStreamMultiCallable(grpc.UnaryStreamMultiCallable):\n    def __init__(self, method, base_callable, interceptor):\n        self._method = method\n        self._base_callable = base_callable\n        self._interceptor = interceptor\n\n    def __call__(self, request, timeout=None, metadata=None, credentials=None):\n        def invoker(request, metadata):\n            return self._base_callable(request, timeout, metadata, credentials)\n\n        client_info = _StreamClientInfo(self._method, False, True, timeout)\n        return self._interceptor.intercept_stream(\n            request, metadata, client_info, invoker\n        )\n\n\nclass _InterceptorStreamUnaryMultiCallable(grpc.StreamUnaryMultiCallable):\n    def __init__(self, method, base_callable, interceptor):\n        self._method = method\n        self._base_callable = base_callable\n        self._interceptor = interceptor\n\n    def __call__(\n        self, request_iterator, timeout=None, metadata=None, credentials=None\n    ):\n        def invoker(request_iterator, metadata):\n            return self._base_callable(\n                request_iterator, timeout, metadata, credentials\n            )\n\n        client_info = _StreamClientInfo(self._method, True, False, timeout)\n        return self._interceptor.intercept_stream(\n            request_iterator, metadata, client_info, invoker\n        )\n\n    def with_call(\n        self, request_iterator, timeout=None, metadata=None, credentials=None\n    ):\n        def invoker(request_iterator, metadata):\n            return self._base_callable.with_call(\n                request_iterator, timeout, metadata, credentials\n            )\n\n        client_info = _StreamClientInfo(self._method, True, False, timeout)\n        return self._interceptor.intercept_stream(\n            request_iterator, metadata, client_info, invoker\n        )\n\n    def future(\n        self, request_iterator, timeout=None, metadata=None, credentials=None\n    ):\n        def invoker(request_iterator, metadata):\n            return self._base_callable.future(\n                request_iterator, timeout, metadata, credentials\n            )\n\n        client_info = _StreamClientInfo(self._method, True, False, timeout)\n        return self._interceptor.intercept_stream(\n            request_iterator, metadata, client_info, invoker\n        )\n\n\nclass _InterceptorStreamStreamMultiCallable(grpc.StreamStreamMultiCallable):\n    def __init__(self, method, base_callable, interceptor):\n        self._method = method\n        self._base_callable = base_callable\n        self._interceptor = interceptor\n\n    def __call__(\n        self, request_iterator, timeout=None, metadata=None, credentials=None\n    ):\n        def invoker(request_iterator, metadata):\n            return self._base_callable(\n                request_iterator, timeout, metadata, credentials\n            )\n\n        client_info = _StreamClientInfo(self._method, True, True, timeout)\n        return self._interceptor.intercept_stream(\n            request_iterator, metadata, client_info, invoker\n        )\n\n\nclass _InterceptorChannel(grpc.Channel):\n    def __init__(self, channel, interceptor):\n        self._channel = channel\n        self._interceptor = interceptor\n\n    def subscribe(self, *args, **kwargs):\n        self._channel.subscribe(*args, **kwargs)\n\n    def unsubscribe(self, *args, **kwargs):\n        self._channel.unsubscribe(*args, **kwargs)\n\n    def unary_unary(\n        self, method, request_serializer=None, response_deserializer=None\n    ):\n        base_callable = self._channel.unary_unary(\n            method, request_serializer, response_deserializer\n        )\n        if isinstance(self._interceptor, grpcext.UnaryClientInterceptor):\n            return _InterceptorUnaryUnaryMultiCallable(\n                method, base_callable, self._interceptor\n            )\n        return base_callable\n\n    def unary_stream(\n        self, method, request_serializer=None, response_deserializer=None\n    ):\n        base_callable = self._channel.unary_stream(\n            method, request_serializer, response_deserializer\n        )\n        if isinstance(self._interceptor, grpcext.StreamClientInterceptor):\n            return _InterceptorUnaryStreamMultiCallable(\n                method, base_callable, self._interceptor\n            )\n        return base_callable\n\n    def stream_unary(\n        self, method, request_serializer=None, response_deserializer=None\n    ):\n        base_callable = self._channel.stream_unary(\n            method, request_serializer, response_deserializer\n        )\n        if isinstance(self._interceptor, grpcext.StreamClientInterceptor):\n            return _InterceptorStreamUnaryMultiCallable(\n                method, base_callable, self._interceptor\n            )\n        return base_callable\n\n    def stream_stream(\n        self, method, request_serializer=None, response_deserializer=None\n    ):\n        base_callable = self._channel.stream_stream(\n            method, request_serializer, response_deserializer\n        )\n        if isinstance(self._interceptor, grpcext.StreamClientInterceptor):\n            return _InterceptorStreamStreamMultiCallable(\n                method, base_callable, self._interceptor\n            )\n        return base_callable\n\n    def close(self):\n        if not hasattr(self._channel, \"close\"):\n            raise RuntimeError(\n                \"close() is not supported with the installed version of grpcio\"\n            )\n        self._channel.close()\n\n    def __enter__(self):\n        \"\"\"Enters the runtime context related to the channel object.\"\"\"\n        raise NotImplementedError()\n\n    def __exit__(self, exc_type, exc_val, exc_tb):\n        \"\"\"Exits the runtime context related to the channel object.\"\"\"\n        raise NotImplementedError()\n\n\ndef intercept_channel(channel, *interceptors):\n    result = channel\n    for interceptor in interceptors:\n        if not isinstance(\n            interceptor, grpcext.UnaryClientInterceptor\n        ) and not isinstance(interceptor, grpcext.StreamClientInterceptor):\n            raise TypeError(\n                \"interceptor must be either a \"\n                \"grpcext.UnaryClientInterceptor or a \"\n                \"grpcext.StreamClientInterceptor\"\n            )\n        result = _InterceptorChannel(result, interceptor)\n    return result\n", "path": "instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/grpcext/_interceptor.py"}], "after_files": [{"content": "# Copyright The OpenTelemetry Authors\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# pylint:disable=relative-beyond-top-level\n# pylint:disable=arguments-differ\n# pylint:disable=no-member\n# pylint:disable=signature-differs\n\n\"\"\"Implementation of gRPC Python interceptors.\"\"\"\n\n\nimport collections\n\nimport grpc\n\nfrom opentelemetry.instrumentation.grpc import grpcext\n\n\nclass _UnaryClientInfo(\n    collections.namedtuple(\"_UnaryClientInfo\", (\"full_method\", \"timeout\"))\n):\n    pass\n\n\nclass _StreamClientInfo(\n    collections.namedtuple(\n        \"_StreamClientInfo\",\n        (\"full_method\", \"is_client_stream\", \"is_server_stream\", \"timeout\"),\n    )\n):\n    pass\n\n\nclass _InterceptorUnaryUnaryMultiCallable(grpc.UnaryUnaryMultiCallable):\n    def __init__(self, method, base_callable, interceptor):\n        self._method = method\n        self._base_callable = base_callable\n        self._interceptor = interceptor\n\n    def __call__(self, request, timeout=None, metadata=None, credentials=None):\n        def invoker(request, metadata):\n            return self._base_callable(request, timeout, metadata, credentials)\n\n        client_info = _UnaryClientInfo(self._method, timeout)\n        return self._interceptor.intercept_unary(\n            request, metadata, client_info, invoker\n        )\n\n    def with_call(\n        self, request, timeout=None, metadata=None, credentials=None\n    ):\n        def invoker(request, metadata):\n            return self._base_callable.with_call(\n                request, timeout, metadata, credentials\n            )\n\n        client_info = _UnaryClientInfo(self._method, timeout)\n        return self._interceptor.intercept_unary(\n            request, metadata, client_info, invoker\n        )\n\n    def future(self, request, timeout=None, metadata=None, credentials=None):\n        def invoker(request, metadata):\n            return self._base_callable.future(\n                request, timeout, metadata, credentials\n            )\n\n        client_info = _UnaryClientInfo(self._method, timeout)\n        return self._interceptor.intercept_unary(\n            request, metadata, client_info, invoker\n        )\n\n\nclass _InterceptorUnaryStreamMultiCallable(grpc.UnaryStreamMultiCallable):\n    def __init__(self, method, base_callable, interceptor):\n        self._method = method\n        self._base_callable = base_callable\n        self._interceptor = interceptor\n\n    def __call__(self, request, timeout=None, metadata=None, credentials=None):\n        def invoker(request, metadata):\n            return self._base_callable(request, timeout, metadata, credentials)\n\n        client_info = _StreamClientInfo(self._method, False, True, timeout)\n        return self._interceptor.intercept_stream(\n            request, metadata, client_info, invoker\n        )\n\n\nclass _InterceptorStreamUnaryMultiCallable(grpc.StreamUnaryMultiCallable):\n    def __init__(self, method, base_callable, interceptor):\n        self._method = method\n        self._base_callable = base_callable\n        self._interceptor = interceptor\n\n    def __call__(\n        self, request_iterator, timeout=None, metadata=None, credentials=None\n    ):\n        def invoker(request_iterator, metadata):\n            return self._base_callable(\n                request_iterator, timeout, metadata, credentials\n            )\n\n        client_info = _StreamClientInfo(self._method, True, False, timeout)\n        return self._interceptor.intercept_stream(\n            request_iterator, metadata, client_info, invoker\n        )\n\n    def with_call(\n        self, request_iterator, timeout=None, metadata=None, credentials=None\n    ):\n        def invoker(request_iterator, metadata):\n            return self._base_callable.with_call(\n                request_iterator, timeout, metadata, credentials\n            )\n\n        client_info = _StreamClientInfo(self._method, True, False, timeout)\n        return self._interceptor.intercept_stream(\n            request_iterator, metadata, client_info, invoker\n        )\n\n    def future(\n        self, request_iterator, timeout=None, metadata=None, credentials=None\n    ):\n        def invoker(request_iterator, metadata):\n            return self._base_callable.future(\n                request_iterator, timeout, metadata, credentials\n            )\n\n        client_info = _StreamClientInfo(self._method, True, False, timeout)\n        return self._interceptor.intercept_stream(\n            request_iterator, metadata, client_info, invoker\n        )\n\n\nclass _InterceptorStreamStreamMultiCallable(grpc.StreamStreamMultiCallable):\n    def __init__(self, method, base_callable, interceptor):\n        self._method = method\n        self._base_callable = base_callable\n        self._interceptor = interceptor\n\n    def __call__(\n        self, request_iterator, timeout=None, metadata=None, credentials=None\n    ):\n        def invoker(request_iterator, metadata):\n            return self._base_callable(\n                request_iterator, timeout, metadata, credentials\n            )\n\n        client_info = _StreamClientInfo(self._method, True, True, timeout)\n        return self._interceptor.intercept_stream(\n            request_iterator, metadata, client_info, invoker\n        )\n\n\nclass _InterceptorChannel(grpc.Channel):\n    def __init__(self, channel, interceptor):\n        self._channel = channel\n        self._interceptor = interceptor\n\n    def subscribe(self, *args, **kwargs):\n        self._channel.subscribe(*args, **kwargs)\n\n    def unsubscribe(self, *args, **kwargs):\n        self._channel.unsubscribe(*args, **kwargs)\n\n    def unary_unary(\n        self, method, request_serializer=None, response_deserializer=None\n    ):\n        base_callable = self._channel.unary_unary(\n            method, request_serializer, response_deserializer\n        )\n        if isinstance(self._interceptor, grpcext.UnaryClientInterceptor):\n            return _InterceptorUnaryUnaryMultiCallable(\n                method, base_callable, self._interceptor\n            )\n        return base_callable\n\n    def unary_stream(\n        self, method, request_serializer=None, response_deserializer=None\n    ):\n        base_callable = self._channel.unary_stream(\n            method, request_serializer, response_deserializer\n        )\n        if isinstance(self._interceptor, grpcext.StreamClientInterceptor):\n            return _InterceptorUnaryStreamMultiCallable(\n                method, base_callable, self._interceptor\n            )\n        return base_callable\n\n    def stream_unary(\n        self, method, request_serializer=None, response_deserializer=None\n    ):\n        base_callable = self._channel.stream_unary(\n            method, request_serializer, response_deserializer\n        )\n        if isinstance(self._interceptor, grpcext.StreamClientInterceptor):\n            return _InterceptorStreamUnaryMultiCallable(\n                method, base_callable, self._interceptor\n            )\n        return base_callable\n\n    def stream_stream(\n        self, method, request_serializer=None, response_deserializer=None\n    ):\n        base_callable = self._channel.stream_stream(\n            method, request_serializer, response_deserializer\n        )\n        if isinstance(self._interceptor, grpcext.StreamClientInterceptor):\n            return _InterceptorStreamStreamMultiCallable(\n                method, base_callable, self._interceptor\n            )\n        return base_callable\n\n    def close(self):\n        if not hasattr(self._channel, \"close\"):\n            raise RuntimeError(\n                \"close() is not supported with the installed version of grpcio\"\n            )\n        self._channel.close()\n\n    def __enter__(self):\n        \"\"\"Enters the runtime context related to the channel object.\"\"\"\n        return self\n\n    def __exit__(self, exc_type, exc_val, exc_tb):\n        \"\"\"Exits the runtime context related to the channel object.\"\"\"\n        self.close()\n\n\ndef intercept_channel(channel, *interceptors):\n    result = channel\n    for interceptor in interceptors:\n        if not isinstance(\n            interceptor, grpcext.UnaryClientInterceptor\n        ) and not isinstance(interceptor, grpcext.StreamClientInterceptor):\n            raise TypeError(\n                \"interceptor must be either a \"\n                \"grpcext.UnaryClientInterceptor or a \"\n                \"grpcext.StreamClientInterceptor\"\n            )\n        result = _InterceptorChannel(result, interceptor)\n    return result\n", "path": "instrumentation/opentelemetry-instrumentation-grpc/src/opentelemetry/instrumentation/grpc/grpcext/_interceptor.py"}]}