protocol_hwgrep.py 3.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  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 <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. import serial
  23. import serial.tools.list_ports
  24. try:
  25. basestring
  26. except NameError:
  27. basestring = str # python 3 pylint: disable=redefined-builtin
  28. class Serial(serial.Serial):
  29. """Just inherit the native Serial port implementation and patch the port property."""
  30. # pylint: disable=no-member
  31. @serial.Serial.port.setter
  32. def port(self, value):
  33. """translate port name before storing it"""
  34. if isinstance(value, basestring) and value.startswith('hwgrep://'):
  35. serial.Serial.port.__set__(self, self.from_url(value))
  36. else:
  37. serial.Serial.port.__set__(self, value)
  38. def from_url(self, url):
  39. """extract host and port from an URL string"""
  40. if url.lower().startswith("hwgrep://"):
  41. url = url[9:]
  42. n = 0
  43. test_open = False
  44. args = url.split('&')
  45. regexp = args.pop(0)
  46. for arg in args:
  47. if '=' in arg:
  48. option, value = arg.split('=', 1)
  49. else:
  50. option = arg
  51. value = None
  52. if option == 'n':
  53. # pick n'th element
  54. n = int(value) - 1
  55. if n < 1:
  56. raise ValueError('option "n" expects a positive integer larger than 1: {!r}'.format(value))
  57. elif option == 'skip_busy':
  58. # open to test if port is available. not the nicest way..
  59. test_open = True
  60. else:
  61. raise ValueError('unknown option: {!r}'.format(option))
  62. # use a for loop to get the 1st element from the generator
  63. for port, desc, hwid in sorted(serial.tools.list_ports.grep(regexp)):
  64. if test_open:
  65. try:
  66. s = serial.Serial(port)
  67. except serial.SerialException:
  68. # it has some error, skip this one
  69. continue
  70. else:
  71. s.close()
  72. if n:
  73. n -= 1
  74. continue
  75. return port
  76. else:
  77. raise serial.SerialException('no ports found matching regexp {!r}'.format(url))
  78. # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  79. if __name__ == '__main__':
  80. s = Serial(None)
  81. s.port = 'hwgrep://ttyS0'
  82. print(s)