Skip to content

Commit 203459d

Browse files
committed
Rework hub and port enumeration
When a new hub instance is created, the user should be able to enumerate all attached ports. This is now implemented in the Hub() constructor with the optional argument `enumerate`. The argument defaults to False. In order to avoid code deduplication, the port enumeration in discover_hubs() has been modified to use the new functionality with Hub(path, enumerate=True). Signed-off-by: Nicolai Buchwitz <nb@tipi-net.de>
1 parent 916ce6b commit 203459d

1 file changed

Lines changed: 85 additions & 23 deletions

File tree

uhubctl/__init__.py

Lines changed: 85 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import re
22
import subprocess
3+
from typing import List, Optional
34

45
UHUBCTL_BINARY = "uhubctl"
56

67

7-
def __uhubctl(args: list = []) -> list:
8+
def _uhubctl(args: list = []) -> list:
89
cmd = UHUBCTL_BINARY.split(" ") + args
910
result = subprocess.run(cmd, capture_output=True)
1011
stdout = result.stdout.decode()
@@ -18,62 +19,123 @@ def __uhubctl(args: list = []) -> list:
1819

1920

2021
def discover_hubs():
21-
hubs = []
22+
"""
23+
Return list of all by uhubctl supported USB hubs with their ports
2224
23-
pattern_hub = re.compile("Current status for hub ([\.\d-]+)")
24-
pattern_port = re.compile(" Port (\d+): \d{4} (power|off)")
25+
Returns:
26+
List of hubs
2527
26-
for line in __uhubctl():
27-
reg_hub = pattern_hub.match(line)
28-
reg_port = pattern_port.match(line)
28+
"""
29+
hubs = []
2930

30-
if reg_hub:
31-
hub = Hub(reg_hub.group(1))
32-
hubs.append(hub)
33-
elif reg_port:
34-
# assume that the port is the last detected one
35-
hub = hubs[-1]
31+
pattern = re.compile("Current status for hub ([\.\d-]+)")
3632

37-
if not hub:
38-
continue
33+
for line in _uhubctl():
34+
regex = pattern.match(line)
3935

40-
port = Port(hub, reg_port.group(1))
41-
hub.ports.append(port)
36+
if regex:
37+
hub = Hub(regex.group(1), enumerate=True)
38+
hubs.append(hub)
4239

4340
return hubs
4441

4542

4643
class Hub:
47-
def __init__(self, path: str) -> None:
48-
self.path = path
49-
self.ports = []
44+
def __init__(self, path: str, enumerate: bool = False) -> None:
45+
"""
46+
Create new hub instance
47+
48+
Arguments:
49+
path: USB hub path identifier
50+
enumerate: Automatically enumerate ports
51+
"""
52+
self.path: str = path
53+
self.ports: List[Port] = []
54+
55+
if enumerate:
56+
self.discover_ports()
5057

5158
def add_port(self, port_number: int) -> 'Port':
59+
"""
60+
Add port to hub by port number
61+
62+
Arguments:
63+
port_number: Indentification number of port
64+
65+
Returns:
66+
Port
67+
68+
"""
5269
port = Port(self, port_number)
5370
self.ports.append(port)
5471

5572
return port
5673

5774
def add_ports(self, port_start: int, port_end: int):
75+
"""
76+
Add multiple ports to hub
77+
78+
Arguments:
79+
port_start: First port's indentification number
80+
port_end: Last port's ndentification number
81+
"""
5882
for port_number in range(port_start, port_end):
5983
self.add_port(port_number)
6084

85+
def find_port(self, port_number: int) -> Optional['Port']:
86+
"""
87+
Find port by port number
88+
89+
Arguments:
90+
port_number: Identification number of port to find
91+
92+
Returns:
93+
Port or None
94+
"""
95+
96+
for port in self.ports:
97+
if port.port_number == int(port_number):
98+
return port
99+
100+
return None
101+
102+
def discover_ports(self) -> None:
103+
"""
104+
Discover ports for this hub instance
105+
"""
106+
pattern = re.compile(" Port (\d+): \d{4} (power|off)")
107+
108+
for line in _uhubctl(["-l", self.path]):
109+
regex = pattern.match(line)
110+
111+
if regex:
112+
port = Port(self, regex.group(1))
113+
self.ports.append(port)
114+
61115
def __str__(self) -> str:
62116
return f"USB Hub {self.path}"
63117

64118

65119
class Port:
66120
def __init__(self, hub: Hub, port_number: int):
121+
"""
122+
Create new port instance
123+
124+
Arguments:
125+
hub: Hub to attach port to
126+
port_number: Number of port to create
127+
128+
"""
67129
self.hub = hub
68-
self.port_number = port_number
130+
self.port_number = int(port_number)
69131

70132
@property
71133
def status(self) -> bool:
72134
status = None
73135
pattern = re.compile(f" Port {self.port_number}: \d{{4}} (power|off)")
74136

75137
args = ["-l", self.hub.path, "-p", self.port_number]
76-
for line in __uhubctl(args):
138+
for line in _uhubctl(args):
77139
reg = pattern.match(line)
78140

79141
if reg:
@@ -93,7 +155,7 @@ def status(self, status: bool) -> None:
93155
else:
94156
args.append("off")
95157

96-
__uhubctl(args)
158+
_uhubctl(args)
97159

98160
def __str__(self) -> str:
99161
return f"USB Port {self.hub.path}.{self.port_number}"

0 commit comments

Comments
 (0)