mango-explorer/Retrier.ipynb

211 lines
6.4 KiB
Plaintext
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{
"cells": [
{
"cell_type": "markdown",
"id": "premium-convergence",
"metadata": {},
"source": [
"# ⚠ Warning\n",
"\n",
"THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n",
"\n",
"[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gl/OpinionatedGeek%2Fmango-explorer/HEAD?filepath=Retrier.ipynb) _🏃 To run this notebook press the ⏩ icon in the toolbar above._\n",
"\n",
"[🥭 Mango Markets](https://mango.markets/) support is available at: [Docs](https://docs.mango.markets/) | [Discord](https://discord.gg/67jySBhxrg) | [Twitter](https://twitter.com/mangomarkets) | [Github](https://github.com/blockworks-foundation) | [Email](mailto:hello@blockworks.foundation)"
]
},
{
"cell_type": "markdown",
"id": "streaming-bhutan",
"metadata": {},
"source": [
"# 🥭 Retrier\n",
"\n",
"This notebook creates a 'retrier' context that can automatically retry failing functions."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "critical-attempt",
"metadata": {
"jupyter": {
"source_hidden": true
}
},
"outputs": [],
"source": [
"import logging\n",
"import typing\n",
"\n",
"from contextlib import contextmanager\n"
]
},
{
"cell_type": "markdown",
"id": "planned-belief",
"metadata": {},
"source": [
"# Retrier class\n",
"\n",
"This class takes a function and a maximum number of times to try running the function.\n",
"\n",
"If the function succeeds, the resultant value is returned.\n",
"\n",
"If the function fails by raising an exception, the function is retried.\n",
"\n",
"It is retried up to the maximum number of retries. If they all fail, the last failing exception is re-raised.\n",
"\n",
"This class is best used in a `with...` block using the `retry_context()` function below."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "broken-spokesman",
"metadata": {},
"outputs": [],
"source": [
"class Retrier:\n",
" def __init__(self, func: typing.Callable, retries: int) -> None:\n",
" self.logger: logging.Logger = logging.getLogger(self.__class__.__name__)\n",
" self.func: typing.Callable = func\n",
" self.retries: int = retries\n",
"\n",
" def run(self, *args):\n",
" captured_exception: Exception = None\n",
" for counter in range(self.retries):\n",
" try:\n",
" return self.func(*args)\n",
" except Exception as exception:\n",
" self.logger.info(f\"Retriable call failed [{counter}] with error '{exception}'. Retrying...\")\n",
" captured_exception = exception\n",
"\n",
" raise captured_exception\n"
]
},
{
"cell_type": "markdown",
"id": "false-attitude",
"metadata": {},
"source": [
"# retry_context generator\n",
"\n",
"This is a bit of Python 'magic' to allow using the Retrier in a `with...` block.\n",
"\n",
"For example, this will call function `some_function(param1, param2)` up to `retry_count` times (7 in this case). It will only retry if the function throws an exception - the result of the first successful call is used to set the `result` variable:\n",
"```\n",
"retry_count = 7\n",
"with retry_context(some_function, retry_count) as retrier:\n",
" result = retrier.run(param1, param2)\n",
"```"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "precious-encoding",
"metadata": {},
"outputs": [],
"source": [
"@contextmanager\n",
"def retry_context(func: typing.Callable, retries: int = 3) -> typing.Iterator[Retrier]:\n",
" yield Retrier(func, retries)\n"
]
},
{
"cell_type": "markdown",
"id": "incident-giving",
"metadata": {},
"source": [
"# 🏃 Running\n",
"\n",
"Run a failing method, retrying it 5 times, just to show how it works in practice."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "rental-fusion",
"metadata": {},
"outputs": [],
"source": [
"if __name__ == \"__main__\":\n",
" logging.getLogger().setLevel(logging.INFO)\n",
"\n",
" def _raiser(value):\n",
" # All this does is raise an exception\n",
" raise Exception(f\"This is a test: {value}\")\n",
"\n",
" # NOTE! This will fail by design, with the exception message:\n",
" # \"Exception: This is a test: ignored parameter\"\n",
" with retry_context(_raiser, 5) as retrier:\n",
" response = retrier.run(\"ignored parameter\")\n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.6"
},
"toc": {
"base_numbering": 1,
"nav_menu": {},
"number_sections": true,
"sideBar": true,
"skip_h1_title": false,
"title_cell": "Table of Contents",
"title_sidebar": "Contents",
"toc_cell": false,
"toc_position": {},
"toc_section_display": true,
"toc_window_display": true
},
"varInspector": {
"cols": {
"lenName": 16,
"lenType": 16,
"lenVar": 40
},
"kernels_config": {
"python": {
"delete_cmd_postfix": "",
"delete_cmd_prefix": "del ",
"library": "var_list.py",
"varRefreshCmd": "print(var_dic_list())"
},
"r": {
"delete_cmd_postfix": ") ",
"delete_cmd_prefix": "rm(",
"library": "var_list.r",
"varRefreshCmd": "cat(var_dic_list()) "
}
},
"types_to_exclude": [
"module",
"function",
"builtin_function_or_method",
"instance",
"_Feature"
],
"window_display": false
}
},
"nbformat": 4,
"nbformat_minor": 5
}