Last active
September 8, 2022 20:51
-
-
Save sug0/87091e88779e493a51bd071554da247c to your computer and use it in GitHub Desktop.
Promises-like API in Python
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import multiprocessing | |
| from collections import namedtuple | |
| from concurrent.futures import ThreadPoolExecutor | |
| Poll = namedtuple('Poll', ['resolved', 'value']) | |
| _CURRENT_EXECUTOR = None | |
| def enter_runtime(main=None, threads=0): | |
| if main == None: | |
| raise PromiseError('provide a `main` argument') | |
| if threads == 0: | |
| threads = multiprocessing.cpu_count() | |
| global _CURRENT_EXECUTOR | |
| with ThreadPoolExecutor(max_workers=threads) as executor: | |
| _CURRENT_EXECUTOR = executor | |
| main() | |
| _CURRENT_EXECUTOR = None | |
| class PromiseError(Exception): | |
| ... | |
| class Promise(object): | |
| def __init__(self, body): | |
| self._run(body) | |
| def resolve_with(value): | |
| return Promise(lambda resolve, _: resolve(value())) | |
| def resolve(x): | |
| return Promise.resolve_with(lambda: x) | |
| def reject_with(err): | |
| return Promise(lambda _, reject: reject(err())) | |
| def reject_with(err): | |
| return Promise.resolve_with(lambda: err) | |
| def then(self, fmap): | |
| def next(resolve, reject): | |
| # resolve future | |
| self._future.result() | |
| resolve(fmap(self._value)) | |
| return Promise(next) | |
| def poll(self): | |
| if self._future.running(): | |
| return Poll(False, None) | |
| else: | |
| return Poll(True, self._value) | |
| # suffix await syntax ftw | |
| @property | |
| def wait(self): | |
| self._future.result() | |
| return self._value | |
| def _run(self, body): | |
| def reject(err): | |
| raise PromiseError(err) | |
| def resolve(value): | |
| self._value = value | |
| def future_main(): | |
| body(resolve, reject) | |
| global _CURRENT_EXECUTOR | |
| if not _CURRENT_EXECUTOR: | |
| raise PromiseError('no executor found') | |
| self._future = _CURRENT_EXECUTOR.submit(future_main) | |
| def async_main(): | |
| # using functor style | |
| functor_result = Promise.resolve(5) \ | |
| .then(lambda x: x + 2) \ | |
| .then(lambda x: x * 10) \ | |
| .wait | |
| # using monadic style | |
| five = Promise.resolve_with(lambda: 5).wait | |
| seven = Promise.resolve_with(lambda: five + 2).wait | |
| monadic_result = Promise.resolve_with(lambda: seven * 10).wait | |
| assert functor_result == monadic_result | |
| print(functor_result) | |
| if __name__ == '__main__': | |
| enter_runtime(main=async_main) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment