1#! python 2# 3# This module implements a special URL handler that uses the port listing to 4# find ports by searching the string descriptions. 5# 6# This file is part of pySerial. https://github.com/pyserial/pyserial 7# (C) 2011-2015 Chris Liechti <[email protected]> 8# 9# SPDX-License-Identifier: BSD-3-Clause 10# 11# URL format: hwgrep://<regexp>&<option> 12# 13# where <regexp> is a Python regexp according to the re module 14# 15# violating the normal definition for URLs, the character `&` is used to 16# separate parameters from the arguments (instead of `?`, but the question mark 17# is heavily used in regexp'es) 18# 19# options: 20# n=<N> pick the N'th entry instead of the first one (numbering starts at 1) 21# skip_busy tries to open port to check if it is busy, fails on posix as ports are not locked! 22 23from __future__ import absolute_import 24 25import serial 26import serial.tools.list_ports 27 28try: 29 basestring 30except NameError: 31 basestring = str # python 3 pylint: disable=redefined-builtin 32 33 34class Serial(serial.Serial): 35 """Just inherit the native Serial port implementation and patch the port property.""" 36 # pylint: disable=no-member 37 38 @serial.Serial.port.setter 39 def port(self, value): 40 """translate port name before storing it""" 41 if isinstance(value, basestring) and value.startswith('hwgrep://'): 42 serial.Serial.port.__set__(self, self.from_url(value)) 43 else: 44 serial.Serial.port.__set__(self, value) 45 46 def from_url(self, url): 47 """extract host and port from an URL string""" 48 if url.lower().startswith("hwgrep://"): 49 url = url[9:] 50 n = 0 51 test_open = False 52 args = url.split('&') 53 regexp = args.pop(0) 54 for arg in args: 55 if '=' in arg: 56 option, value = arg.split('=', 1) 57 else: 58 option = arg 59 value = None 60 if option == 'n': 61 # pick n'th element 62 n = int(value) - 1 63 if n < 1: 64 raise ValueError('option "n" expects a positive integer larger than 1: {!r}'.format(value)) 65 elif option == 'skip_busy': 66 # open to test if port is available. not the nicest way.. 67 test_open = True 68 else: 69 raise ValueError('unknown option: {!r}'.format(option)) 70 # use a for loop to get the 1st element from the generator 71 for port, desc, hwid in sorted(serial.tools.list_ports.grep(regexp)): 72 if test_open: 73 try: 74 s = serial.Serial(port) 75 except serial.SerialException: 76 # it has some error, skip this one 77 continue 78 else: 79 s.close() 80 if n: 81 n -= 1 82 continue 83 return port 84 else: 85 raise serial.SerialException('no ports found matching regexp {!r}'.format(url)) 86 87# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 88if __name__ == '__main__': 89 s = Serial(None) 90 s.port = 'hwgrep://ttyS0' 91 print(s) 92