list_ports_linux: cleanup
diff --git a/serial/tools/list_ports_linux.py b/serial/tools/list_ports_linux.py
index 11092b5..f7491e5 100644
--- a/serial/tools/list_ports_linux.py
+++ b/serial/tools/list_ports_linux.py
@@ -3,64 +3,47 @@
# portable serial port access with python
#
# This is a module that gathers a list of serial ports including details on
-# GNU/Linux systems
+# GNU/Linux systems.
+# The comports function is expected to return an iterable that yields tuples of
+# 3 strings: port name, human readable description and a hardware ID.
#
# (C) 2011-2015 Chris Liechti <cliechti@gmx.net>
#
# SPDX-License-Identifier: BSD-3-Clause
import glob
-import sys
import os
import re
+import subprocess
+import sys
-try:
- import subprocess
-except ImportError:
- def popen(argv):
- try:
- si, so = os.popen4(' '.join(argv))
- return so.read().strip()
- except:
- raise IOError('lsusb failed')
-else:
- def popen(argv):
- try:
- return subprocess.check_output(argv, stderr=subprocess.STDOUT).strip()
- except:
- raise IOError('lsusb failed')
+def read_command(argv):
+ """run a command and return its output"""
+ try:
+ return subprocess.check_output(argv, stderr=subprocess.STDOUT).strip().decode('ascii', 'replace')
+ except subprocess.SubprocessError as e:
+ raise IOError('command %r failed: %s' % (argv, e))
-
-# The comports function is expected to return an iterable that yields tuples of
-# 3 strings: port name, human readable description and a hardware ID.
-#
-# as currently no method is known to get the second two strings easily, they
-# are currently just identical to the port name.
-
-# try to detect the OS so that a device can be selected...
-plat = sys.platform.lower()
-
-def read_line(filename):
+def read_line(*args):
"""\
Helper function to read a single line from a file.
+ One or more parameters are allowed, they are joined with os.path.join.
Returns None on errors..
"""
try:
- f = open(filename)
- line = f.readline().strip()
- f.close()
+ with open(os.path.join(*args)) as f:
+ line = f.readline().strip()
return line
except IOError:
return None
def re_group(regexp, text):
"""search for regexp in text, return 1st group on match"""
- if sys.version < '3':
- m = re.search(regexp, text)
+ m = re.search(regexp, text)
+ if m:
+ return m.group(1)
else:
- # text is bytes-like
- m = re.search(regexp, text.decode('ascii', 'replace'))
- if m: return m.group(1)
+ return None
# try to extract descriptions from sysfs. this was done by experimenting,
@@ -69,14 +52,14 @@
def usb_sysfs_hw_string(sysfs_path):
"""given a path to a usb device in sysfs, return a string describing it"""
bus, dev = os.path.basename(os.path.realpath(sysfs_path)).split('-')
- snr = read_line(sysfs_path+'/serial')
- if snr:
+ snr = read_line(sysfs_path, 'serial')
+ if snr is not None:
snr_txt = ' SNR=%s' % (snr,)
else:
snr_txt = ''
return 'USB VID:PID=%s:%s%s' % (
- read_line(sysfs_path+'/idVendor'),
- read_line(sysfs_path+'/idProduct'),
+ read_line(sysfs_path, 'idVendor'),
+ read_line(sysfs_path, 'idProduct'),
snr_txt
)
@@ -84,8 +67,11 @@
base = os.path.basename(os.path.realpath(sysfs_path))
bus = base.split('-')[0]
try:
- dev = int(read_line(os.path.join(sysfs_path, 'devnum')))
- desc = popen(['lsusb', '-v', '-s', '%s:%s' % (bus, dev)])
+ dev = int(read_line(sysfs_path, 'devnum'))
+ desc = read_command(['lsusb', '-v', '-s', '%s:%s' % (bus, dev)])
+ except IOError:
+ return base
+ else:
# descriptions from device
iManufacturer = re_group('iManufacturer\s+\w+ (.+)', desc)
iProduct = re_group('iProduct\s+\w+ (.+)', desc)
@@ -95,8 +81,6 @@
idProduct = re_group('idProduct\s+0x\w+ (.+)', desc)
# create descriptions. prefer text from device, fall back to the others
return '%s %s %s' % (iManufacturer or idVendor, iProduct or idProduct, iSerial)
- except IOError:
- return base
def describe(device):
"""\
@@ -154,3 +138,4 @@
if __name__ == '__main__':
for port, desc, hwid in sorted(comports()):
print("%s: %s [%s]" % (port, desc, hwid))
+