Skip to content

Commit

Permalink
Remove second level of wrapping.
Browse files Browse the repository at this point in the history
The new code is simple enough that we can have _callable directly create
self-contained functions without it getting too long.
  • Loading branch information
aebrahim committed Oct 12, 2023
1 parent b8e5db6 commit 6601d9d
Showing 1 changed file with 23 additions and 39 deletions.
62 changes: 23 additions & 39 deletions once/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,14 @@ def _callable(self, func: collections.abc.Callable):
if self.fn_type == _WrappedFunctionType.ASYNC_GENERATOR:

async def wrapped(*args, **kwargs):
async with self.async_lock:
if not self.called:
self.return_value = _iterator_wrappers.AsyncGeneratorWrapper(
func, *args, **kwargs
)
self.called = True
next_value = None
iterator = self._execute_call_once_async_iter(func, *args, **kwargs)
iterator = self.return_value.yield_results()
while True:
try:
next_value = yield await iterator.asend(next_value)
Expand All @@ -77,57 +83,35 @@ async def wrapped(*args, **kwargs):
elif self.fn_type == _WrappedFunctionType.ASYNC_FUNCTION:

async def wrapped(*args, **kwargs):
return await self._execute_call_once_async(func, *args, **kwargs)
async with self.async_lock:
if not self.called:
self.return_value = await func(*args, **kwargs)
self.called = True
return self.return_value

elif self.fn_type == _WrappedFunctionType.SYNC_FUNCTION:

def wrapped(*args, **kwargs):
return self._execute_call_once_sync(func, *args, **kwargs)
with self.lock:
if not self.called:
self.return_value = func(*args, **kwargs)
self.called = True
return self.return_value

else:
assert self.fn_type == _WrappedFunctionType.SYNC_GENERATOR

def wrapped(*args, **kwargs):
yield from self._execute_call_once_sync_iter(func, *args, **kwargs)
with self.lock:
if not self.called:
self.return_value = _iterator_wrappers.GeneratorWrapper(func, *args, **kwargs)
self.called = True
iterator = self.return_value
yield from iterator.yield_results()

functools.update_wrapper(wrapped, func)
return wrapped

async def _execute_call_once_async(self, func: collections.abc.Callable, *args, **kwargs):
async with self.async_lock:
if not self.called:
self.return_value = await func(*args, **kwargs)
self.called = True
return self.return_value

async def _execute_call_once_async_iter(self, func: collections.abc.Callable, *args, **kwargs):
async with self.async_lock:
if not self.called:
self.return_value = _iterator_wrappers.AsyncGeneratorWrapper(func, *args, **kwargs)
self.called = True
next_value = None
iterator = self.return_value.yield_results()
while True:
try:
next_value = yield await iterator.asend(next_value)
except StopAsyncIteration:
return

def _execute_call_once_sync(self, func: collections.abc.Callable, *args, **kwargs):
with self.lock:
if not self.called:
self.return_value = func(*args, **kwargs)
self.called = True
return self.return_value

def _execute_call_once_sync_iter(self, func: collections.abc.Callable, *args, **kwargs):
with self.lock:
if not self.called:
self.return_value = _iterator_wrappers.GeneratorWrapper(func, *args, **kwargs)
self.called = True
iterator = self.return_value
yield from iterator.yield_results()


def once(func: collections.abc.Callable):
"""Decorator to ensure a function is only called once.
Expand Down

0 comments on commit 6601d9d

Please sign in to comment.