URI: 
       tbase_crash_reporter.py - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
       tbase_crash_reporter.py (5160B)
       ---
            1 # Electrum - lightweight Bitcoin client
            2 #
            3 # Permission is hereby granted, free of charge, to any person
            4 # obtaining a copy of this software and associated documentation files
            5 # (the "Software"), to deal in the Software without restriction,
            6 # including without limitation the rights to use, copy, modify, merge,
            7 # publish, distribute, sublicense, and/or sell copies of the Software,
            8 # and to permit persons to whom the Software is furnished to do so,
            9 # subject to the following conditions:
           10 #
           11 # The above copyright notice and this permission notice shall be
           12 # included in all copies or substantial portions of the Software.
           13 #
           14 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
           15 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
           16 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
           17 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
           18 # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
           19 # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
           20 # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
           21 # SOFTWARE.
           22 import asyncio
           23 import json
           24 import locale
           25 import traceback
           26 import sys
           27 
           28 from .version import ELECTRUM_VERSION
           29 from . import constants
           30 from .i18n import _
           31 from .util import make_aiohttp_session
           32 from .logging import describe_os_version, Logger, get_git_version
           33 
           34 
           35 class BaseCrashReporter(Logger):
           36     report_server = "https://crashhub.electrum.org"
           37     config_key = "show_crash_reporter"
           38     issue_template = """<h2>Traceback</h2>
           39 <pre>
           40 {traceback}
           41 </pre>
           42 
           43 <h2>Additional information</h2>
           44 <ul>
           45   <li>Electrum version: {app_version}</li>
           46   <li>Python version: {python_version}</li>
           47   <li>Operating system: {os}</li>
           48   <li>Wallet type: {wallet_type}</li>
           49   <li>Locale: {locale}</li>
           50 </ul>
           51     """
           52     CRASH_MESSAGE = _('Something went wrong while executing Electrum.')
           53     CRASH_TITLE = _('Sorry!')
           54     REQUEST_HELP_MESSAGE = _('To help us diagnose and fix the problem, you can send us a bug report that contains '
           55                              'useful debug information:')
           56     DESCRIBE_ERROR_MESSAGE = _("Please briefly describe what led to the error (optional):")
           57     ASK_CONFIRM_SEND = _("Do you want to send this report?")
           58 
           59     def __init__(self, exctype, value, tb):
           60         Logger.__init__(self)
           61         self.exc_args = (exctype, value, tb)
           62 
           63     def send_report(self, asyncio_loop, proxy, endpoint="/crash", *, timeout=None):
           64         if constants.net.GENESIS[-4:] not in ["4943", "e26f"] and ".electrum.org" in BaseCrashReporter.report_server:
           65             # Gah! Some kind of altcoin wants to send us crash reports.
           66             raise Exception(_("Missing report URL."))
           67         report = self.get_traceback_info()
           68         report.update(self.get_additional_info())
           69         report = json.dumps(report)
           70         coro = self.do_post(proxy, BaseCrashReporter.report_server + endpoint, data=report)
           71         response = asyncio.run_coroutine_threadsafe(coro, asyncio_loop).result(timeout)
           72         return response
           73 
           74     async def do_post(self, proxy, url, data):
           75         async with make_aiohttp_session(proxy) as session:
           76             async with session.post(url, data=data, raise_for_status=True) as resp:
           77                 return await resp.text()
           78 
           79     def get_traceback_info(self):
           80         exc_string = str(self.exc_args[1])
           81         stack = traceback.extract_tb(self.exc_args[2])
           82         readable_trace = "".join(traceback.format_list(stack))
           83         id = {
           84             "file": stack[-1].filename,
           85             "name": stack[-1].name,
           86             "type": self.exc_args[0].__name__
           87         }
           88         return {
           89             "exc_string": exc_string,
           90             "stack": readable_trace,
           91             "id": id
           92         }
           93 
           94     def get_additional_info(self):
           95         args = {
           96             "app_version": get_git_version() or ELECTRUM_VERSION,
           97             "python_version": sys.version,
           98             "os": describe_os_version(),
           99             "wallet_type": "unknown",
          100             "locale": locale.getdefaultlocale()[0] or "?",
          101             "description": self.get_user_description()
          102         }
          103         try:
          104             args["wallet_type"] = self.get_wallet_type()
          105         except:
          106             # Maybe the wallet isn't loaded yet
          107             pass
          108         return args
          109 
          110     def _get_traceback_str(self) -> str:
          111         return "".join(traceback.format_exception(*self.exc_args))
          112 
          113     def get_report_string(self):
          114         info = self.get_additional_info()
          115         info["traceback"] = self._get_traceback_str()
          116         return self.issue_template.format(**info)
          117 
          118     def get_user_description(self):
          119         raise NotImplementedError
          120 
          121     def get_wallet_type(self) -> str:
          122         raise NotImplementedError
          123 
          124 
          125 def trigger_crash():
          126     # note: do not change the type of the exception, the message,
          127     # or the name of this method. All reports generated through this
          128     # method will be grouped together by the crash reporter, and thus
          129     # don't spam the issue tracker.
          130 
          131     class TestingException(Exception):
          132         pass
          133 
          134     def crash_test():
          135         raise TestingException("triggered crash for testing purposes")
          136 
          137     import threading
          138     t = threading.Thread(target=crash_test)
          139     t.start()