Skip to content

Commit 69c3f66

Browse files
authored
Merge pull request #27 from pytest-dev/feature/zc-lockfile
Rely on zc.lockfile for file locking behavior
2 parents 985a760 + 1be24f5 commit 69c3f66

File tree

8 files changed

+34
-83
lines changed

8 files changed

+34
-83
lines changed

.travis.yml

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,6 @@ python: "2.7"
55
matrix:
66
include:
77
- env: TOXENV=linters
8-
- env: TOXENV=py27-pytest30
9-
- env: TOXENV=py27-pytest31
10-
- env: TOXENV=py27-pytest32
11-
- env: TOXENV=py27-pytest33
12-
- env: TOXENV=py27-pytest34
13-
- env: TOXENV=py27-pytest35
14-
- env: TOXENV=py27-pytest36
15-
- env: TOXENV=py27-pytest37
16-
- env: TOXENV=py27-pytest38
178
- env: TOXENV=py27-pytest39
189
- env: TOXENV=py27-pytest310
1910
- env: TOXENV=py27-pytestlatest

CHANGES.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,17 @@
11
Changelog
22
=========
33

4+
2.0.0
5+
-----
6+
7+
- #23: Rely on ``zc.lockfile`` for lockfile behavior.
8+
- #28: Fixtures now supports later versions of mysql and no longer
9+
support versions of mysql prior to ``mysql --initialize`` support.
10+
- #29: Fix issues with later versions of mysql where ``mysql_defaults_file``
11+
fixture would prevent startup of mysql.
12+
- Fixed issue in test suite where mysql fixture was not tested.
13+
- Removed ``pytest_services.locks.lock_file``.
14+
415
1.3.1
516
-----
617

pytest_services/locks.py

Lines changed: 7 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,33 @@
11
"""Fixtures for supporting a distributed test run."""
22
import contextlib
3-
import errno
4-
import fcntl
53
import json
64
import os
75
import socket
8-
import time
96

107
import pytest
8+
import zc.lockfile
119

1210
marker = object()
1311

1412

15-
def lock_file(filename, content, operation):
16-
"""Lock given file.
17-
18-
:param filename: full path to the lockfile
19-
:param content: content string to write to the lockfile after successful lock
20-
:param operation: os operation to use for lock
21-
:return: file object of opened locked file. Can be used to write content to it
22-
"""
23-
try:
24-
handle = os.fdopen(os.open(filename, os.O_RDWR | os.O_CREAT, 0o666), 'r+')
25-
except OSError as e:
26-
if e.errno == errno.EACCES:
27-
raise Exception('Failed to open/create file. Check permissions on containing folder')
28-
raise
29-
fcntl.flock(handle, operation)
30-
if content:
31-
handle.write(content)
32-
handle.flush()
33-
os.fsync(handle.fileno())
13+
def try_remove(filename):
3414
try:
35-
os.chmod(filename, 0o666)
15+
os.unlink(filename)
3616
except OSError:
3717
pass
38-
return handle
39-
40-
41-
def unlock_file(filename, handle, remove=True):
42-
"""Unlock given file."""
43-
fcntl.flock(handle, fcntl.LOCK_UN)
44-
handle.close()
45-
if remove:
46-
try:
47-
os.unlink(filename)
48-
except OSError:
49-
pass
5018

5119

5220
@contextlib.contextmanager
53-
def file_lock(filename, operation=fcntl.LOCK_EX, remove=True, timeout=20):
21+
def file_lock(filename, remove=True, timeout=20):
5422
"""A lock that is shared across processes.
5523
5624
:param filename: the name of the file that will be locked.
57-
:pram operation: the lock operation.
5825
5926
"""
60-
# http://bservices_log.vmfarms.com/2011/03/cross-process-locking-and.html
61-
times = 0
62-
while True:
63-
try:
64-
handle = lock_file(filename, None, operation)
65-
break
66-
except IOError:
67-
if times > timeout:
68-
raise Exception('Not able to aquire lock file {0} in {1}'.format(filename, timeout))
69-
time.sleep(0.5)
70-
times += 1
27+
with contextlib.closing(zc.lockfile.SimpleLockFile(filename)) as lockfile:
28+
yield lockfile._fp
7129

72-
try:
73-
yield handle
74-
finally:
75-
unlock_file(filename, handle, remove=remove)
30+
remove and try_remove(filename)
7631

7732

7833
def unlock_resource(name, resource, lock_dir, services_log):

pytest_services/mysql.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,12 @@
1212

1313

1414
@pytest.fixture(scope='session')
15-
def mysql_defaults_file(run_services, mysql_data_dir, memory_temp_dir):
15+
def mysql_defaults_file(
16+
run_services, tmp_path_factory, memory_temp_dir, request):
1617
"""MySQL defaults file."""
1718
if run_services:
18-
defaults_path = os.path.join(mysql_data_dir, 'defaults.cnf')
19+
cfg = tmp_path_factory.mktemp(request.session.name)
20+
defaults_path = str(cfg / 'defaults.cnf')
1921

2022
with open(defaults_path, 'w+') as fd:
2123
fd.write("""
@@ -47,14 +49,15 @@ def mysql_system_database(
4749
):
4850
"""Install database to given path."""
4951
if run_services:
50-
mysql_install_db = find_executable('mysql_install_db')
51-
assert mysql_install_db, 'You have to install mysql_install_db script.'
52+
mysqld = find_executable('mysqld')
53+
assert mysqld, 'You have to install mysqld script.'
5254

5355
try:
54-
services_log.debug('Starting mysql_install_db.')
56+
services_log.debug('Starting mysqld.')
5557
check_output([
56-
mysql_install_db,
58+
mysqld,
5759
'--defaults-file={0}'.format(mysql_defaults_file),
60+
'--initialize-insecure',
5861
'--datadir={0}'.format(mysql_data_dir),
5962
'--basedir={0}'.format(mysql_base_dir),
6063
'--user={0}'.format(os.environ['USER'])
@@ -65,7 +68,7 @@ def mysql_system_database(
6568
'Please ensure you disabled apparmor for /run/shm/** or for whole mysql'.format(e=e))
6669
raise
6770
finally:
68-
services_log.debug('mysql_install_db was executed.')
71+
services_log.debug('mysqld was executed.')
6972

7073

7174
@pytest.fixture(scope='session')

pytest_services/xvfb.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
"""Fixtures for the GUI environment."""
22
import os
3-
import fcntl
43
import socket
54
import re
65
try:
@@ -71,7 +70,7 @@ def xvfb(request, run_services, xvfb_display, lock_dir, xvfb_resolution, watcher
7170
listen_args = []
7271

7372
with file_lock(os.path.join(lock_dir, 'xvfb_{0}.lock'.format(xvfb_display)),
74-
operation=fcntl.LOCK_EX | fcntl.LOCK_NB):
73+
):
7574

7675
def checker():
7776
try:

setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
'requests',
2020
'psutil',
2121
'pytest',
22+
'zc.lockfile >= 2.0',
2223
]
2324

2425
PY2 = sys.version_info[0] < 3

tests/test_plugin.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ def test_memcached(request, memcached, memcached_socket):
1717
assert mc.get('some') is None
1818

1919

20-
def test_mysql(mysql, mysql_connection):
20+
def test_mysql(mysql, mysql_connection, mysql_socket):
2121
"""Test mysql service."""
22-
conn = MySQLdb.connect(user='root')
22+
conn = MySQLdb.connect(user='root', unix_socket=mysql_socket)
2323
assert conn
2424

2525

tox.ini

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,18 @@
22
distshare={homedir}/.tox/distshare
33
envlist=
44
linters
5-
py27-pytest{30,31,32,33,34,35,36,37,38,39,310,latest}
5+
py27-pytest{39,310,latest}
66
py{34,35,36}-pytestlatest
77
py27-pytestlatest-xdist
88
py27-pytestlatest-coveralls
99
skip_missing_interpreters = true
1010

1111
[testenv]
12-
commands= py.test tests --junitxml={envlogdir}/junit-{envname}.xml
12+
commands= py.test tests --junitxml={envlogdir}/junit-{envname}.xml {posargs}
1313
deps =
1414
pytestlatest: pytest
1515
pytest310: pytest~=3.10.0
1616
pytest39: pytest~=3.9.0
17-
pytest38: pytest~=3.8.0
18-
pytest37: pytest~=3.7.0
19-
pytest36: pytest~=3.6.0
20-
pytest35: pytest~=3.5.0
21-
pytest34: pytest~=3.4.0
22-
pytest33: pytest~=3.3.0
23-
pytest32: pytest~=3.2.0
24-
pytest31: pytest~=3.1.0
25-
pytest30: pytest~=3.0.0
2617
xdist: pytest-xdist
2718
-r{toxinidir}/requirements-testing.txt
2819
passenv = DISPLAY COVERALLS_REPO_TOKEN USER TRAVIS TRAVIS_JOB_ID TRAVIS_BRANCH

0 commit comments

Comments
 (0)