blob: 1a288c94536d7dd4033df9861a4a27f8d0227e11 [file] [log] [blame]
rjw2e8229f2022-02-15 21:08:12 +08001#! 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 <cliechti@gmx.net>
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 charachter `&` 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)