From 76551a8eeac5d1e7071da0fb25e77e93c5040d69 Mon Sep 17 00:00:00 2001 From: Jussi Vatjus-Anttila Date: Fri, 2 Oct 2020 08:56:34 +0300 Subject: [PATCH] sync with latest pytest-lockable core functionality (#2) * update lockable implementation and rename file * add missing import * update tests * add missing docstring --- lockable/__init__.py | 3 +++ lockable/{plugin.py => lockable.py} | 19 +++++++++++++++---- tests/test_plugin.py | 11 ++++++----- 3 files changed, 24 insertions(+), 9 deletions(-) rename lockable/{plugin.py => lockable.py} (87%) diff --git a/lockable/__init__.py b/lockable/__init__.py index e69de29..a0cff9f 100644 --- a/lockable/__init__.py +++ b/lockable/__init__.py @@ -0,0 +1,3 @@ +""" Lockable module """ +from lockable.lockable import read_resources_list, parse_requirements, lock, \ + validate_json, ResourceNotFound, get_requirements diff --git a/lockable/plugin.py b/lockable/lockable.py similarity index 87% rename from lockable/plugin.py rename to lockable/lockable.py index 7b08e5d..a59b5f6 100644 --- a/lockable/plugin.py +++ b/lockable/lockable.py @@ -10,6 +10,10 @@ from filelock import Timeout, FileLock +class ResourceNotFound(Exception): + """ Exception raised when resource not found """ + + def read_resources_list(filename): """ Read resources json file """ with open(filename) as json_file: @@ -49,6 +53,10 @@ def parse_requirements(requirements_str): except ValueError: continue key, value = part.split('=') + if value.lower() == "true": + value = True + elif value.lower() == "false": + value = False requirements[key] = value return requirements @@ -58,13 +66,14 @@ def _try_lock(candidate, lock_folder): resource_id = candidate.get("id") try: lock_file = os.path.join(lock_folder, f"{resource_id}.lock") - lockable = FileLock(lock_file) - lockable.acquire(timeout=0) + _lockable = FileLock(lock_file) + _lockable.acquire(timeout=0) print(f'Allocated resource: {resource_id}') def release(): + nonlocal _lockable print(f'Release resource: {resource_id}') - lockable.release() + _lockable.release() try: os.remove(lock_file) except OSError as error: @@ -106,11 +115,13 @@ def lock(requirements: dict, """ Lock resource context """ local_resources = filter_(resource_list, requirements) random.shuffle(local_resources) + if not local_resources: + raise ResourceNotFound("Suitable resource not available") with _lock_some(local_resources, timeout_s, lock_folder, retry_interval) as resource: yield resource -def _get_requirements(requirements, hostname): +def get_requirements(requirements, hostname): """ Generate requirements""" print(f'hostname: {hostname}') return merge(dict(hostname=hostname, online=True), requirements) diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 91c0914..7295353 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -9,7 +9,7 @@ import multiprocessing as mp from tempfile import mktemp from contextlib import contextmanager -from lockable.plugin import read_resources_list, parse_requirements, lock, validate_json +from lockable.lockable import read_resources_list, parse_requirements, lock, validate_json, ResourceNotFound HOSTNAME = socket.gethostname() @@ -50,6 +50,7 @@ def test_parse_requirements(self): self.assertEqual(parse_requirements("a=b&b=2&"), dict(a='b', b="2")) self.assertEqual(parse_requirements('{"a":"c"}'), dict(a='c')) self.assertEqual(parse_requirements('{"a":"c", "d":2}'), dict(a='c', d=2)) + self.assertEqual(parse_requirements('a=true&d=False'), dict(a=True, d=False)) def test_lock_success(self): requirements = dict(id=1) @@ -66,18 +67,18 @@ def test_not_available(self): with lock(requirements=requirements, resource_list=resource_list, lock_folder='.', timeout_s=0.1): raise AssertionError('did not raise TimeoutError') - except TimeoutError: + except ResourceNotFound: pass self.assertFalse(os.path.exists('1.lock')) def test_not_online(self): - requirements = dict(id=1) + requirements = dict(id='1', online=True) resource_list = [dict(hostame=HOSTNAME, id='1', online=False)] try: with lock(requirements=requirements, resource_list=resource_list, lock_folder='.', timeout_s=0.1): - raise AssertionError('did not raise TimeoutError') - except TimeoutError: + raise AssertionError('did not raise ResourceNotFound') + except ResourceNotFound: pass self.assertFalse(os.path.exists('1.lock'))