commit e573c6d385a26c68a6da3d0e744a078f8750a18a
parent ab441a507abc160803bbb60e1b5d165f0de6289f
Author: ThomasV <thomasv@electrum.org>
Date: Fri, 12 Oct 2018 18:53:29 +0200
Merge pull request #4770 from SomberNight/kill_aiosafe
rm aiosafe decorator. instead: log_exceptions and ignore_exceptions
Diffstat:
6 files changed, 41 insertions(+), 33 deletions(-)
diff --git a/electrum/address_synchronizer.py b/electrum/address_synchronizer.py
@@ -28,7 +28,7 @@ from collections import defaultdict
from . import bitcoin
from .bitcoin import COINBASE_MATURITY, TYPE_ADDRESS, TYPE_PUBKEY
-from .util import PrintError, profiler, bfh, VerifiedTxInfo, TxMinedStatus, aiosafe, SilentTaskGroup
+from .util import PrintError, profiler, bfh, VerifiedTxInfo, TxMinedStatus
from .transaction import Transaction, TxOutput
from .synchronizer import Synchronizer
from .verifier import SPV
diff --git a/electrum/exchange_rate.py b/electrum/exchange_rate.py
@@ -14,7 +14,7 @@ from typing import Sequence
from .bitcoin import COIN
from .i18n import _
-from .util import PrintError, ThreadJob, make_dir, aiosafe
+from .util import PrintError, ThreadJob, make_dir, log_exceptions
from .util import make_aiohttp_session
from .network import Network
@@ -58,7 +58,7 @@ class ExchangeBase(PrintError):
def name(self):
return self.__class__.__name__
- @aiosafe
+ @log_exceptions
async def update_safe(self, ccy):
try:
self.print_error("getting fx quotes for", ccy)
@@ -89,7 +89,7 @@ class ExchangeBase(PrintError):
self.on_history()
return h
- @aiosafe
+ @log_exceptions
async def get_historical_rates_safe(self, ccy, cache_dir):
try:
self.print_error("requesting fx history for", ccy)
diff --git a/electrum/interface.py b/electrum/interface.py
@@ -34,7 +34,7 @@ from collections import defaultdict
import aiorpcx
from aiorpcx import ClientSession, Notification
-from .util import PrintError, aiosafe, bfh, AIOSafeSilentException, SilentTaskGroup
+from .util import PrintError, ignore_exceptions, log_exceptions, bfh, SilentTaskGroup
from . import util
from . import x509
from . import pem
@@ -146,9 +146,6 @@ class Interface(PrintError):
self.tip_header = None
self.tip = 0
- # note that an interface dying MUST NOT kill the whole network,
- # hence exceptions raised by "run" need to be caught not to kill
- # main_taskgroup! the aiosafe decorator does this.
asyncio.run_coroutine_threadsafe(
self.network.main_taskgroup.spawn(self.run()), self.network.asyncio_loop)
self.group = SilentTaskGroup()
@@ -249,7 +246,8 @@ class Interface(PrintError):
self.got_disconnected.set_result(1)
return wrapper_func
- @aiosafe
+ @ignore_exceptions # do not kill main_taskgroup
+ @log_exceptions
@handle_disconnect
async def run(self):
try:
diff --git a/electrum/network.py b/electrum/network.py
@@ -40,7 +40,7 @@ import dns.resolver
from aiorpcx import TaskGroup
from . import util
-from .util import PrintError, print_error, aiosafe, bfh, SilentTaskGroup
+from .util import PrintError, print_error, log_exceptions, ignore_exceptions, bfh, SilentTaskGroup
from .bitcoin import COIN
from . import constants
from . import blockchain
@@ -478,7 +478,7 @@ class Network(PrintError):
addr = host
return socket._getaddrinfo(addr, *args, **kwargs)
- @aiosafe
+ @log_exceptions
async def set_parameters(self, net_params: NetworkParameters):
proxy = net_params.proxy
proxy_str = serialize_proxy(proxy)
@@ -619,7 +619,8 @@ class Network(PrintError):
await self._close_interface(interface)
self.trigger_callback('network_updated')
- @aiosafe
+ @ignore_exceptions # do not kill main_taskgroup
+ @log_exceptions
async def _run_new_interface(self, server):
interface = Interface(self, server, self.config.path, self.proxy)
timeout = 10 if not self.proxy else 20
diff --git a/electrum/plugins/labels/labels.py b/electrum/plugins/labels/labels.py
@@ -9,7 +9,7 @@ import base64
from electrum.plugin import BasePlugin, hook
from electrum.crypto import aes_encrypt_with_iv, aes_decrypt_with_iv
from electrum.i18n import _
-from electrum.util import aiosafe, make_aiohttp_session
+from electrum.util import log_exceptions, ignore_exceptions, make_aiohttp_session
class LabelsPlugin(BasePlugin):
@@ -58,7 +58,8 @@ class LabelsPlugin(BasePlugin):
# Caller will write the wallet
self.set_nonce(wallet, nonce + 1)
- @aiosafe
+ @ignore_exceptions
+ @log_exceptions
async def do_post_safe(self, *args):
await self.do_post(*args)
@@ -129,7 +130,8 @@ class LabelsPlugin(BasePlugin):
self.set_nonce(wallet, response["nonce"] + 1)
self.on_pulled(wallet)
- @aiosafe
+ @ignore_exceptions
+ @log_exceptions
async def pull_safe_thread(self, wallet, force):
await self.pull_thread(wallet, force)
diff --git a/electrum/util.py b/electrum/util.py
@@ -846,29 +846,36 @@ def make_dir(path, allow_symlink=True):
os.chmod(path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
-class AIOSafeSilentException(Exception): pass
-
-
-def aiosafe(f):
- # save exception in object.
- # f must be a method of a PrintError instance.
- # aiosafe calls should not be nested
- async def f2(*args, **kwargs):
- self = args[0]
+def log_exceptions(func):
+ """Decorator to log AND re-raise exceptions."""
+ assert asyncio.iscoroutinefunction(func), 'func needs to be a coroutine'
+ async def wrapper(*args, **kwargs):
+ self = args[0] if len(args) > 0 else None
try:
- return await f(*args, **kwargs)
- except AIOSafeSilentException as e:
- self.exception = e
+ return await func(*args, **kwargs)
except asyncio.CancelledError as e:
- self.exception = e
+ raise
except BaseException as e:
- self.exception = e
- self.print_error("Exception in", f.__name__, ":", e.__class__.__name__, str(e))
+ print_ = self.print_error if hasattr(self, 'print_error') else print_error
+ print_("Exception in", func.__name__, ":", e.__class__.__name__, repr(e))
try:
traceback.print_exc(file=sys.stderr)
except BaseException as e2:
- self.print_error("aiosafe:traceback.print_exc raised: {}... original exc: {}".format(e2, e))
- return f2
+ print_error("traceback.print_exc raised: {}...".format(e2))
+ raise
+ return wrapper
+
+
+def ignore_exceptions(func):
+ """Decorator to silently swallow all exceptions."""
+ assert asyncio.iscoroutinefunction(func), 'func needs to be a coroutine'
+ async def wrapper(*args, **kwargs):
+ try:
+ return await func(*args, **kwargs)
+ except BaseException as e:
+ pass
+ return wrapper
+
TxMinedStatus = NamedTuple("TxMinedStatus", [("height", int),
("conf", int),
@@ -941,7 +948,7 @@ class NetworkJobOnDefaultServer(PrintError):
async def stop(self):
await self.group.cancel_remaining()
- @aiosafe
+ @log_exceptions
async def _restart(self, *args):
interface = self.network.interface
if interface is None: