Skip to content

Commit

Permalink
add get and __str__ methods for Allocation (#22)
Browse files Browse the repository at this point in the history
* add get and __str__ methods for Allocation
* refactor Allocation to own file and own unit tests
  • Loading branch information
jupe authored Oct 23, 2021
1 parent 60c46bc commit 1f86ff1
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 63 deletions.
54 changes: 54 additions & 0 deletions lockable/allocation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
""" Allocation context module """
from uuid import uuid1
from dataclasses import dataclass
from typing import Union
from datetime import datetime, timedelta


@dataclass
class Allocation:
"""
Reservation dataclass
"""
requirements: dict
resource_info: dict
_release: callable
pid_file: str
allocation_queue_time: timedelta = None # how long to wait before resource allocated
allocation_start_time: datetime = datetime.now()
release_time: Union[datetime, None] = None
alloc_id: str = str(uuid1())

def get(self, key):
""" Get resource information by key """
return self.resource_info.get(key)

def __str__(self):
info = ', '.join([f'{k}={v}' for k, v in self.resource_info.items()])
return f'Allocation(queue_time: {self.allocation_queue_time}, resource_info: {info})'

@property
def resource_id(self):
""" resource id getter """
return self.resource_info['id']

def release(self, alloc_id: str):
""" Release resource when selecting alloc_id """
assert self.alloc_id is not None, 'already released resource'
assert self.alloc_id == alloc_id, 'Allocation id mismatch'
self._release()
self.alloc_id = None
self.release_time = datetime.now()

def unlock(self):
""" Unlock/Release resource without alloc_id """
self.release(self.alloc_id)

@property
def allocation_durations(self) -> timedelta:
"""
Get allocation duration
If allocation is not ended, returnallocation duration so far.
"""
end_time = self.release_time or datetime.now()
return end_time - self.allocation_start_time
47 changes: 2 additions & 45 deletions lockable/lockable.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,60 +5,17 @@
import os
import time
import tempfile
from typing import Union
from datetime import datetime, timedelta
from dataclasses import dataclass
from datetime import datetime
from contextlib import contextmanager
from uuid import uuid1
from pydash import filter_, merge
from pid import PidFile, PidFileError
from lockable.provider_helpers import create as create_provider
from lockable.logger import get_logger
from lockable.allocation import Allocation

MODULE_LOGGER = get_logger()


@dataclass
class Allocation:
"""
Reservation dataclass
"""
requirements: dict
resource_info: dict
_release: callable
pid_file: str
allocation_queue_time: timedelta = None # how long to wait before resource allocated
allocation_start_time: datetime = datetime.now()
release_time: Union[datetime, None] = None
alloc_id: str = str(uuid1())

@property
def resource_id(self):
""" resource id getter """
return self.resource_info['id']

def release(self, alloc_id: str):
""" Release resource when selecting alloc_id """
assert self.alloc_id is not None, 'already released resource'
assert self.alloc_id == alloc_id, 'Allocation id mismatch'
self._release()
self.alloc_id = None
self.release_time = datetime.now()

def unlock(self):
""" Unlock/Release resource without alloc_id """
self.release(self.alloc_id)

@property
def allocation_durations(self) -> timedelta:
"""
Get allocation duration
If allocation is not ended, returnallocation duration so far.
"""
end_time = self.release_time or datetime.now()
return end_time - self.allocation_start_time


class ResourceNotFound(Exception):
""" Exception raised when resource not found """

Expand Down
19 changes: 1 addition & 18 deletions tests/test_Lockable.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from tempfile import TemporaryDirectory
from unittest import TestCase

from lockable.lockable import Lockable, ResourceNotFound, Allocation, timedelta
from lockable.lockable import Lockable, ResourceNotFound, Allocation


@contextmanager
Expand Down Expand Up @@ -165,23 +165,6 @@ def test_unlock(self):
lockable.unlock(allocation)
self.assertFalse(os.path.exists(lock_file))

def test_allocation_object(self):
alloc = Allocation(requirements={},
resource_info={'id': 1},
alloc_id=1,
pid_file=1,
_release=lambda: True)
self.assertEqual(alloc.resource_id, 1)
self.assertIsInstance(alloc.allocation_durations, timedelta)
with self.assertRaises(AssertionError):
alloc.release(2)
alloc.release(1)
with self.assertRaises(AssertionError):
alloc.unlock()
self.assertEqual(alloc.allocation_durations,
alloc.release_time - alloc.allocation_start_time)
self.assertEqual(alloc.allocation_queue_time, None)

def test_lock_offline(self):
with TemporaryDirectory() as tmpdirname:
list_file = os.path.join(tmpdirname, 'test.json')
Expand Down
30 changes: 30 additions & 0 deletions tests/test_allocation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from unittest import TestCase
import logging
from lockable.allocation import Allocation, timedelta


class LockableTests(TestCase):

def setUp(self) -> None:
logger = logging.getLogger('lockable')
logger.handlers.clear()
logger.addHandler(logging.NullHandler())

def test_allocation_object(self):
alloc = Allocation(requirements={},
resource_info={'id': 1},
alloc_id=1,
pid_file=1,
_release=lambda: True)
self.assertEqual(alloc.resource_id, 1)
self.assertIsInstance(alloc.allocation_durations, timedelta)
self.assertEqual(alloc.get('id'), 1)
with self.assertRaises(AssertionError):
alloc.release(2)
alloc.release(1)
with self.assertRaises(AssertionError):
alloc.unlock()
self.assertEqual(alloc.allocation_durations,
alloc.release_time - alloc.allocation_start_time)
self.assertEqual(alloc.allocation_queue_time, None)
self.assertEqual(str(alloc), 'Allocation(queue_time: None, resource_info: id=1)')

0 comments on commit 1f86ff1

Please sign in to comment.