Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix windows #157

Merged
merged 15 commits into from
Nov 9, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 11 additions & 6 deletions py/_path/local.py
Original file line number Diff line number Diff line change
Expand Up @@ -781,14 +781,15 @@ def _gethomedir(cls):
# """
# special class constructors for local filesystem paths
# """
@classmethod
def get_temproot(cls):
""" return the system's temporary directory
(where tempfiles are usually created in)
"""
import tempfile
return py.path.local(tempfile.gettempdir())
get_temproot = classmethod(get_temproot)

@classmethod
def mkdtemp(cls, rootdir=None):
""" return a Path object pointing to a fresh new temporary directory
(which we created ourself).
Expand All @@ -797,7 +798,6 @@ def mkdtemp(cls, rootdir=None):
if rootdir is None:
rootdir = cls.get_temproot()
return cls(py.error.checked_call(tempfile.mkdtemp, dir=str(rootdir)))
mkdtemp = classmethod(mkdtemp)

def make_numbered_dir(cls, prefix='session-', rootdir=None, keep=3,
lock_timeout = 172800): # two days
Expand Down Expand Up @@ -827,7 +827,9 @@ def create_lockfile(path):
if hasattr(lockfile, 'mksymlinkto'):
lockfile.mksymlinkto(str(mypid))
else:
lockfile.write(str(mypid), 'wx')
fd = py.error.checked_call(os.open, str(lockfile), os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o644)
with os.fdopen(fd, 'w') as f:
f.write(str(mypid))
return lockfile

def atexit_remove_lockfile(lockfile):
Expand Down Expand Up @@ -862,12 +864,15 @@ def try_remove_lockfile():
if lock_timeout:
lockfile = create_lockfile(udir)
atexit_remove_lockfile(lockfile)
except (py.error.EEXIST, py.error.ENOENT):
except (py.error.EEXIST, py.error.ENOENT, py.error.EBUSY):
# race condition (1): another thread/process created the dir
# in the meantime - try again
# race condition (2): another thread/process spuriously acquired
# lock treating empty directory as candidate
# for removal - try again
# race condition (3): another thread/process tried to create the lock at
# the same time (happened in Python 3.3 on Windows)
# https://ci.appveyor.com/project/pytestbot/py/build/1.0.21/job/ffi85j4c0lqwsfwa
if lastmax == maxnum:
raise
lastmax = maxnum
Expand Down Expand Up @@ -898,7 +903,7 @@ def is_garbage(path):
# try acquiring lock to remove directory as exclusive user
if lock_timeout:
create_lockfile(path)
except (py.error.EEXIST, py.error.ENOENT):
except (py.error.EEXIST, py.error.ENOENT, py.error.EBUSY):
path_time = get_mtime(path)
if not path_time:
# assume directory doesn't exist now
Expand All @@ -908,7 +913,7 @@ def is_garbage(path):
# and lock timeout hasn't expired yet
continue

# path dir locked for exlusive use
# path dir locked for exclusive use
# and scheduled for removal to avoid another thread/process
# treating it as a new directory or removal candidate
garbage_path = rootdir.join(garbage_prefix + str(uuid.uuid4()))
Expand Down
8 changes: 1 addition & 7 deletions testing/code/test_assertion.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,7 @@ def test_assert_within_finally():
i = 42
""")
s = excinfo.exconly()
assert re.search("division.+by zero", s) is not None

#def g():
# A.f()
#excinfo = getexcinfo(TypeError, g)
#msg = getmsg(excinfo)
#assert msg.find("must be called with A") != -1
assert re.search("ZeroDivisionError:.*division", s) is not None


def test_assert_multiline_1():
Expand Down
5 changes: 4 additions & 1 deletion testing/log/test_log.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import py
import sys

from py._log.log import default_keywordmapper
import pytest

callcapture = py.io.StdCapture.call

default_keywordmapper = pytest.importorskip('py._log.log.default_keywordmapper')

def setup_module(mod):
mod._oldstate = default_keywordmapper.getstate()

Expand All @@ -13,6 +15,7 @@ def teardown_module(mod):

class TestLogProducer:
def setup_method(self, meth):
from py._log.log import default_keywordmapper
default_keywordmapper.setstate(_oldstate)

def test_getstate_setstate(self):
Expand Down
10 changes: 10 additions & 0 deletions testing/log/test_warning.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
import sys
from distutils.version import LooseVersion

import pytest

import py

mypath = py.path.local(__file__).new(ext=".py")


win = sys.platform.startswith('win')
pytestmark = pytest.mark.skipif(win and LooseVersion(pytest.__version__) >= LooseVersion('3.1'),
reason='apiwarn is not compatible with pytest >= 3.1 (#162)')


@pytest.mark.xfail
def test_forwarding_to_warnings_module():
pytest.deprecated_call(py.log._apiwarn, "1.3", "..")
Expand Down
18 changes: 11 additions & 7 deletions testing/path/test_local.py
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,17 @@ def test_fspath_protocol_other_class(self, fake_fspath_obj):
assert py_path.join(fake_fspath_obj).strpath == os.path.join(
py_path.strpath, str_path)

@pytest.mark.skipif(sys.version_info[:2] == (2, 6) and sys.platform.startswith('win'),
reason='multiprocessing bug in Python 2.6/Windows prevents this test '
'from working as intended '
'(see https://bugs.python.org/issue10845 and #157).')
def test_make_numbered_dir_multiprocess_safe(self, tmpdir):
# https://github.com/pytest-dev/py/issues/30
pool = multiprocessing.Pool()
results = [pool.apply_async(batch_make_numbered_dirs, [tmpdir, 100]) for _ in range(20)]
for r in results:
assert r.get()


class TestExecutionOnWindows:
pytestmark = win32only
Expand Down Expand Up @@ -446,13 +457,6 @@ def notimpl(x, y):
assert x.relto(tmpdir)
assert x.check()

def test_make_numbered_dir_multiprocess_safe(self, tmpdir):
Copy link
Member Author

@nicoddemus nicoddemus Nov 5, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason why this test was not catching the regression in the previous locking code for Windows is that it was being skipped on Windows (this class has the pytestmark = skiponwin32 mark).

# https://github.com/pytest-dev/py/issues/30
pool = multiprocessing.Pool()
results = [pool.apply_async(batch_make_numbered_dirs, [tmpdir, 100]) for _ in range(20)]
for r in results:
assert r.get() == True

def test_locked_make_numbered_dir(self, tmpdir):
for i in range(10):
numdir = local.make_numbered_dir(prefix='base2.', rootdir=tmpdir,
Expand Down
7 changes: 6 additions & 1 deletion testing/path/test_svnauth.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import py
import svntestbase
from py.path import SvnAuth
import time
import sys
Expand Down Expand Up @@ -323,6 +322,12 @@ def test_log(self, setup):
assert log[0].msg == 'added foo.txt'

def test_switch(self, setup):
import pytest
try:
import xdist
pytest.skip('#160: fails under xdist')
except ImportError:
pass
wc = py.path.svnwc(setup.temppath, auth=setup.auth)
svnurl = 'svn://localhost:%s/%s' % (setup.port, setup.repopath.basename)
wc.checkout(svnurl)
Expand Down
9 changes: 8 additions & 1 deletion testing/path/test_svnwc.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@
from py._path import svnwc as svncommon
from svntestbase import CommonSvnTests


pytestmark = pytest.mark.xfail(sys.platform.startswith('win'),
reason='#161 all tests in this file are failing on Windows',
run=False)


def test_make_repo(path1, tmpdir):
repo = tmpdir.join("repo")
py.process.cmdexec('svnadmin create %s' % repo)
Expand Down Expand Up @@ -106,8 +112,9 @@ def test_status_unchanged(self, path1):
assert r.join('sampledir/otherfile').basename in [item.basename
for item in s.unchanged]

@pytest.mark.xfail(reason="svn-1.7 has buggy 'status --xml' output")
def test_status_update(self, path1):
# not a mark because the global "pytestmark" will end up overwriting a mark here
pytest.xfail("svn-1.7 has buggy 'status --xml' output")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is fixed in recent pytest and should be in the next feature release (the evaluator refactoring)

r = path1
try:
r.update(rev=1)
Expand Down
4 changes: 4 additions & 0 deletions testing/root/test_py_imports.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

@py.test.mark.parametrize('name', [x for x in dir(py) if x[0] != '_'])
def test_dir(name):
if name == 'log' and sys.platform.startswith('win'):
py.test.skip('syslog is not available on Windows')
obj = getattr(py, name)
if hasattr(obj, '__map__'): # isinstance(obj, Module):
keys = dir(obj)
Expand Down Expand Up @@ -52,6 +54,8 @@ def recurse(p):


def check_import(modpath):
if modpath == 'py._log.log' and sys.platform.startswith('win'):
py.test.skip('syslog is not available on Windows')
py.builtin.print_("checking import", modpath)
assert __import__(modpath)

Expand Down