Skip to content

Commit cb01a6b

Browse files
Use instanceof to check if responses are valid Response objects (#273)
1 parent 8abed07 commit cb01a6b

File tree

3 files changed

+46
-1
lines changed

3 files changed

+46
-1
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3737
([#227](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/227]))
3838
- `opentelemetry-instrumentation-botocore` Adds a field to report the number of retries it take to complete an API call
3939
([#275](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/275))
40+
- `opentelemetry-instrumentation-requests` Use instanceof to check if responses are valid Response objects
41+
([#273](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/273))
4042

4143
### Changed
4244
- `opentelemetry-instrumentation-asgi`, `opentelemetry-instrumentation-wsgi` Return `None` for `CarrierGetter` if key not found

instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/__init__.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838

3939
from requests import Timeout, URLRequired
4040
from requests.exceptions import InvalidSchema, InvalidURL, MissingSchema
41+
from requests.models import Response
4142
from requests.sessions import Session
4243
from requests.structures import CaseInsensitiveDict
4344

@@ -158,7 +159,7 @@ def _instrumented_requests_call(
158159
finally:
159160
context.detach(token)
160161

161-
if result is not None:
162+
if isinstance(result, Response):
162163
if span.is_recording():
163164
span.set_attribute(
164165
"http.status_code", result.status_code

instrumentation/opentelemetry-instrumentation-requests/tests/test_requests_integration.py

+42
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@
2828
from opentelemetry.trace.status import StatusCode
2929

3030

31+
class InvalidResponseObjectException(Exception):
32+
def __init__(self):
33+
super().__init__()
34+
self.response = {}
35+
36+
3137
class RequestsIntegrationTestBase(abc.ABC):
3238
# pylint: disable=no-member
3339

@@ -307,6 +313,42 @@ def test_requests_exception_without_response(self, *_, **__):
307313
mocked_response.status_code = 500
308314
mocked_response.reason = "Internal Server Error"
309315

316+
@mock.patch(
317+
"requests.adapters.HTTPAdapter.send",
318+
side_effect=InvalidResponseObjectException,
319+
)
320+
def test_requests_exception_without_proper_response_type(self, *_, **__):
321+
with self.assertRaises(InvalidResponseObjectException):
322+
self.perform_request(self.URL)
323+
324+
span = self.assert_span()
325+
self.assertEqual(
326+
span.attributes,
327+
{"component": "http", "http.method": "GET", "http.url": self.URL},
328+
)
329+
self.assertEqual(span.status.status_code, StatusCode.ERROR)
330+
331+
self.assertIsNotNone(RequestsInstrumentor().meter)
332+
self.assertEqual(len(RequestsInstrumentor().meter.instruments), 1)
333+
recorder = list(RequestsInstrumentor().meter.instruments.values())[0]
334+
match_key = get_dict_as_key(
335+
{
336+
"http.method": "GET",
337+
"http.url": "http://httpbin.org/status/200",
338+
}
339+
)
340+
for key in recorder.bound_instruments.keys():
341+
self.assertEqual(key, match_key)
342+
# pylint: disable=protected-access
343+
bound = recorder.bound_instruments.get(key)
344+
for view_data in bound.view_datas:
345+
self.assertEqual(view_data.labels, key)
346+
self.assertEqual(view_data.aggregator.current.count, 1)
347+
348+
mocked_response = requests.Response()
349+
mocked_response.status_code = 500
350+
mocked_response.reason = "Internal Server Error"
351+
310352
@mock.patch(
311353
"requests.adapters.HTTPAdapter.send",
312354
side_effect=requests.RequestException(response=mocked_response),

0 commit comments

Comments
 (0)