only try to create the __pycache__ dir (not a tree to it) fixes #60

Also, improve error handling surrounding __pycache__ creation.
This commit is contained in:
Benjamin Peterson 2011-07-25 21:40:38 -05:00
parent f86c8469f5
commit 643ab120f4
3 changed files with 54 additions and 12 deletions

View File

@ -1,6 +1,7 @@
Changes between 2.1.0 and 2.1.1.DEV
----------------------------------------------
- fix error conditions involving the creation of __pycache__
- fix assertion rewriting on inserts involving strings containing '%'
- fix assertion rewriting on calls with a ** arg
- don't cache rewritten modules if bytecode generation is disabled

View File

@ -2,6 +2,7 @@
import ast
import collections
import errno
import itertools
import imp
import marshal
@ -97,15 +98,23 @@ class AssertionRewritingHook(object):
cache_dir = os.path.join(fn_pypath.dirname, "__pycache__")
if write:
try:
py.path.local(cache_dir).ensure(dir=True)
except py.error.EACCES:
state.trace("read only directory: %r" % (fn_pypath.dirname,))
write = False
except py.error.EEXIST:
state.trace("failure to create directory: %r" % (
fn_pypath.dirname,))
raise
#write = False
os.mkdir(cache_dir)
except OSError:
e = sys.exc_info()[1].errno
if e == errno.EEXIST:
# Either the __pycache__ directory already exists (the
# common case) or it's blocked by a non-dir node. In the
# latter case, we'll ignore it in _write_pyc.
pass
elif e == errno.ENOTDIR:
# One of the path components was not a directory, likely
# because we're in a zip file.
write = False
elif e == errno.EACCES:
state.trace("read only directory: %r" % (fn_pypath.dirname,))
write = False
else:
raise
cache_name = fn_pypath.basename[:-3] + "." + PYTEST_TAG + ".pyc"
pyc = os.path.join(cache_dir, cache_name)
# Notice that even if we're in a read-only directory, I'm going to check
@ -146,13 +155,21 @@ def _write_pyc(co, source_path, pyc):
# little reason deviate, and I hope sometime to be able to use
# imp.load_compiled to load them. (See the comment in load_module above.)
mtime = int(source_path.mtime())
fp = open(pyc, "wb")
try:
fp = open(pyc, "wb")
except IOError:
if sys.exc_info()[1].errno == errno.ENOTDIR:
# This happens when we get a EEXIST in find_module creating the
# __pycache__ directory and __pycache__ is by some non-dir node.
return False
raise
try:
fp.write(imp.get_magic())
fp.write(struct.pack("<l", mtime))
marshal.dump(co, fp)
finally:
fp.close()
return True
def _rewrite_test(state, fn):
"""Try to read and rewrite *fn* and return the code object."""
@ -186,8 +203,8 @@ def _make_rewritten_pyc(state, fn, pyc, co):
# 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())
_write_pyc(co, fn, proc_pyc)
os.rename(proc_pyc, pyc)
if _write_pyc(co, fn, proc_pyc):
os.rename(proc_pyc, pyc)
return co
def _read_pyc(source, pyc):

View File

@ -1,4 +1,5 @@
import sys
import zipfile
import py
import pytest
@ -283,6 +284,29 @@ class TestAssertionRewrite:
class TestRewriteOnImport:
def test_pycache_is_a_file(self, testdir):
testdir.tmpdir.join("__pycache__").write("Hello")
testdir.makepyfile("""
def test_rewritten():
assert "@py_builtins" in globals()""")
assert testdir.runpytest().ret == 0
def test_zipfile(self, testdir):
z = testdir.tmpdir.join("myzip.zip")
z_fn = str(z)
f = zipfile.ZipFile(z_fn, "w")
try:
f.writestr("test_gum/__init__.py", "")
f.writestr("test_gum/test_lizard.py", "")
finally:
f.close()
z.chmod(256)
testdir.makepyfile("""
import sys
sys.path.append(%r)
import test_gum.test_lizard""" % (z_fn,))
assert testdir.runpytest().ret == 0
def test_readonly(self, testdir):
sub = testdir.mkdir("testing")
sub.join("test_readonly.py").write(