Skip to content

Commit b835350

Browse files
committed
INV-1894 - Fix circular error issue for wsgi requests
1 parent 1884d76 commit b835350

File tree

5 files changed

+84
-14
lines changed

5 files changed

+84
-14
lines changed

stackify/transport/default/log.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from stackify.transport.default.formats import JSONObject
33
from stackify.transport.default.error import StackifyError
44
from stackify.utils import data_to_json
5+
from stackify.utils import extract_request
56

67

78
class LogMsg(JSONObject):
@@ -31,6 +32,8 @@ def from_record(self, record):
3132
data = {k: v for k, v in record.__dict__.items()
3233
if k not in RECORD_VARS}
3334

35+
data = extract_request(data)
36+
3437
if data:
3538
self.data = data_to_json(data)
3639

stackify/utils.py

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,41 @@ def arg_or_env(name, args, default=None, env_key=None):
2121

2222
def data_to_json(data):
2323
try:
24-
if object_is_iterable(data) and 'request' in data and hasattr(data['request'], '_messages'):
25-
data['request'] = get_default_object(data['request'])
26-
2724
return json.dumps(data, default=lambda x: get_default_object(x))
28-
except ValueError as e:
29-
internal_logger.exception('Failed to serialize object to json: {} - Exception: {}'.format(data.__str__(), str(e)))
30-
return json.dumps(data.__str__()) # String representation of the object
25+
except Exception as e:
26+
internal_logger.exception('Failed to serialize object to json: {} - Exception: {}'.format(str(data), str(e)))
27+
return str(data) # String representation of the object
28+
29+
30+
def extract_request(data):
31+
if 'request' in data and "WSGIRequest" in str(data['request']):
32+
new_request = {}
33+
obj = data['request']
34+
35+
if hasattr(obj, 'path'):
36+
new_request['path'] = obj.path
37+
38+
if hasattr(obj, 'method'):
39+
new_request['method'] = obj.method
40+
41+
if hasattr(obj, 'POST'):
42+
new_request['form'] = obj.POST
43+
44+
if hasattr(obj, 'GET'):
45+
new_request['query'] = obj.GET
46+
47+
if hasattr(obj, 'content_type'):
48+
new_request['content_type'] = obj.content_type
49+
50+
data['request'] = new_request
51+
52+
return data
3153

3254

3355
def get_default_object(obj):
56+
if object_is_iterable(obj):
57+
return [item for item in obj]
58+
3459
return hasattr(obj, '__dict__') and obj.__dict__ or obj.__str__()
3560

3661

test.sh

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,14 @@ function runPyTest() {
2222
virtualenv -p python${python_version} ${test_venv}
2323

2424
echo "Activating virtualenv ${test_venv}..."
25-
source ${test_venv}/bin/activate
25+
26+
if [ -f ${test_venv}/bin/activate ]; then
27+
source ${test_venv}/bin/activate
28+
fi
29+
30+
if [ -f ${test_venv}/Scripts/activate ]; then
31+
source ${test_venv}/Scripts/activate
32+
fi
2633

2734
echo 'Installing dependencies...'
2835
pip install -r requirements.txt

tests/test_utils.py

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ def tearDown(self):
3030
for logger in self.loggers:
3131
del global_loggers[logger.name]
3232

33+
def test_utils_data_to_json_string(self):
34+
dummy = 'test'
35+
result = stackify.utils.data_to_json(dummy)
36+
self.assertEqual('"test"', result)
37+
3338
def test_utils_data_to_json_unserializable(self):
3439
dummy = Dummy()
3540
result = stackify.utils.data_to_json(dummy)
@@ -57,9 +62,9 @@ def test_utils_data_to_json_tuple(self):
5762
self.assertEqual(expected, result)
5863

5964
def test_utils_data_to_json_dummy_iterable(self):
60-
dummy = DummyInterable()
65+
dummy = DummyIterable()
6166
result = stackify.utils.data_to_json(dummy)
62-
expected = '{"a": 21}'
67+
expected = '[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]'
6368
self.assertEqual(expected, result)
6469

6570
def test_utils_data_to_json_dummy_object_with_property(self):
@@ -79,29 +84,50 @@ def test_utils_data_to_json_dummy_object_circular(self, func):
7984
data['payload']['dummy'] = data
8085

8186
result = stackify.utils.data_to_json(data)
82-
func.assert_called()
8387

84-
substring = "'dummy': <tests.test_utils.DummyProperty object at"
88+
substring = "'payload': {'dummy': "
89+
data_str = str(data)
90+
91+
assert func.called
92+
assert "Failed to serialize object to json: {}".format(data_str) in func.call_args_list[0][0][0]
93+
8594
self.assertTrue(substring in result)
95+
self.assertEqual(result, data_str)
8696

8797
def test_utils_data_to_json_dummy_request(self):
8898
dummy = DummyRequest()
8999
result = stackify.utils.data_to_json(dummy)
90100
substring = '{"_messages": "<tests.test_utils.Dummy object at'
91101
self.assertTrue(substring in result)
92102

103+
def test_utils_extract_request_dummy_wsgi_request(self):
104+
dummy = WSGIRequestMock()
105+
dummy.path = 'test'
106+
dummy.POST = 'test_form'
107+
dummy.not_exists = 'test'
108+
data = {
109+
'request': dummy
110+
}
111+
112+
result = stackify.utils.extract_request(data)
113+
request = result['request']
114+
115+
self.assertEqual(request['path'], 'test')
116+
self.assertEqual(request['form'], 'test_form')
117+
self.assertTrue('not_exists' not in request)
118+
93119

94120
class Dummy(object):
95121
pass
96122

97123

98-
class DummyInterable:
124+
class DummyIterable:
99125
def __iter__(self):
100126
self.a = 1
101127
return self
102128

103129
def __next__(self):
104-
if self.a <= 20:
130+
if self.a <= 10:
105131
x = self.a
106132
self.a += 1
107133
return x
@@ -122,5 +148,10 @@ def __init__(self):
122148
self._messages = Dummy()
123149

124150

151+
class WSGIRequestMock:
152+
def __init__(self):
153+
pass
154+
155+
125156
if __name__ == '__main__':
126157
unittest.main()

tests/transport/agent/test_agent_socket.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import retrying
33
from unittest import TestCase
44
from mock import patch
5+
import os
56

67
import stackify
78
from tests.bases import fake_retry_decorator
@@ -40,7 +41,10 @@ def test_send_should_use_defaut_socket(self, mock_post):
4041
self.agent_socket.send(url, message)
4142

4243
assert mock_post.called
43-
assert mock_post.call_args_list[0][0][0] == 'http+unix://%2Fusr%2Flocal%2Fstackify%2Fstackify.sock/test_url'
44+
if os.name == 'nt':
45+
assert mock_post.call_args_list[0][0][0] == 'http+unix://%2Fusr%2Flocal%2Fstackify%2Fstackify.sock\\test_url'
46+
else:
47+
assert mock_post.call_args_list[0][0][0] == 'http+unix://%2Fusr%2Flocal%2Fstackify%2Fstackify.sock/test_url'
4448

4549
@patch('requests_unixsocket.Session.post')
4650
def test_send_should_include_headers(self, mock_post):

0 commit comments

Comments
 (0)