protocol_hwgrep.py 3.1 KB

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