Skip to content

Commit 6c8e8a9

Browse files
lrafeeiTimPansinoumaannamalai
authored
Fix Starlette instrumentation after 0.20.1 release (#560)
* Fix Starlette instrumentation after 0.20.1 release Co-authored-by: Timothy Pansino <[email protected]> Co-authored-by: Uma Annamalai <[email protected]> * Fix the hasattr gate for Starlette instrumentation Co-authored-by: Timothy Pansino <[email protected]> * Fix basehttp test for Python 3.7/Starlette 0.20.1 * Clean up tests in Starlette * Remove import of "version_info" Co-authored-by: Timothy Pansino <[email protected]> * Update tests/framework_starlette/test_bg_tasks.py Co-authored-by: Timothy Pansino <[email protected]> Co-authored-by: Uma Annamalai <[email protected]> Co-authored-by: Timothy Pansino <[email protected]>
1 parent 1ea34ed commit 6c8e8a9

File tree

6 files changed

+89
-30
lines changed

6 files changed

+89
-30
lines changed

newrelic/config.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2467,6 +2467,11 @@ def _process_module_builtin_defaults():
24672467
"newrelic.hooks.framework_starlette",
24682468
"instrument_starlette_middleware_errors",
24692469
)
2470+
_process_module_definition(
2471+
"starlette.middleware.exceptions",
2472+
"newrelic.hooks.framework_starlette",
2473+
"instrument_starlette_middleware_exceptions",
2474+
)
24702475
_process_module_definition(
24712476
"starlette.exceptions",
24722477
"newrelic.hooks.framework_starlette",

newrelic/hooks/framework_starlette.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,14 +241,26 @@ def instrument_starlette_middleware_errors(module):
241241
wrap_function_wrapper(module, "ServerErrorMiddleware.debug_response", wrap_exception_handler)
242242

243243

244-
def instrument_starlette_exceptions(module):
244+
def instrument_starlette_middleware_exceptions(module):
245245
wrap_function_wrapper(module, "ExceptionMiddleware.__call__", error_middleware_wrapper)
246246

247247
wrap_function_wrapper(module, "ExceptionMiddleware.http_exception", wrap_exception_handler)
248248

249249
wrap_function_wrapper(module, "ExceptionMiddleware.add_exception_handler", wrap_add_exception_handler)
250250

251251

252+
def instrument_starlette_exceptions(module):
253+
# ExceptionMiddleware was moved to starlette.middleware.exceptions, need to check
254+
# that it isn't being imported through a deprecation and double wrapped.
255+
if not hasattr(module, "__deprecated__"):
256+
257+
wrap_function_wrapper(module, "ExceptionMiddleware.__call__", error_middleware_wrapper)
258+
259+
wrap_function_wrapper(module, "ExceptionMiddleware.http_exception", wrap_exception_handler)
260+
261+
wrap_function_wrapper(module, "ExceptionMiddleware.add_exception_handler", wrap_add_exception_handler)
262+
263+
252264
def instrument_starlette_background_task(module):
253265
wrap_function_wrapper(module, "BackgroundTask.__call__", wrap_background_method)
254266

tests/framework_starlette/_test_bg_tasks.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ async def async_bg_task():
5252
pass
5353

5454

55-
async def sync_bg_task():
55+
def sync_bg_task():
5656
pass
5757

5858

tests/framework_starlette/test_application.py

Lines changed: 41 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
from newrelic.common.object_names import callable_name
2626
from testing_support.validators.validate_code_level_metrics import validate_code_level_metrics
2727

28+
starlette_version = tuple(int(x) for x in starlette.__version__.split("."))
2829

2930
@pytest.fixture(scope="session")
3031
def target_application():
@@ -34,10 +35,18 @@ def target_application():
3435

3536

3637
FRAMEWORK_METRIC = ("Python/Framework/Starlette/%s" % starlette.__version__, 1)
37-
DEFAULT_MIDDLEWARE_METRICS = [
38-
("Function/starlette.middleware.errors:ServerErrorMiddleware.__call__", 1),
39-
("Function/starlette.exceptions:ExceptionMiddleware.__call__", 1),
40-
]
38+
39+
if starlette_version >= (0, 20, 1):
40+
DEFAULT_MIDDLEWARE_METRICS = [
41+
("Function/starlette.middleware.errors:ServerErrorMiddleware.__call__", 1),
42+
("Function/starlette.middleware.exceptions:ExceptionMiddleware.__call__", 1),
43+
]
44+
else:
45+
DEFAULT_MIDDLEWARE_METRICS = [
46+
("Function/starlette.middleware.errors:ServerErrorMiddleware.__call__", 1),
47+
("Function/starlette.exceptions:ExceptionMiddleware.__call__", 1),
48+
]
49+
4150
MIDDLEWARE_METRICS = [
4251
("Function/_test_application:middleware_factory.<locals>.middleware", 2),
4352
("Function/_test_application:middleware_decorator", 1),
@@ -71,17 +80,27 @@ def test_application_non_async(target_application, app_name):
7180
response = app.get("/non_async")
7281
assert response.status == 200
7382

83+
# Starting in Starlette v0.20.1, the ExceptionMiddleware class
84+
# has been moved to the starlette.middleware.exceptions from
85+
# starlette.exceptions
86+
version_tweak_string = ".middleware" if starlette_version >= (0, 20, 1) else ""
7487

75-
@pytest.mark.parametrize(
76-
"app_name, transaction_name",
88+
DEFAULT_MIDDLEWARE_METRICS = [
89+
("Function/starlette.middleware.errors:ServerErrorMiddleware.__call__", 1),
90+
("Function/starlette%s.exceptions:ExceptionMiddleware.__call__" % version_tweak_string, 1),
91+
]
92+
93+
middleware_test = (
94+
("no_error_handler", "starlette%s.exceptions:ExceptionMiddleware.__call__" % version_tweak_string),
7795
(
78-
("no_error_handler", "starlette.exceptions:ExceptionMiddleware.__call__"),
79-
(
80-
"non_async_error_handler_no_middleware",
81-
"starlette.exceptions:ExceptionMiddleware.__call__",
82-
),
96+
"non_async_error_handler_no_middleware",
97+
"starlette%s.exceptions:ExceptionMiddleware.__call__" % version_tweak_string,
8398
),
8499
)
100+
101+
@pytest.mark.parametrize(
102+
"app_name, transaction_name", middleware_test,
103+
)
85104
def test_application_nonexistent_route(target_application, app_name, transaction_name):
86105
@validate_transaction_metrics(
87106
transaction_name,
@@ -244,19 +263,20 @@ def _test():
244263
_test()
245264

246265

247-
@pytest.mark.parametrize(
248-
"app_name,scoped_metrics",
266+
middleware_test_exception = (
249267
(
250-
(
251-
"no_middleware",
252-
[("Function/starlette.exceptions:ExceptionMiddleware.http_exception", 1)],
253-
),
254-
(
255-
"teapot_exception_handler_no_middleware",
256-
[("Function/_test_application:teapot_handler", 1)],
257-
),
268+
"no_middleware",
269+
[("Function/starlette%s.exceptions:ExceptionMiddleware.http_exception" % version_tweak_string, 1)],
270+
),
271+
(
272+
"teapot_exception_handler_no_middleware",
273+
[("Function/_test_application:teapot_handler", 1)],
258274
),
259275
)
276+
277+
@pytest.mark.parametrize(
278+
"app_name,scoped_metrics", middleware_test_exception
279+
)
260280
def test_starlette_http_exception(target_application, app_name, scoped_metrics):
261281
@validate_transaction_errors(errors=["starlette.exceptions:HTTPException"])
262282
@validate_transaction_metrics(

tests/framework_starlette/test_bg_tasks.py

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,17 @@
1313
# limitations under the License.
1414

1515
import pytest
16+
import sys
1617
from testing_support.fixtures import validate_transaction_metrics
1718
from testing_support.validators.validate_transaction_count import (
1819
validate_transaction_count,
1920
)
2021

22+
from starlette import __version__
23+
starlette_version = tuple(int(x) for x in __version__.split("."))
24+
2125
try:
22-
from starlette.middleware import Middleware
26+
from starlette.middleware import Middleware # Ignore Flake8 Error
2327

2428
no_middleware = False
2529
except ImportError:
@@ -79,18 +83,35 @@ def _test():
7983
@skip_if_no_middleware
8084
@pytest.mark.parametrize("route", ["async", "sync"])
8185
def test_basehttp_style_middleware(target_application, route):
82-
metrics = [
86+
route_metrics = [("Function/_test_bg_tasks:run_%s_bg_task" % route, 1)]
87+
old_metrics = [
8388
("Function/_test_bg_tasks:%s_bg_task" % route, 1),
8489
("Function/_test_bg_tasks:run_%s_bg_task" % route, 1),
8590
]
8691

87-
@validate_transaction_metrics(
88-
"_test_bg_tasks:run_%s_bg_task" % route, scoped_metrics=metrics
89-
)
90-
@validate_transaction_count(1)
9192
def _test():
9293
app = target_application["basehttp"]
9394
response = app.get("/" + route)
9495
assert response.status == 200
9596

97+
if starlette_version >= (0, 20, 1):
98+
if sys.version_info[:2] > (3, 7):
99+
_test = validate_transaction_metrics(
100+
"_test_bg_tasks:run_%s_bg_task" % route, index=-2, scoped_metrics=route_metrics
101+
)(_test)
102+
_test = validate_transaction_metrics(
103+
"_test_bg_tasks:%s_bg_task" % route, background_task=True
104+
)(_test)
105+
_test = validate_transaction_count(2)(_test)
106+
else: # Python <= 3.7 requires this specific configuration with starlette 0.20.1
107+
_test = validate_transaction_metrics(
108+
"_test_bg_tasks:run_%s_bg_task" % route, scoped_metrics=route_metrics
109+
)(_test)
110+
_test = validate_transaction_count(1)(_test)
111+
else:
112+
_test = validate_transaction_metrics(
113+
"_test_bg_tasks:run_%s_bg_task" % route, scoped_metrics=old_metrics
114+
)(_test)
115+
_test = validate_transaction_count(1)(_test)
116+
96117
_test()

tox.ini

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ envlist =
136136
python-framework_pyramid-{py37,py38,py39,py310}-Pyramidmaster,
137137
python-framework_sanic-{py38,pypy3}-sanic{190301,1906,1812,1912,200904,210300},
138138
python-framework_sanic-{py36,py37,py38,py310,pypy3}-saniclatest,
139-
python-framework_starlette-{py36,py310,pypy3}-starlette{0014,0015},
139+
python-framework_starlette-{py36,py310,pypy3}-starlette{0014,0015,0019},
140140
python-framework_starlette-{py36,py37,py38,py39,py310,pypy3}-starlette{latest},
141141
python-framework_strawberry-{py37,py38,py39,py310}-strawberrylatest,
142142
libcurl-framework_tornado-{py36,py37,py38,py39,py310,pypy3}-tornado0600,
@@ -316,6 +316,7 @@ deps =
316316
framework_starlette: graphene<3
317317
framework_starlette-starlette0014: starlette<0.15
318318
framework_starlette-starlette0015: starlette<0.16
319+
framework_starlette-starlette0019: starlette<0.20
319320
framework_starlette-starlettelatest: starlette
320321
; Strawberry 0.95.0 is incompatible with Starlette 0.18.0, downgrade until future release
321322
framework_strawberry: starlette<0.18.0

0 commit comments

Comments
 (0)