forked from jazzband/django-silk
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
jazzband#33 organise cprofile output as a sortable table (jazzband#200)
* add .venv* to .gitignore * made the profile output a sortable table with links to the appropriate source code * remove memoization of get_dot function since this was causing problem on python 2, and should probably be in a separate PR * removed python 3 only spitlines * fixed failing test due to PY3/2 differences in profile output * fixed failing test due difference in python 3.4 * fixed failing test due to floating point precision * fixed problem due to trying to import abs from math * reverted incorrect attempt to fix flaky test due to floating point precision
- Loading branch information
1 parent
3f416c3
commit ff50df3
Showing
10 changed files
with
231 additions
and
39 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,3 +13,4 @@ factory-boy==2.8.1 | |
freezegun==0.3.5 | ||
networkx==1.11 | ||
pydotplus==2.0.2 | ||
contextlib2==0.5.5 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
from collections import namedtuple | ||
from django.test import TestCase | ||
from silk.views.code import _code, _code_context, _code_context_from_request | ||
|
||
|
||
FILE_PATH = __file__ | ||
LINE_NUM = 5 | ||
END_LINE_NUM = 10 | ||
|
||
with open(__file__) as f: | ||
ACTUAL_LINES = [l + '\n' for l in f.read().split('\n')] | ||
|
||
|
||
class CodeTestCase(TestCase): | ||
|
||
def assertActualLineEqual(self, actual_line, end_line_num=None): | ||
expected_actual_line = ACTUAL_LINES[LINE_NUM - 1:end_line_num or LINE_NUM] | ||
self.assertEqual(actual_line, expected_actual_line) | ||
|
||
def assertCodeEqual(self, code): | ||
expected_code = [line.strip('\n') for line in ACTUAL_LINES[0:LINE_NUM + 10]] + [''] | ||
self.assertEqual(code, expected_code) | ||
|
||
def test_code(self): | ||
for end_line_num in None, END_LINE_NUM: | ||
actual_line, code = _code(FILE_PATH, LINE_NUM, end_line_num) | ||
self.assertActualLineEqual(actual_line, end_line_num) | ||
self.assertCodeEqual(code) | ||
|
||
def test_code_context(self): | ||
for end_line_num in None, END_LINE_NUM: | ||
for prefix in '', 'salchicha_': | ||
context = _code_context(FILE_PATH, LINE_NUM, end_line_num, prefix) | ||
self.assertActualLineEqual(context[prefix + 'actual_line'], end_line_num) | ||
self.assertCodeEqual(context[prefix + 'code']) | ||
self.assertEqual(context[prefix + 'file_path'], FILE_PATH) | ||
self.assertEqual(context[prefix + 'line_num'], LINE_NUM) | ||
|
||
def test_code_context_from_request(self): | ||
for end_line_num in None, END_LINE_NUM: | ||
for prefix in '', 'salchicha_': | ||
request = namedtuple('Request', 'GET')(dict(file_path=FILE_PATH, line_num=LINE_NUM)) | ||
context = _code_context_from_request(request, end_line_num, prefix) | ||
self.assertActualLineEqual(context[prefix + 'actual_line'], end_line_num) | ||
self.assertCodeEqual(context[prefix + 'code']) | ||
self.assertEqual(context[prefix + 'file_path'], FILE_PATH) | ||
self.assertEqual(context[prefix + 'line_num'], LINE_NUM) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
# future | ||
from __future__ import print_function | ||
# std | ||
import cProfile | ||
import sys | ||
# 3rd party | ||
import contextlib2 as contextlib | ||
from six import StringIO, PY3 | ||
from django.test import TestCase | ||
# silk | ||
from silk.utils.profile_parser import parse_profile | ||
|
||
|
||
class ProfileParserTestCase(TestCase): | ||
|
||
def test_profile_parser(self): | ||
""" | ||
Verify that the function parse_profile produces the expected output. | ||
""" | ||
with contextlib.closing(StringIO()) as stream: | ||
with contextlib.redirect_stdout(stream): | ||
cProfile.run('print()') | ||
stream.seek(0) | ||
actual = list(parse_profile(stream)) | ||
if PY3: | ||
if sys.version_info < (3,5): | ||
expected = [ | ||
['ncalls', 'tottime', 'percall', 'cumtime', 'percall', 'filename:lineno(function)'], | ||
['1', '0.000', '0.000', '0.000', '0.000', '<string>:1(<module>)'], | ||
['1', '0.000', '0.000', '0.000', '0.000', '{built-in method exec}'], | ||
['1', '0.000', '0.000', '0.000', '0.000', '{built-in method print}'], | ||
['1', '0.000', '0.000', '0.000', '0.000', "{method 'disable' of '_lsprof.Profiler' objects}"], | ||
] | ||
else: | ||
expected = [ | ||
['ncalls', 'tottime', 'percall', 'cumtime', 'percall', 'filename:lineno(function)'], | ||
['1', '0.000', '0.000', '0.000', '0.000', '<string>:1(<module>)'], | ||
['1', '0.000', '0.000', '0.000', '0.000', '{built-in method builtins.exec}'], | ||
['1', '0.000', '0.000', '0.000', '0.000', '{built-in method builtins.print}'], | ||
['1', '0.000', '0.000', '0.000', '0.000', "{method 'disable' of '_lsprof.Profiler' objects}"], | ||
] | ||
else: | ||
expected = [ | ||
['ncalls', 'tottime', 'percall', 'cumtime', 'percall', 'filename:lineno(function)'], | ||
['1', '0.000', '0.000', '0.000', '0.000', '<string>:1(<module>)'], | ||
['2', '0.000', '0.000', '0.000', '0.000', 'StringIO.py:208(write)'], | ||
['2', '0.000', '0.000', '0.000', '0.000', 'StringIO.py:38(_complain_ifclosed)'], | ||
['2', '0.000', '0.000', '0.000', '0.000', '{isinstance}'], | ||
['2', '0.000', '0.000', '0.000', '0.000', '{len}'], | ||
['2', '0.000', '0.000', '0.000', '0.000', "{method 'append' of 'list' objects}"], | ||
['1', '0.000', '0.000', '0.000', '0.000', "{method 'disable' of '_lsprof.Profiler' objects}"] | ||
] | ||
|
||
self.assertListEqual(actual, expected) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
from six import text_type | ||
import re | ||
|
||
|
||
_pattern = re.compile(' +') | ||
|
||
|
||
def parse_profile(output): | ||
""" | ||
Parse the output of cProfile to a list of tuples. | ||
""" | ||
if isinstance(output, text_type): | ||
output = output.split('\n') | ||
for i, line in enumerate(output): | ||
# ignore n function calls, total time and ordered by and empty lines | ||
line = line.strip() | ||
if i > 3 and line: | ||
columns = _pattern.split(line)[0:] | ||
function = ' '.join(columns[5:]) | ||
columns = columns[:5] + [function] | ||
yield columns |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters