feat(python): detect busy device as a separate condition

fixes #1026
pull/2352/head
matejcik 2 years ago
parent aaa224a4f7
commit 8049282bdb

@ -0,0 +1 @@
New exception type `DeviceIsBusy` indicates that the device is in use by another process.

@ -0,0 +1 @@
trezorctl will correctly report that device is in use.

@ -21,9 +21,8 @@ from typing import TYPE_CHECKING, Any, Callable, Dict, Optional
import click
from .. import exceptions
from .. import exceptions, transport
from ..client import TrezorClient
from ..transport import get_transport
from ..ui import ClickUI, ScriptUI
if TYPE_CHECKING:
@ -67,14 +66,14 @@ class TrezorConnection:
def get_transport(self) -> "Transport":
try:
# look for transport without prefix search
return get_transport(self.path, prefix_search=False)
return transport.get_transport(self.path, prefix_search=False)
except Exception:
# most likely not found. try again below.
pass
# look for transport with prefix search
# if this fails, we want the exception to bubble up to the caller
return get_transport(self.path, prefix_search=True)
return transport.get_transport(self.path, prefix_search=True)
def get_ui(self) -> "TrezorClientUI":
if self.script:
@ -101,6 +100,9 @@ class TrezorConnection:
"""
try:
client = self.get_client()
except transport.DeviceIsBusy:
click.echo("Device is in use by another process.")
sys.exit(1)
except Exception:
click.echo("Failed to find a Trezor device.")
if self.path is not None:

@ -26,7 +26,7 @@ import click
from .. import log, messages, protobuf, ui
from ..client import TrezorClient
from ..transport import enumerate_devices
from ..transport import DeviceIsBusy, enumerate_devices
from ..transport.udp import UdpTransport
from . import (
AliasedGroup,
@ -247,9 +247,15 @@ def list_devices(no_resolve: bool) -> Optional[Iterable["Transport"]]:
return enumerate_devices()
for transport in enumerate_devices():
client = TrezorClient(transport, ui=ui.ClickUI())
click.echo(f"{transport} - {format_device_name(client.features)}")
client.end_session()
try:
client = TrezorClient(transport, ui=ui.ClickUI())
description = format_device_name(client.features)
client.end_session()
except DeviceIsBusy:
description = "Device is in use by another process"
except Exception:
description = "Failed to read details"
click.echo(f"{transport} - {description}")
return None

@ -48,6 +48,10 @@ class TransportException(TrezorException):
pass
class DeviceIsBusy(TransportException):
pass
class Transport:
"""Raw connection to a Trezor device.

@ -21,7 +21,7 @@ from typing import TYPE_CHECKING, Any, Dict, Iterable, Optional
import requests
from ..log import DUMP_PACKETS
from . import MessagePayload, Transport, TransportException
from . import DeviceIsBusy, MessagePayload, Transport, TransportException
if TYPE_CHECKING:
from ..models import TrezorModel
@ -37,14 +37,19 @@ CONNECTION = requests.Session()
CONNECTION.headers.update(TREZORD_ORIGIN_HEADER)
def call_bridge(uri: str, data: Optional[str] = None) -> requests.Response:
url = TREZORD_HOST + "/" + uri
class BridgeException(TransportException):
def __init__(self, path: str, status: int, message: str) -> None:
self.path = path
self.status = status
self.message = message
super().__init__(f"trezord: {path} failed with code {status}: {message}")
def call_bridge(path: str, data: Optional[str] = None) -> requests.Response:
url = TREZORD_HOST + "/" + path
r = CONNECTION.post(url, data=data)
if r.status_code != 200:
error_str = (
f"trezord: {uri} failed with code {r.status_code}: {r.json()['error']}"
)
raise TransportException(error_str)
raise BridgeException(path, r.status_code, r.json()["error"])
return r
@ -150,7 +155,12 @@ class BridgeTransport(Transport):
return []
def begin_session(self) -> None:
data = self._call("acquire/" + self.device["path"])
try:
data = self._call("acquire/" + self.device["path"])
except BridgeException as e:
if e.message == "wrong previous session":
raise DeviceIsBusy(self.device["path"]) from e
raise
self.session = data.json()["session"]
def end_session(self) -> None:

@ -22,7 +22,7 @@ from typing import Iterable, List, Optional
from ..log import DUMP_PACKETS
from ..models import TREZORS, TrezorModel
from . import UDEV_RULES_STR, TransportException
from . import UDEV_RULES_STR, DeviceIsBusy, TransportException
from .protocol import ProtocolBasedTransport, ProtocolV1
LOG = logging.getLogger(__name__)
@ -57,7 +57,10 @@ class WebUsbHandle:
else:
args = ()
raise IOError("Cannot open device", *args)
self.handle.claimInterface(self.interface)
try:
self.handle.claimInterface(self.interface)
except usb1.USBErrorAccess as e:
raise DeviceIsBusy(self.device) from e
def close(self) -> None:
if self.handle is not None:

Loading…
Cancel
Save