Skip to content

Commit aa01abc

Browse files
committed
Add function to pretty print HttpRequest
Make it easy to dump the contents of a view's HttpRequest to a logger.
1 parent 5db2915 commit aa01abc

File tree

2 files changed

+68
-1
lines changed

2 files changed

+68
-1
lines changed

python/nav/django/utils.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
"""Utility methods for django used in NAV"""
1919

2020
from django.core.exceptions import FieldDoesNotExist
21+
from django.http import HttpRequest
2122
from django.urls import reverse
2223
from django.utils.http import urlencode
2324

@@ -56,6 +57,39 @@ def get_verbose_name(model, lookup):
5657
raise FieldDoesNotExist
5758

5859

60+
def pformat_request(request: HttpRequest, function, *attributes) -> None:
61+
"""View ``request`` via `function``, one line per attribute
62+
63+
Use the ``attributes`` parameter to limit what attributes are inspected.
64+
65+
Also dumps the contents of the dicts ``request.environ``and
66+
``request.META``, one line per value, sorted per key.
67+
68+
The ``function`` must have an input signature compatible with
69+
``logging.Logger.debug()``.
70+
71+
Meant for debugging via logs.
72+
73+
Example usage::
74+
75+
pformat_request(request, logging.getLogger(__name__).debug)
76+
"""
77+
DICT_ATTRIBUTES = ('META', 'environ')
78+
79+
existing_attributes = vars(request).keys()
80+
if attributes:
81+
attributes = set(existing_attributes).intersection(attributes)
82+
else:
83+
attributes = existing_attributes
84+
for attribute in sorted(attributes):
85+
value = getattr(request, attribute)
86+
if attribute in DICT_ATTRIBUTES:
87+
for key in sorted(value.keys()):
88+
function('request.%s: %s: %s', attribute, key, value[key])
89+
else:
90+
function('request.%s: %s', attribute, value)
91+
92+
5993
#
6094
# Django version differentiated helper functions:
6195
#

tests/unittests/django/utils_test.py

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# -*- coding: utf-8 -*-
2-
from nav.django.utils import get_verbose_name, reverse_with_query
2+
import logging
3+
from unittest import TestCase
4+
5+
from django.test import RequestFactory
6+
7+
from nav.django.utils import get_verbose_name, pformat_request, reverse_with_query
8+
9+
_logger = logging.getLogger(__name__)
310

411

512
def test_verbose_name():
@@ -13,3 +20,29 @@ def test_verbose_name():
1320
def test_reverse_with_query_should_work_with_unicode():
1421
"""Reveals issues with PY2/PY3 co-compatibility"""
1522
assert reverse_with_query("maintenance-new", roomid="bø-123")
23+
24+
25+
class TestPFormatRequest(TestCase):
26+
def test_should_log_more_lines_than_there_are_attributes_in_request(
27+
self,
28+
):
29+
r = RequestFactory()
30+
request = r.get('/')
31+
num_request_attributes = len(vars(request))
32+
with self.assertLogs(level=logging.DEBUG) as logs:
33+
pformat_request(request, _logger.debug)
34+
self.assertTrue(len(logs.records) > num_request_attributes)
35+
36+
def test_should_log_nothing_for_nonexistent_attribute(self):
37+
r = RequestFactory()
38+
request = r.get('/')
39+
with self.assertRaises(AssertionError):
40+
with self.assertLogs():
41+
pformat_request(request, _logger.debug, 'doesnotexist-nanana')
42+
43+
def test_should_log_one_line_for_content_type(self):
44+
r = RequestFactory()
45+
request = r.get('/')
46+
with self.assertLogs(level=logging.DEBUG) as logs:
47+
pformat_request(request, _logger.debug, 'content_type')
48+
self.assertEqual(len(logs.records), 1)

0 commit comments

Comments
 (0)