Skip to content

Commit

Permalink
Merge pull request pytest-dev#7 from werehuman/profiling-long-file-names
Browse files Browse the repository at this point in the history
fix: avoid errors when filename is too long
  • Loading branch information
eeaston committed Feb 15, 2016
2 parents ca1dd18 + 87fc483 commit 7380fff
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 8 deletions.
9 changes: 9 additions & 0 deletions pytest-profiling/README.md
Expand Up @@ -89,6 +89,15 @@ pstats files (one per test item) are retained for later analysis in `prof` direc
test_import.prof
```

Profiling plugin by default names pstats filename as corresponding test name. If full path is longer that operation system allows then plugin renames it to first 4 bytes of md5 hash of test name:

```bash
$ ls -1 prof/
combined.prof
test_not_longer_than_max_allowed.prof
68b329da.prof
```

If the ``--profile-svg`` option is given, along with the prof files and tabular output a svg file will be generated:

```bash
Expand Down
24 changes: 21 additions & 3 deletions pytest-profiling/pytest_profiling.py
Expand Up @@ -5,6 +5,11 @@
import cProfile
import pstats
import pipes
import errno
from hashlib import md5


LARGE_FILENAME_HASH_LEN = 8


class Profiling(object):
Expand Down Expand Up @@ -50,9 +55,22 @@ def pytest_pyfunc_call(self, __multicall__, pyfuncitem):
"""Hook into pytest_pyfunc_call; marked as a tryfirst hook so that we
can call everyone else inside `cProfile.runctx`.
"""
prof = os.path.join("prof", pyfuncitem.name + ".prof")
cProfile.runctx("fn()", globals(), dict(fn=__multicall__.execute), filename=prof)
self.profs.append(prof)
prof = cProfile.Profile()
prof.runctx("fn()", globals(), dict(fn=__multicall__.execute))
prof_filename = os.path.join("prof", pyfuncitem.name + ".prof")
try:
prof.dump_stats(prof_filename)
except EnvironmentError as err:
if err.errno != errno.ENAMETOOLONG:
raise

if len(pyfuncitem.name) < LARGE_FILENAME_HASH_LEN:
raise

hash_str = md5(pyfuncitem.name).hexdigest()[:LARGE_FILENAME_HASH_LEN]
prof_filename = os.path.join("prof", hash_str + ".prof")
prof.dump_stats(prof_filename)
self.profs.append(prof_filename)


def pytest_addoption(parser):
Expand Down
13 changes: 8 additions & 5 deletions pytest-profiling/tests/unit/test_profile.py
Expand Up @@ -22,13 +22,16 @@ def test_hooks_pyfunc_call():
multicall, pyfuncitem, plugin = Mock(), Mock(), Profiling(False)
pyfuncitem.name.__add__ = Mock()
with patch('os.path.join', return_value=sentinel.join):
with patch('pytest_profiling.cProfile') as cProfile:
with patch('pytest_profiling.cProfile.Profile') as Profile:
plugin.pytest_pyfunc_call(multicall, pyfuncitem)
assert cProfile.runctx.called
args, kwargs = cProfile.runctx.call_args
assert kwargs['filename'] == sentinel.join
prof = Profile() # mock instances are singletons
assert prof.runctx.called
assert prof.dump_stats.called
runctx_args, _ = prof.runctx.call_args
(dump_stats_filename, ), _ = prof.dump_stats.call_args
assert dump_stats_filename == sentinel.join
assert not multicall.execute.called
eval(*args)
eval(*runctx_args)
assert multicall.execute.called
assert plugin.profs == [sentinel.join]

Expand Down

0 comments on commit 7380fff

Please sign in to comment.