Skip to content

Commit

Permalink
Fix semaphores (#28)
Browse files Browse the repository at this point in the history
* fix: semaphore repr errs

* fix: class-defined modifiers for properties

* fix(test): test_semaphore
  • Loading branch information
BobTheBuidler authored Mar 7, 2023
1 parent 5353b3f commit 1d61a82
Show file tree
Hide file tree
Showing 3 changed files with 17 additions and 11 deletions.
6 changes: 5 additions & 1 deletion a_sync/_bound.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,15 @@ def _wrap_property(

async_property.hidden_method_name = f"__{async_property.field_name}__"

@unbound_a_sync(**modifiers)
async def _get(instance: ASyncABC):
return await async_property.__get__(instance, async_property)

@functools.wraps(async_property)
def a_sync_method(self: ASyncABC, **kwargs) -> T:
if not isinstance(self, ASyncABC):
raise RuntimeError(f"{self} must be an instance of a class that inherits from ASyncABC.")
awaitable = async_property.__get__(self, async_property)
awaitable = _get(self)
return _helpers._await(awaitable) if self.__a_sync_should_await__(kwargs) else awaitable

@property # type: ignore [misc]
Expand Down
5 changes: 5 additions & 0 deletions a_sync/semaphores.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ def __init__(self, value: Optional[int]) -> None:
self.semaphores: DefaultDict[Thread, asyncio.Semaphore] = defaultdict(lambda: asyncio.Semaphore(value)) # type: ignore [arg-type]
self.dummy = DummySemaphore()

def __repr__(self) -> str:
return f"<ThreadsafeSemaphore value={self._value}>"

@property
def use_dummy(self) -> bool:
return self._value is None
Expand All @@ -45,6 +48,8 @@ async def __aexit__(self, *args):
class DummySemaphore(asyncio.Semaphore):
def __init__(*args, **kwargs):
...
def __repr__(self) -> str:
return "<DummySemaphore>"
async def __aenter__(self):
...
async def __aexit__(self, *args):
Expand Down
17 changes: 7 additions & 10 deletions tests/test_semaphore.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,36 +9,33 @@
# Maybe a problem with test suite interaction?
# - semaphore modifier works fine with integer inputs


instance = TestSemaphore(1, sync=False)

@increment
@pytest.mark.asyncio_cooperative
async def test_semaphore(i: int):
instance = TestSemaphore(i, sync=False)

start = time()
assert await instance.test_fn() == i
assert await instance.test_fn() == 1
duration = time() - start
assert i < 3 or duration > i # There is a 1 second sleep in this fn. If the semaphore is not working, all tests will complete in 1 second.


@increment
@pytest.mark.asyncio_cooperative
async def test_semaphore_property(i: int):
instance = TestSemaphore(i, sync=False)
start = time()
assert await instance.test_property == i * 2
assert await instance.test_property == 2
duration = time() - start
assert i < 3 or duration > i # There is a 1 second sleep in this fn. If the semaphore is not working, all tests will complete in 1 second.



@increment
@pytest.mark.asyncio_cooperative
async def test_semaphore_cached_property(i: int):
instance = TestSemaphore(i, sync=False)
start = time()
assert await instance.test_cached_property == i * 3
assert await instance.test_cached_property == 3
duration = time() - start
# There is a 1 second sleep in this fn but a semaphore override with a value of 50.
# You can tell it worked correctly because the class-defined semaphore value is just one, whch would cause this test to fail if it were used.
# If the override is not working, all tests will complete in just over 1 second.
assert i < 3 or duration < 1.5
assert i == 1 or duration < 1.05

0 comments on commit 1d61a82

Please sign in to comment.