12
12
# See the License for the specific language governing permissions and
13
13
# limitations under the License.
14
14
15
+ import functools
16
+ import logging
15
17
import sys
16
18
import time
17
- import logging
18
- import functools
19
19
20
20
from newrelic .api .application import application_instance
21
- from newrelic .api .transaction import current_transaction
22
- from newrelic .api .time_trace import notice_error
23
- from newrelic .api .web_transaction import WSGIWebTransaction
24
21
from newrelic .api .function_trace import FunctionTrace
25
22
from newrelic .api .html_insertion import insert_html_snippet , verify_body_exists
26
-
23
+ from newrelic .api .time_trace import notice_error
24
+ from newrelic .api .transaction import current_transaction
25
+ from newrelic .api .web_transaction import WSGIWebTransaction
27
26
from newrelic .common .object_names import callable_name
28
- from newrelic .common .object_wrapper import wrap_object , FunctionWrapper
29
-
27
+ from newrelic .common .object_wrapper import FunctionWrapper , wrap_object
30
28
from newrelic .packages import six
31
29
32
30
_logger = logging .getLogger (__name__ )
33
31
34
32
35
33
class _WSGIApplicationIterable (object ):
36
-
37
34
def __init__ (self , transaction , generator ):
38
35
self .transaction = transaction
39
36
self .generator = generator
@@ -68,8 +65,7 @@ def start_trace(self):
68
65
self .transaction ._sent_start = time .time ()
69
66
70
67
if not self .response_trace :
71
- self .response_trace = FunctionTrace (
72
- name = 'Response' , group = 'Python/WSGI' )
68
+ self .response_trace = FunctionTrace (name = "Response" , group = "Python/WSGI" )
73
69
self .response_trace .__enter__ ()
74
70
75
71
def close (self ):
@@ -81,13 +77,12 @@ def close(self):
81
77
self .response_trace = None
82
78
83
79
try :
84
- with FunctionTrace (
85
- name = 'Finalize' , group = 'Python/WSGI' ):
80
+ with FunctionTrace (name = "Finalize" , group = "Python/WSGI" ):
86
81
87
82
if isinstance (self .generator , _WSGIApplicationMiddleware ):
88
83
self .generator .close ()
89
84
90
- elif hasattr (self .generator , ' close' ):
85
+ elif hasattr (self .generator , " close" ):
91
86
name = callable_name (self .generator .close )
92
87
with FunctionTrace (name ):
93
88
self .generator .close ()
@@ -105,7 +100,6 @@ def close(self):
105
100
106
101
107
102
class _WSGIInputWrapper (object ):
108
-
109
103
def __init__ (self , transaction , input ):
110
104
self .__transaction = transaction
111
105
self .__input = input
@@ -114,7 +108,7 @@ def __getattr__(self, name):
114
108
return getattr (self .__input , name )
115
109
116
110
def close (self ):
117
- if hasattr (self .__input , ' close' ):
111
+ if hasattr (self .__input , " close" ):
118
112
self .__input .close ()
119
113
120
114
def read (self , * args , ** kwargs ):
@@ -204,8 +198,7 @@ def __init__(self, application, environ, start_response, transaction):
204
198
205
199
# Grab the iterable returned by the wrapped WSGI
206
200
# application.
207
- self .iterable = self .application (self .request_environ ,
208
- self .start_response )
201
+ self .iterable = self .application (self .request_environ , self .start_response )
209
202
210
203
def process_data (self , data ):
211
204
# If this is the first data block, then immediately try
@@ -217,7 +210,7 @@ def html_to_be_inserted():
217
210
header = self .transaction .browser_timing_header ()
218
211
219
212
if not header :
220
- return b''
213
+ return b""
221
214
222
215
footer = self .transaction .browser_timing_footer ()
223
216
@@ -228,10 +221,12 @@ def html_to_be_inserted():
228
221
229
222
if modified is not None :
230
223
if self .debug :
231
- _logger .debug ('RUM insertion from WSGI middleware '
232
- 'triggered on first yielded string from '
233
- 'response. Bytes added was %r.' ,
234
- len (modified ) - len (data ))
224
+ _logger .debug (
225
+ "RUM insertion from WSGI middleware "
226
+ "triggered on first yielded string from "
227
+ "response. Bytes added was %r." ,
228
+ len (modified ) - len (data ),
229
+ )
235
230
236
231
if self .content_length is not None :
237
232
length = len (modified ) - len (data )
@@ -264,7 +259,7 @@ def html_to_be_inserted():
264
259
265
260
if self .response_data :
266
261
self .response_data .append (data )
267
- data = b'' .join (self .response_data )
262
+ data = b"" .join (self .response_data )
268
263
self .response_data = []
269
264
270
265
# Perform the insertion of the HTML. This should always
@@ -276,10 +271,12 @@ def html_to_be_inserted():
276
271
277
272
if modified is not None :
278
273
if self .debug :
279
- _logger .debug ('RUM insertion from WSGI middleware '
280
- 'triggered on subsequent string yielded from '
281
- 'response. Bytes added was %r.' ,
282
- len (modified ) - len (data ))
274
+ _logger .debug (
275
+ "RUM insertion from WSGI middleware "
276
+ "triggered on subsequent string yielded from "
277
+ "response. Bytes added was %r." ,
278
+ len (modified ) - len (data ),
279
+ )
283
280
284
281
if self .content_length is not None :
285
282
length = len (modified ) - len (data )
@@ -297,11 +294,10 @@ def flush_headers(self):
297
294
# additional data was inserted into the response.
298
295
299
296
if self .content_length is not None :
300
- header = (( ' Content-Length' , str (self .content_length ) ))
297
+ header = (" Content-Length" , str (self .content_length ))
301
298
self .response_headers .append (header )
302
299
303
- self .outer_write = self .outer_start_response (self .response_status ,
304
- self .response_headers , * self .response_args )
300
+ self .outer_write = self .outer_start_response (self .response_status , self .response_headers , * self .response_args )
305
301
306
302
def inner_write (self , data ):
307
303
# If the write() callable is used, we do not attempt to
@@ -345,8 +341,7 @@ def start_response(self, status, response_headers, *args):
345
341
# This is because it can be disabled using an API call.
346
342
# Also check whether RUM insertion has already occurred.
347
343
348
- if (self .transaction .autorum_disabled or
349
- self .transaction .rum_header_generated ):
344
+ if self .transaction .autorum_disabled or self .transaction .rum_header_generated :
350
345
351
346
self .flush_headers ()
352
347
self .pass_through = True
@@ -370,21 +365,21 @@ def start_response(self, status, response_headers, *args):
370
365
for (name , value ) in response_headers :
371
366
_name = name .lower ()
372
367
373
- if _name == ' content-length' :
368
+ if _name == " content-length" :
374
369
try :
375
370
content_length = int (value )
376
371
continue
377
372
378
373
except ValueError :
379
374
pass_through = True
380
375
381
- elif _name == ' content-type' :
376
+ elif _name == " content-type" :
382
377
content_type = value
383
378
384
- elif _name == ' content-encoding' :
379
+ elif _name == " content-encoding" :
385
380
content_encoding = value
386
381
387
- elif _name == ' content-disposition' :
382
+ elif _name == " content-disposition" :
388
383
content_disposition = value
389
384
390
385
headers .append ((name , value ))
@@ -408,9 +403,7 @@ def should_insert_html():
408
403
409
404
return False
410
405
411
- if (content_disposition is not None and
412
- content_disposition .split (';' )[0 ].strip ().lower () ==
413
- 'attachment' ):
406
+ if content_disposition is not None and content_disposition .split (";" )[0 ].strip ().lower () == "attachment" :
414
407
return False
415
408
416
409
if content_type is None :
@@ -419,7 +412,7 @@ def should_insert_html():
419
412
settings = self .transaction .settings
420
413
allowed_content_type = settings .browser_monitoring .content_type
421
414
422
- if content_type .split (';' )[0 ] not in allowed_content_type :
415
+ if content_type .split (";" )[0 ] not in allowed_content_type :
423
416
return False
424
417
425
418
return True
@@ -443,7 +436,7 @@ def close(self):
443
436
# Call close() on the iterable as required by the
444
437
# WSGI specification.
445
438
446
- if hasattr (self .iterable , ' close' ):
439
+ if hasattr (self .iterable , " close" ):
447
440
name = callable_name (self .iterable .close )
448
441
with FunctionTrace (name ):
449
442
self .iterable .close ()
@@ -518,18 +511,35 @@ def __iter__(self):
518
511
yield data
519
512
520
513
521
- def WSGIApplicationWrapper (wrapped , application = None , name = None ,
522
- group = None , framework = None ):
514
+ def WSGIApplicationWrapper (wrapped , application = None , name = None , group = None , framework = None ):
515
+
516
+ # Python 2 does not allow rebinding nonlocal variables, so to fix this
517
+ # framework must be stored in list so it can be edited by closure.
518
+ _framework = [framework ]
519
+
520
+ def get_framework ():
521
+ """Used to delay imports by passing framework as a callable."""
522
+ framework = _framework [0 ]
523
+ if isinstance (framework , tuple ) or framework is None :
524
+ return framework
525
+
526
+ if callable (framework ):
527
+ framework = framework ()
528
+ _framework [0 ] = framework
529
+
530
+ if framework is not None and not isinstance (framework , tuple ):
531
+ framework = (framework , None )
532
+ _framework [0 ] = framework
523
533
524
- if framework is not None and not isinstance (framework , tuple ):
525
- framework = (framework , None )
534
+ return framework
526
535
527
536
def _nr_wsgi_application_wrapper_ (wrapped , instance , args , kwargs ):
528
537
# Check to see if any transaction is present, even an inactive
529
538
# one which has been marked to be ignored or which has been
530
539
# stopped already.
531
540
532
541
transaction = current_transaction (active_only = False )
542
+ framework = get_framework ()
533
543
534
544
if transaction :
535
545
# If there is any active transaction we will return without
@@ -545,8 +555,7 @@ def _nr_wsgi_application_wrapper_(wrapped, instance, args, kwargs):
545
555
# supportability metrics.
546
556
547
557
if framework :
548
- transaction .add_framework_info (
549
- name = framework [0 ], version = framework [1 ])
558
+ transaction .add_framework_info (name = framework [0 ], version = framework [1 ])
550
559
551
560
# Also override the web transaction name to be the name of
552
561
# the wrapped callable if not explicitly named, and we want
@@ -560,9 +569,8 @@ def _nr_wsgi_application_wrapper_(wrapped, instance, args, kwargs):
560
569
if name is None and settings :
561
570
if framework is not None :
562
571
naming_scheme = settings .transaction_name .naming_scheme
563
- if naming_scheme in (None , 'framework' ):
564
- transaction .set_transaction_name (
565
- callable_name (wrapped ), priority = 1 )
572
+ if naming_scheme in (None , "framework" ):
573
+ transaction .set_transaction_name (callable_name (wrapped ), priority = 1 )
566
574
567
575
elif name :
568
576
transaction .set_transaction_name (name , group , priority = 1 )
@@ -580,11 +588,11 @@ def _args(environ, start_response, *args, **kwargs):
580
588
581
589
target_application = application
582
590
583
- if ' newrelic.app_name' in environ :
584
- app_name = environ [' newrelic.app_name' ]
591
+ if " newrelic.app_name" in environ :
592
+ app_name = environ [" newrelic.app_name" ]
585
593
586
- if ';' in app_name :
587
- app_names = [n .strip () for n in app_name .split (';' )]
594
+ if ";" in app_name :
595
+ app_names = [n .strip () for n in app_name .split (";" )]
588
596
app_name = app_names [0 ]
589
597
target_application = application_instance (app_name )
590
598
for altname in app_names [1 :]:
@@ -598,7 +606,7 @@ def _args(environ, start_response, *args, **kwargs):
598
606
599
607
# FIXME Should this allow for multiple apps if a string.
600
608
601
- if not hasattr (application , ' activate' ):
609
+ if not hasattr (application , " activate" ):
602
610
target_application = application_instance (application )
603
611
604
612
# Now start recording the actual web transaction.
@@ -609,8 +617,7 @@ def _args(environ, start_response, *args, **kwargs):
609
617
# reporting as supportability metrics.
610
618
611
619
if framework :
612
- transaction .add_framework_info (
613
- name = framework [0 ], version = framework [1 ])
620
+ transaction .add_framework_info (name = framework [0 ], version = framework [1 ])
614
621
615
622
# Override the initial web transaction name to be the supplied
616
623
# name, or the name of the wrapped callable if wanting to use
@@ -630,24 +637,20 @@ def _args(environ, start_response, *args, **kwargs):
630
637
naming_scheme = settings .transaction_name .naming_scheme
631
638
632
639
if framework is not None :
633
- if naming_scheme in (None , 'framework' ):
634
- transaction .set_transaction_name (
635
- callable_name (wrapped ), priority = 1 )
640
+ if naming_scheme in (None , "framework" ):
641
+ transaction .set_transaction_name (callable_name (wrapped ), priority = 1 )
636
642
637
- elif naming_scheme in ('component' , 'framework' ):
638
- transaction .set_transaction_name (
639
- callable_name (wrapped ), priority = 1 )
643
+ elif naming_scheme in ("component" , "framework" ):
644
+ transaction .set_transaction_name (callable_name (wrapped ), priority = 1 )
640
645
641
646
elif name :
642
647
transaction .set_transaction_name (name , group , priority = 1 )
643
648
644
649
def _start_response (status , response_headers , * args ):
645
650
646
- additional_headers = transaction .process_response (
647
- status , response_headers , * args )
651
+ additional_headers = transaction .process_response (status , response_headers , * args )
648
652
649
- _write = start_response (status ,
650
- response_headers + additional_headers , * args )
653
+ _write = start_response (status , response_headers + additional_headers , * args )
651
654
652
655
def write (data ):
653
656
if not transaction ._sent_start :
@@ -667,17 +670,13 @@ def write(data):
667
670
# Should always exist, but check as test harnesses may not
668
671
# have it.
669
672
670
- if 'wsgi.input' in environ :
671
- environ ['wsgi.input' ] = _WSGIInputWrapper (transaction ,
672
- environ ['wsgi.input' ])
673
+ if "wsgi.input" in environ :
674
+ environ ["wsgi.input" ] = _WSGIInputWrapper (transaction , environ ["wsgi.input" ])
673
675
674
- with FunctionTrace (
675
- name = 'Application' , group = 'Python/WSGI' ):
676
+ with FunctionTrace (name = "Application" , group = "Python/WSGI" ):
676
677
with FunctionTrace (name = callable_name (wrapped )):
677
- if (settings and settings .browser_monitoring .enabled and
678
- not transaction .autorum_disabled ):
679
- result = _WSGIApplicationMiddleware (wrapped ,
680
- environ , _start_response , transaction )
678
+ if settings and settings .browser_monitoring .enabled and not transaction .autorum_disabled :
679
+ result = _WSGIApplicationMiddleware (wrapped , environ , _start_response , transaction )
681
680
else :
682
681
result = wrapped (environ , _start_response )
683
682
@@ -691,11 +690,10 @@ def write(data):
691
690
692
691
693
692
def wsgi_application (application = None , name = None , group = None , framework = None ):
694
- return functools .partial (WSGIApplicationWrapper , application = application ,
695
- name = name , group = group , framework = framework )
693
+ return functools .partial (
694
+ WSGIApplicationWrapper , application = application , name = name , group = group , framework = framework
695
+ )
696
696
697
697
698
- def wrap_wsgi_application (module , object_path , application = None ,
699
- name = None , group = None , framework = None ):
700
- wrap_object (module , object_path , WSGIApplicationWrapper ,
701
- (application , name , group , framework ))
698
+ def wrap_wsgi_application (module , object_path , application = None , name = None , group = None , framework = None ):
699
+ wrap_object (module , object_path , WSGIApplicationWrapper , (application , name , group , framework ))
0 commit comments