diff options
author | Bruno Oliveira <nicoddemus@gmail.com> | 2018-04-11 19:41:10 -0300 |
---|---|---|
committer | Bruno Oliveira <nicoddemus@gmail.com> | 2018-04-12 08:19:28 -0300 |
commit | aa95a425d7708d80fc887b1825662daf3a624ba7 (patch) | |
tree | 4503bf1a8b1f2e909c74343d5d59d7053032e8ea /_pytest | |
parent | f79b0324fe21eb35918ca67421f9967fa23c0e3b (diff) | |
download | pytest-aa95a425d7708d80fc887b1825662daf3a624ba7.tar.gz |
Attempt to solve race-condition which corrupts .pyc files on Windows
This uses of the `atomicwrites` library.
This is very hard to create a reliable test for.
Fix #3008
Diffstat (limited to '_pytest')
-rw-r--r-- | _pytest/assertion/rewrite.py | 41 |
1 files changed, 14 insertions, 27 deletions
diff --git a/_pytest/assertion/rewrite.py b/_pytest/assertion/rewrite.py index db3674930..0499a792f 100644 --- a/_pytest/assertion/rewrite.py +++ b/_pytest/assertion/rewrite.py @@ -12,7 +12,9 @@ import struct import sys import types +import atomicwrites import py + from _pytest.assertion import util @@ -140,7 +142,7 @@ class AssertionRewritingHook(object): # Probably a SyntaxError in the test. return None if write: - _make_rewritten_pyc(state, source_stat, pyc, co) + _write_pyc(state, co, source_stat, pyc) else: state.trace("found cached rewritten pyc for %r" % (fn,)) self.modules[name] = co, pyc @@ -258,22 +260,21 @@ def _write_pyc(state, co, source_stat, pyc): # sometime to be able to use imp.load_compiled to load them. (See # the comment in load_module above.) try: - fp = open(pyc, "wb") - except IOError: - err = sys.exc_info()[1].errno - state.trace("error writing pyc file at %s: errno=%s" % (pyc, err)) + with atomicwrites.atomic_write(pyc, mode="wb", overwrite=True) as fp: + fp.write(imp.get_magic()) + mtime = int(source_stat.mtime) + size = source_stat.size & 0xFFFFFFFF + fp.write(struct.pack("<ll", mtime, size)) + if six.PY2: + marshal.dump(co, fp.file) + else: + marshal.dump(co, fp) + except EnvironmentError as e: + state.trace("error writing pyc file at %s: errno=%s" % (pyc, e.errno)) # we ignore any failure to write the cache file # there are many reasons, permission-denied, __pycache__ being a # file etc. return False - try: - fp.write(imp.get_magic()) - mtime = int(source_stat.mtime) - size = source_stat.size & 0xFFFFFFFF - fp.write(struct.pack("<ll", mtime, size)) - marshal.dump(co, fp) - finally: - fp.close() return True @@ -338,20 +339,6 @@ def _rewrite_test(config, fn): return stat, co -def _make_rewritten_pyc(state, source_stat, pyc, co): - """Try to dump rewritten code to *pyc*.""" - if sys.platform.startswith("win"): - # Windows grants exclusive access to open files and doesn't have atomic - # rename, so just write into the final file. - _write_pyc(state, co, source_stat, pyc) - else: - # When not on windows, assume rename is atomic. Dump the code object - # into a file specific to this process and atomically replace it. - proc_pyc = pyc + "." + str(os.getpid()) - if _write_pyc(state, co, source_stat, proc_pyc): - os.rename(proc_pyc, pyc) - - def _read_pyc(source, pyc, trace=lambda x: None): """Possibly read a pytest pyc containing rewritten code. |