tcrash_reporter.py - electrum - Electrum Bitcoin wallet
HTML git clone https://git.parazyd.org/electrum
DIR Log
DIR Files
DIR Refs
DIR Submodules
---
tcrash_reporter.py (6768B)
---
1 import sys
2 import json
3
4 from aiohttp.client_exceptions import ClientError
5 from kivy import base, utils
6 from kivy.clock import Clock
7 from kivy.core.window import Window
8 from kivy.factory import Factory
9 from kivy.lang import Builder
10 from kivy.uix.label import Label
11 from kivy.utils import platform
12
13 from electrum.gui.kivy.i18n import _
14
15 from electrum.base_crash_reporter import BaseCrashReporter
16 from electrum.logging import Logger
17
18
19 Builder.load_string('''
20 <CrashReporter@Popup>
21 BoxLayout:
22 orientation: 'vertical'
23 Label:
24 id: crash_message
25 text_size: root.width, None
26 size: self.texture_size
27 size_hint: None, None
28 Label:
29 id: request_help_message
30 text_size: root.width*.95, None
31 size: self.texture_size
32 size_hint: None, None
33 BoxLayout:
34 size_hint: 1, 0.1
35 Button:
36 text: 'Show report contents'
37 height: '48dp'
38 size_hint: 1, None
39 on_release: root.show_contents()
40 BoxLayout:
41 size_hint: 1, 0.1
42 Label:
43 id: describe_error_message
44 text_size: root.width, None
45 size: self.texture_size
46 size_hint: None, None
47 TextInput:
48 id: user_message
49 size_hint: 1, 0.3
50 BoxLayout:
51 size_hint: 1, 0.7
52 BoxLayout:
53 size_hint: 1, None
54 height: '48dp'
55 orientation: 'horizontal'
56 Button:
57 height: '48dp'
58 text: 'Send'
59 on_release: root.send_report()
60 Button:
61 text: 'Never'
62 on_release: root.show_never()
63 Button:
64 text: 'Not now'
65 on_release: root.dismiss()
66
67 <CrashReportDetails@Popup>
68 BoxLayout:
69 orientation: 'vertical'
70 ScrollView:
71 do_scroll_x: False
72 Label:
73 id: contents
74 text_size: root.width*.9, None
75 size: self.texture_size
76 size_hint: None, None
77 Button:
78 text: 'Close'
79 height: '48dp'
80 size_hint: 1, None
81 on_release: root.dismiss()
82 ''')
83
84
85 class CrashReporter(BaseCrashReporter, Factory.Popup):
86 issue_template = """[b]Traceback[/b]
87
88 [i]{traceback}[/i]
89
90
91 [b]Additional information[/b]
92 * Electrum version: {app_version}
93 * Operating system: {os}
94 * Wallet type: {wallet_type}
95 * Locale: {locale}
96 """
97
98 def __init__(self, main_window, exctype, value, tb):
99 BaseCrashReporter.__init__(self, exctype, value, tb)
100 Factory.Popup.__init__(self)
101 self.main_window = main_window
102 self.title = BaseCrashReporter.CRASH_TITLE
103 self.title_size = "24sp"
104 self.ids.crash_message.text = BaseCrashReporter.CRASH_MESSAGE
105 self.ids.request_help_message.text = BaseCrashReporter.REQUEST_HELP_MESSAGE
106 self.ids.describe_error_message.text = BaseCrashReporter.DESCRIBE_ERROR_MESSAGE
107
108 def show_contents(self):
109 details = CrashReportDetails(self.get_report_string())
110 details.open()
111
112 def show_popup(self, title, content):
113 popup = Factory.Popup(title=title,
114 content=Label(text=content, text_size=(Window.size[0] * 3/4, None)),
115 size_hint=(3/4, 3/4))
116 popup.open()
117
118 def send_report(self):
119 try:
120 loop = self.main_window.network.asyncio_loop
121 proxy = self.main_window.network.proxy
122 # FIXME network request in GUI thread...
123 response = json.loads(BaseCrashReporter.send_report(self, loop, proxy,
124 "/crash.json", timeout=10))
125 except (ValueError, ClientError) as e:
126 self.logger.warning(f"Error sending crash report. exc={e!r}")
127 self.show_popup(_('Unable to send report'), _("Please check your network connection."))
128 else:
129 self.show_popup(_('Report sent'), response["text"])
130 location = response["location"]
131 if location:
132 self.logger.info(f"Crash report sent. location={location!r}")
133 self.open_url(location)
134 self.dismiss()
135
136 def on_dismiss(self):
137 self.main_window.on_wizard_aborted()
138
139 def open_url(self, url):
140 if platform != 'android':
141 return
142 from jnius import autoclass, cast
143 String = autoclass("java.lang.String")
144 url = String(url)
145 PythonActivity = autoclass('org.kivy.android.PythonActivity')
146 activity = PythonActivity.mActivity
147 Intent = autoclass('android.content.Intent')
148 Uri = autoclass('android.net.Uri')
149 browserIntent = Intent()
150 # This line crashes the app:
151 # browserIntent.setAction(Intent.ACTION_VIEW)
152 # Luckily we don't need it because the OS is smart enough to recognize the URL
153 browserIntent.setData(Uri.parse(url))
154 currentActivity = cast('android.app.Activity', activity)
155 currentActivity.startActivity(browserIntent)
156
157 def show_never(self):
158 self.main_window.electrum_config.set_key(BaseCrashReporter.config_key, False)
159 self.dismiss()
160
161 def get_user_description(self):
162 return self.ids.user_message.text
163
164 def get_wallet_type(self):
165 return self.main_window.wallet.wallet_type
166
167
168 class CrashReportDetails(Factory.Popup):
169 def __init__(self, text):
170 Factory.Popup.__init__(self)
171 self.title = "Report Details"
172 self.ids.contents.text = text
173 print(text)
174
175
176 class ExceptionHook(base.ExceptionHandler, Logger):
177 def __init__(self, main_window):
178 base.ExceptionHandler.__init__(self)
179 Logger.__init__(self)
180 self.main_window = main_window
181 if not main_window.electrum_config.get(BaseCrashReporter.config_key, default=True):
182 return
183 # For exceptions in Kivy:
184 base.ExceptionManager.add_handler(self)
185 # For everything else:
186 sys.excepthook = lambda exctype, value, tb: self.handle_exception(value)
187
188 def handle_exception(self, _inst):
189 exc_info = sys.exc_info()
190 self.logger.error('exception caught by crash reporter', exc_info=exc_info)
191 # Check if this is an exception from within the exception handler:
192 import traceback
193 for item in traceback.extract_tb(exc_info[2]):
194 if item.filename.endswith("crash_reporter.py"):
195 return
196 e = CrashReporter(self.main_window, *exc_info)
197 # Open in main thread:
198 Clock.schedule_once(lambda _: e.open(), 0)
199 return base.ExceptionManager.PASS