Skip to content

Commit 54ec972

Browse files
committed
refactor(python): improve trezorctl error handling
Following #5613. [no changelog]
1 parent be87389 commit 54ec972

File tree

3 files changed

+41
-96
lines changed

3 files changed

+41
-96
lines changed

python/src/trezorlib/cli/__init__.py

Lines changed: 33 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -238,28 +238,15 @@ def get_seedless_session(self) -> Session:
238238
seedless_session = client.get_seedless_session()
239239
return seedless_session
240240

241-
@contextmanager
242-
def client_context(self):
243-
"""Get a client instance as a context manager. Handle errors in a manner
244-
appropriate for end-users.
245-
246-
Usage:
247-
>>> with obj.client_context() as client:
248-
>>> do_your_actions_here()
249-
"""
241+
def _connection_context(self, connect_fn: t.Callable[[], t.Any]):
250242
try:
251-
client = self.get_client()
252-
except transport.DeviceIsBusy:
253-
click.echo("Device is in use by another process.")
254-
sys.exit(1)
255-
except Exception:
256-
click.echo("Failed to find a Trezor device.")
257-
if self.path is not None:
258-
click.echo(f"Using path: {self.path}")
243+
conn = connect_fn()
244+
except Exception as e:
245+
self._print_exception(e, "Failed to connect")
259246
sys.exit(1)
260247

261248
try:
262-
yield client
249+
yield conn
263250
except exceptions.Cancelled:
264251
# handle cancel action
265252
click.echo("Action was cancelled.")
@@ -269,6 +256,17 @@ def client_context(self):
269256
raise click.ClickException(str(e)) from e
270257
# other exceptions may cause a traceback
271258

259+
@contextmanager
260+
def client_context(self):
261+
"""Get a client instance as a context manager. Handle errors in a manner
262+
appropriate for end-users.
263+
264+
Usage:
265+
>>> with obj.client_context() as client:
266+
>>> do_your_actions_here()
267+
"""
268+
yield from self._connection_context(self.get_client)
269+
272270
@contextmanager
273271
def session_context(
274272
self,
@@ -277,55 +275,25 @@ def session_context(
277275
seedless: bool = False,
278276
must_resume: bool = False,
279277
):
280-
"""Get a session instance as a context manager. Handle errors in a manner
281-
appropriate for end-users.
282-
283-
Usage:
284-
>>> with obj.session_context() as session:
285-
>>> do_your_actions_here()
286-
"""
287-
try:
288-
if seedless:
289-
session = self.get_seedless_session()
290-
else:
291-
session = self.get_session(
292-
derive_cardano=derive_cardano,
293-
empty_passphrase=empty_passphrase,
294-
must_resume=must_resume,
295-
)
296-
except exceptions.DeviceLocked:
297-
click.echo(
298-
"Device is locked, enter a pin on the device.",
299-
err=True,
278+
yield from self._connection_context(
279+
self.get_seedless_session
280+
if seedless
281+
else lambda: self.get_session(
282+
derive_cardano=derive_cardano,
283+
empty_passphrase=empty_passphrase,
284+
must_resume=must_resume,
300285
)
301-
sys.exit(1)
302-
except transport.DeviceIsBusy:
303-
click.echo("Device is in use by another process.")
304-
sys.exit(1)
305-
except exceptions.UnexpectedCodeEntryTagException:
306-
click.echo("Entered Code is invalid.")
307-
sys.exit(1)
308-
except exceptions.FailedSessionResumption:
309-
sys.exit(1)
310-
except exceptions.DerivationOnUninitaizedDeviceError:
311-
click.echo("Device is not initialized.")
312-
sys.exit(1)
313-
except Exception:
314-
click.echo("Failed to find a Trezor device.")
315-
if self.path is not None:
316-
click.echo(f"Using path: {self.path}")
317-
sys.exit(1)
286+
)
318287

319-
try:
320-
yield session
321-
except exceptions.Cancelled:
322-
# handle cancel action
323-
click.echo("Action was cancelled.")
324-
sys.exit(1)
325-
except exceptions.TrezorException as e:
326-
# handle any Trezor-sent exceptions as user-readable
327-
raise click.ClickException(str(e)) from e
328-
# other exceptions may cause a traceback
288+
def _print_exception(self, exc: Exception, message: str):
289+
LOG.debug(message, exc_info=True)
290+
message = f"{message}: {exc.__class__.__name__}"
291+
if description := str(exc):
292+
message = f"{message} ({description})"
293+
294+
click.echo(message)
295+
if self.path is not None:
296+
click.echo(f"Using path: {self.path}")
329297

330298

331299
def with_session(

python/src/trezorlib/client.py

Lines changed: 8 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -172,20 +172,14 @@ def _handle_code_entry(self, session: SessionV2) -> None:
172172
sha_ctx = sha256(cpace.shared_secret)
173173
tag = sha_ctx.digest()
174174

175-
try:
176-
secret_msg = session.call(
177-
messages.ThpCodeEntryCpaceHostTag(
178-
cpace_host_public_key=cpace.host_public_key,
179-
tag=tag,
180-
),
181-
expect=messages.ThpCodeEntrySecret,
182-
skip_firmware_version_check=True,
183-
)
184-
except exceptions.TrezorFailure as e:
185-
if e.message == "Unexpected Code Entry Tag":
186-
raise exceptions.UnexpectedCodeEntryTagException
187-
else:
188-
raise e
175+
secret_msg = session.call(
176+
messages.ThpCodeEntryCpaceHostTag(
177+
cpace_host_public_key=cpace.host_public_key,
178+
tag=tag,
179+
),
180+
expect=messages.ThpCodeEntrySecret,
181+
skip_firmware_version_check=True,
182+
)
189183

190184
# Check `commitment` and `code`
191185
assert secret_msg.secret is not None
@@ -218,14 +212,7 @@ def get_session(
218212
) -> Session:
219213
"""
220214
Returns a new session.
221-
222-
In the case of seed derivation, the function will fail if the device is not initialized.
223215
"""
224-
if self.features.initialized is False and passphrase is not SEEDLESS:
225-
raise exceptions.DerivationOnUninitaizedDeviceError(
226-
"Calling uninitialized device with a passphrase. Call get_seedless_session instead."
227-
)
228-
229216
if isinstance(self.protocol, ProtocolV1Channel):
230217
from .transport.session import SessionV1, derive_seed
231218

python/src/trezorlib/exceptions.py

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -105,16 +105,6 @@ class InvalidSessionError(TrezorException):
105105
Raised when Trezor returns unexpected PassphraseRequest"""
106106

107107

108-
class DerivationOnUninitaizedDeviceError(TrezorException):
109-
"""Tried to derive seed on uninitialized device.
110-
111-
To communicate with uninitialized device, use seedless session instead."""
112-
113-
114-
class UnexpectedCodeEntryTagException(TrezorException):
115-
pass
116-
117-
118108
class ThpError(TrezorException):
119109
pass
120110

0 commit comments

Comments
 (0)